diff options
author | Martin Jones <martin.jones@nokia.com> | 2009-12-02 05:02:05 (GMT) |
---|---|---|
committer | Martin Jones <martin.jones@nokia.com> | 2009-12-02 05:02:05 (GMT) |
commit | 9b31dbebb002da3eecd2d2fc2385098fb983c0c2 (patch) | |
tree | 72b4cbdc4cf4667dee9d8c6055d002e1d3d305ef /src/declarative | |
parent | 17035bd496d35364ff7f49ef9cc86b0a8c48fe1e (diff) | |
parent | 14827a4ae7fd531adc802219473d9822e696b982 (diff) | |
download | Qt-9b31dbebb002da3eecd2d2fc2385098fb983c0c2.zip Qt-9b31dbebb002da3eecd2d2fc2385098fb983c0c2.tar.gz Qt-9b31dbebb002da3eecd2d2fc2385098fb983c0c2.tar.bz2 |
Merge branch 'kinetic-declarativeui' of git@scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
Diffstat (limited to 'src/declarative')
20 files changed, 399 insertions, 189 deletions
diff --git a/src/declarative/qml/qmlboundsignal.cpp b/src/declarative/qml/qmlboundsignal.cpp index deb15dc..f21b781 100644 --- a/src/declarative/qml/qmlboundsignal.cpp +++ b/src/declarative/qml/qmlboundsignal.cpp @@ -93,7 +93,7 @@ QmlAbstractBoundSignal::~QmlAbstractBoundSignal() QmlBoundSignal::QmlBoundSignal(QObject *scope, const QMetaMethod &signal, QObject *parent) -: m_expression(0), m_idx(signal.methodIndex()), m_params(0) +: m_expression(0), m_signal(signal), m_paramsValid(false), m_params(0) { // A cached evaluation of the QmlExpression::value() slot index. // @@ -103,16 +103,13 @@ QmlBoundSignal::QmlBoundSignal(QObject *scope, const QMetaMethod &signal, if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); QmlGraphics_setParent_noEvent(this, parent); - QMetaObject::connect(scope, m_idx, this, evaluateIdx); - - if (!signal.parameterTypes().isEmpty()) - m_params = new QmlBoundSignalParameters(signal, this); + QMetaObject::connect(scope, m_signal.methodIndex(), this, evaluateIdx); } QmlBoundSignal::QmlBoundSignal(QmlContext *ctxt, const QString &val, QObject *scope, const QMetaMethod &signal, QObject *parent) -: m_expression(0), m_idx(signal.methodIndex()), m_params(0) +: m_expression(0), m_signal(signal), m_paramsValid(false), m_params(0) { // A cached evaluation of the QmlExpression::value() slot index. // @@ -122,13 +119,10 @@ QmlBoundSignal::QmlBoundSignal(QmlContext *ctxt, const QString &val, if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); QmlGraphics_setParent_noEvent(this, parent); - QMetaObject::connect(scope, m_idx, this, evaluateIdx); + QMetaObject::connect(scope, m_signal.methodIndex(), this, evaluateIdx); m_expression = new QmlExpression(ctxt, val, scope); m_expression->setTrackChange(false); - - if (!signal.parameterTypes().isEmpty()) - m_params = new QmlBoundSignalParameters(signal, this); } QmlBoundSignal::~QmlBoundSignal() @@ -139,7 +133,7 @@ QmlBoundSignal::~QmlBoundSignal() int QmlBoundSignal::index() const { - return m_idx; + return m_signal.methodIndex(); } /*! @@ -174,6 +168,12 @@ QmlBoundSignal *QmlBoundSignal::cast(QObject *o) int QmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a) { if (c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) { + if (!m_paramsValid) { + if (!m_signal.parameterTypes().isEmpty()) + m_params = new QmlBoundSignalParameters(m_signal, this); + m_paramsValid = true; + } + if (m_params) m_params->setValues(a); if (m_expression) { QmlExpressionPrivate::get(m_expression)->value(m_params); diff --git a/src/declarative/qml/qmlboundsignal_p.h b/src/declarative/qml/qmlboundsignal_p.h index e801892..53a5a6b 100644 --- a/src/declarative/qml/qmlboundsignal_p.h +++ b/src/declarative/qml/qmlboundsignal_p.h @@ -55,6 +55,7 @@ #include <qmlexpression.h> #include <private/qobject_p.h> +#include <QtCore/qmetaobject.h> QT_BEGIN_NAMESPACE @@ -87,7 +88,8 @@ protected: private: QmlExpression *m_expression; - int m_idx; + QMetaMethod m_signal; + bool m_paramsValid; QmlBoundSignalParameters *m_params; }; diff --git a/src/declarative/qml/qmlcompileddata.cpp b/src/declarative/qml/qmlcompileddata.cpp index 44d2745..9b931c5 100644 --- a/src/declarative/qml/qmlcompileddata.cpp +++ b/src/declarative/qml/qmlcompileddata.cpp @@ -167,7 +167,8 @@ QmlCompiledData::~QmlCompiledData() if (importCache) importCache->release(); - qDeleteAll(programs); + qDeleteAll(cachedPrograms); + qDeleteAll(cachedClosures); } QObject *QmlCompiledData::TypeReference::createInstance(QmlContext *ctxt, const QBitField &bindings) const diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index a4e14b2..aac2e02 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -2529,12 +2529,26 @@ bool QmlCompiler::completeComponentBuild() // Pre-rewrite the expression QString expression = binding.expression.asScript(); + + // ### Optimize + QmlRewrite::SharedBindingTester sharableTest; + bool isSharable = sharableTest.isSharable(expression); + QmlRewrite::RewriteBinding rewriteBinding; expression = rewriteBinding(expression); quint32 length = expression.length(); - quint32 pc = output->programs.length(); - output->programs.append(0); + quint32 pc; + + if (isSharable) { + pc = output->cachedClosures.count(); + pc |= 0x80000000; + output->cachedClosures.append(0); + } else { + pc = output->cachedPrograms.length(); + output->cachedPrograms.append(0); + } + binding.compiledData = QByteArray((const char *)&pc, sizeof(quint32)) + QByteArray((const char *)&length, sizeof(quint32)) + diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index b54a62a..542fb48 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -113,7 +113,8 @@ public: QList<QByteArray> datas; QList<QmlParser::Location> locations; QList<QmlInstruction> bytecode; - QList<QScriptProgram *> programs; + QList<QScriptProgram *> cachedPrograms; + QList<QScriptValue *> cachedClosures; QList<QmlPropertyCache *> propertyCaches; QList<QmlIntegerCache *> contextCaches; QList<QmlParser::Object::ScriptBlock> scripts; diff --git a/src/declarative/qml/qmlcomponent.cpp b/src/declarative/qml/qmlcomponent.cpp index 5f8b816..85da2c1 100644 --- a/src/declarative/qml/qmlcomponent.cpp +++ b/src/declarative/qml/qmlcomponent.cpp @@ -705,7 +705,7 @@ void QmlComponentPrivate::completeCreate() QmlEnginePrivate::SimpleList<QmlParserStatus> ps = parserStatus.at(ii); - for (int jj = 0; jj < ps.count; ++jj) { + for (int jj = ps.count - 1; jj >= 0; --jj) { QmlParserStatus *status = ps.at(jj); if (status && status->d) { status->d = 0; diff --git a/src/declarative/qml/qmlcontextscriptclass.cpp b/src/declarative/qml/qmlcontextscriptclass.cpp index fda284a..a95400a 100644 --- a/src/declarative/qml/qmlcontextscriptclass.cpp +++ b/src/declarative/qml/qmlcontextscriptclass.cpp @@ -47,9 +47,27 @@ QT_BEGIN_NAMESPACE struct ContextData : public QScriptDeclarativeClass::Object { - ContextData(QmlContext *c, QObject *o) : context(c), scopeObject(o) {} + ContextData() : isSharedContext(true) {} + ContextData(QmlContext *c, QObject *o) : context(c), scopeObject(o), isSharedContext(false) {} QGuard<QmlContext> context; QGuard<QObject> scopeObject; + bool isSharedContext; + + QmlContext *getContext(QmlEngine *engine) { + if (isSharedContext) { + return QmlEnginePrivate::get(engine)->sharedContext; + } else { + return context.data(); + } + } + + QObject *getScope(QmlEngine *engine) { + if (isSharedContext) { + return QmlEnginePrivate::get(engine)->sharedScope; + } else { + return scopeObject.data(); + } + } }; /* @@ -73,13 +91,20 @@ QScriptValue QmlContextScriptClass::newContext(QmlContext *context, QObject *sco return newObject(scriptEngine, this, new ContextData(context, scopeObject)); } +QScriptValue QmlContextScriptClass::newSharedContext() +{ + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + return newObject(scriptEngine, this, new ContextData()); +} + QmlContext *QmlContextScriptClass::contextFromValue(const QScriptValue &v) { if (scriptClass(v) != this) return 0; ContextData *data = (ContextData *)object(v); - return data->context; + return data->getContext(engine); } QScriptClass::QueryFlags @@ -94,8 +119,8 @@ QmlContextScriptClass::queryProperty(Object *object, const Identifier &name, lastPropertyIndex = -1; lastDefaultObject = -1; - QmlContext *bindContext = ((ContextData *)object)->context.data(); - QObject *scopeObject = ((ContextData *)object)->scopeObject.data(); + QmlContext *bindContext = ((ContextData *)object)->getContext(engine); + QObject *scopeObject = ((ContextData *)object)->getScope(engine); if (!bindContext) return 0; diff --git a/src/declarative/qml/qmlcontextscriptclass_p.h b/src/declarative/qml/qmlcontextscriptclass_p.h index bbd779b..10b848a 100644 --- a/src/declarative/qml/qmlcontextscriptclass_p.h +++ b/src/declarative/qml/qmlcontextscriptclass_p.h @@ -68,6 +68,7 @@ public: ~QmlContextScriptClass(); QScriptValue newContext(QmlContext *, QObject * = 0); + QScriptValue newSharedContext(); QmlContext *contextFromValue(const QScriptValue &); diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index 92a6bd9..0e24fd2 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -110,7 +110,7 @@ struct StaticQtMetaObject : public QObject QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) : rootContext(0), currentExpression(0), - isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0), globalClass(0), + isDebugging(false), contextClass(0), sharedContext(0), sharedScope(0), objectClass(0), valueTypeClass(0), globalClass(0), nodeListClass(0), namedNodeMapClass(0), sqlQueryClass(0), cleanup(0), erroredBindings(0), inProgressCreations(0), scriptEngine(this), workerScriptEngine(0), componentAttacheds(0), rootComponent(0), networkAccessManager(0), typeManager(e), uniqueId(1) @@ -230,6 +230,7 @@ void QmlEnginePrivate::init() Q_Q(QmlEngine); qRegisterMetaType<QVariant>("QVariant"); qRegisterMetaType<QmlScriptString>("QmlScriptString"); + qRegisterMetaType<QScriptValue>("QScriptValue"); contextClass = new QmlContextScriptClass(q); objectClass = new QmlObjectScriptClass(q); diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 9e0c1fc..4eb9843 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -126,6 +126,8 @@ public: struct ImportedNamespace; QmlContextScriptClass *contextClass; + QmlContext *sharedContext; + QObject *sharedScope; QmlObjectScriptClass *objectClass; QmlValueTypeScriptClass *valueTypeClass; QmlTypeNameScriptClass *typeNameClass; diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp index d2d60ee..2239d77 100644 --- a/src/declarative/qml/qmlexpression.cpp +++ b/src/declarative/qml/qmlexpression.cpp @@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE QmlExpressionData::QmlExpressionData() : expressionFunctionValid(false), expressionRewritten(false), me(0), - trackChange(true), line(-1), guardList(0), guardListLength(0) + trackChange(true), isShared(false), line(-1), guardList(0), guardListLength(0) { } @@ -108,27 +108,47 @@ void QmlExpressionPrivate::init(QmlContext *ctxt, void *expr, QmlRefCount *rc, data->expression = QString::fromRawData((QChar *)(exprData + 3), exprData[2]); int progIdx = *(exprData + 1); + bool isShared = progIdx & 0x80000000; + progIdx &= 0x7FFFFFFF; + QmlEngine *engine = ctxt->engine(); QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + if (isShared) { + + if (!dd->cachedClosures.at(progIdx)) { + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + scriptContext->pushScope(ep->contextClass->newSharedContext()); + dd->cachedClosures[progIdx] = new QScriptValue(scriptEngine->evaluate(data->expression, data->url.toString(), data->line)); + scriptEngine->popContext(); + } + + data->expressionFunction = *dd->cachedClosures.at(progIdx); + data->isShared = true; + data->expressionFunctionValid = true; + + } else { + #if !defined(Q_OS_SYMBIAN) //XXX Why doesn't this work? - if (!dd->programs.at(progIdx)) { - dd->programs[progIdx] = - new QScriptProgram(data->expression, data->url.toString(), data->line); - } + if (!dd->cachedPrograms.at(progIdx)) { + dd->cachedPrograms[progIdx] = + new QScriptProgram(data->expression, data->url.toString(), data->line); + } #endif - QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); - scriptContext->pushScope(ep->contextClass->newContext(ctxt, me)); + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + scriptContext->pushScope(ep->contextClass->newContext(ctxt, me)); #if !defined(Q_OS_SYMBIAN) - data->expressionFunction = scriptEngine->evaluate(*dd->programs[progIdx]); + data->expressionFunction = scriptEngine->evaluate(*dd->cachedPrograms.at(progIdx)); #else - data->expressionFunction = scriptEngine->evaluate(data->expression); + data->expressionFunction = scriptEngine->evaluate(data->expression); #endif - data->expressionFunctionValid = true; - scriptEngine->popContext(); + data->expressionFunctionValid = true; + scriptEngine->popContext(); + } } data->QmlAbstractExpression::setContext(ctxt); @@ -328,8 +348,22 @@ QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope, bool *isUnd data->expressionFunctionValid = true; } + QmlContext *oldSharedContext = 0; + QObject *oldSharedScope = 0; + if (data->isShared) { + oldSharedContext = ep->sharedContext; + oldSharedScope = ep->sharedScope; + ep->sharedContext = data->context(); + ep->sharedScope = data->me; + } + QScriptValue svalue = data->expressionFunction.call(); + if (data->isShared) { + ep->sharedContext = oldSharedContext; + ep->sharedScope = oldSharedScope; + } + if (isUndefined) *isUndefined = svalue.isUndefined() || scriptEngine->hasUncaughtException(); diff --git a/src/declarative/qml/qmlexpression_p.h b/src/declarative/qml/qmlexpression_p.h index c6ba54d..dbb12ba 100644 --- a/src/declarative/qml/qmlexpression_p.h +++ b/src/declarative/qml/qmlexpression_p.h @@ -101,6 +101,8 @@ public: QObject *me; bool trackChange; + bool isShared; + QUrl url; int line; diff --git a/src/declarative/qml/qmlmetatype.cpp b/src/declarative/qml/qmlmetatype.cpp index 5fb2f50..46d967b 100644 --- a/src/declarative/qml/qmlmetatype.cpp +++ b/src/declarative/qml/qmlmetatype.cpp @@ -58,6 +58,7 @@ #include <qlocale.h> #include <ctype.h> #include <QtCore/qcryptographichash.h> +#include <QtScript/qscriptvalue.h> #include <private/qmlcustomparser_p.h> #ifdef QT_BOOTSTRAPPED @@ -929,6 +930,7 @@ QList<QmlType*> QmlMetaType::qmlTypes() #include <QtGui/qvector3d.h> #include <QtGui/qvector4d.h> #include <QtGui/qquaternion.h> +Q_DECLARE_METATYPE(QScriptValue); /*! Copies \a copy into \a data, assuming they both are of type \a type. If @@ -1138,6 +1140,9 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) if (type == qMetaTypeId<QVariant>()) { *static_cast<NS(QVariant) *>(data) = *static_cast<const NS(QVariant)*>(copy); return true; + } else if (type == qMetaTypeId<QScriptValue>()) { + *static_cast<NS(QScriptValue) *>(data) = *static_cast<const NS(QScriptValue)*>(copy); + return true; } else if (typeCategory(type) != Unknown) { *static_cast<void **>(data) = *static_cast<void* const *>(copy); return true; @@ -1340,6 +1345,9 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) if (type == qMetaTypeId<QVariant>()) { *static_cast<NS(QVariant) *>(data) = NS(QVariant)(); return true; + } else if (type == qMetaTypeId<QScriptValue>()) { + *static_cast<NS(QScriptValue) *>(data) = NS(QScriptValue)(); + return true; } else if (typeCategory(type) != Unknown) { *static_cast<void **>(data) = 0; return true; diff --git a/src/declarative/qml/qmlobjectscriptclass.cpp b/src/declarative/qml/qmlobjectscriptclass.cpp index 1a6f724..f78a12a 100644 --- a/src/declarative/qml/qmlobjectscriptclass.cpp +++ b/src/declarative/qml/qmlobjectscriptclass.cpp @@ -229,12 +229,16 @@ QScriptValue QmlObjectScriptClass::property(QObject *obj, const Identifier &name } else if (lastData->flags & QmlPropertyCache::Data::IsQmlList) { return enginePriv->listClass->newList(obj, lastData->coreIndex, QmlListScriptClass::QmlListPtr); - } if (lastData->flags & QmlPropertyCache::Data::IsQObjectDerived) { + } else if (lastData->flags & QmlPropertyCache::Data::IsQObjectDerived) { QObject *rv = 0; void *args[] = { &rv, 0 }; QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); - return newQObject(rv, lastData->propType); + } else if (lastData->flags & QmlPropertyCache::Data::IsQScriptValue) { + QScriptValue rv = scriptEngine->nullValue(); + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return rv; } else { QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj); return enginePriv->scriptValueFromVariant(var); diff --git a/src/declarative/qml/qmlpropertycache.cpp b/src/declarative/qml/qmlpropertycache.cpp index b7a5fad..ff4991c 100644 --- a/src/declarative/qml/qmlpropertycache.cpp +++ b/src/declarative/qml/qmlpropertycache.cpp @@ -45,6 +45,8 @@ QT_BEGIN_NAMESPACE +Q_DECLARE_METATYPE(QScriptValue); + void QmlPropertyCache::Data::load(const QMetaProperty &p) { propType = p.userType(); @@ -61,6 +63,8 @@ void QmlPropertyCache::Data::load(const QMetaProperty &p) if (propType == qMetaTypeId<QmlBinding *>()) { flags |= Data::IsQmlBinding; + } else if (propType == qMetaTypeId<QScriptValue>()) { + flags |= Data::IsQScriptValue; } else if (p.isEnumType()) { flags |= Data::IsEnumType; } else { diff --git a/src/declarative/qml/qmlpropertycache_p.h b/src/declarative/qml/qmlpropertycache_p.h index c2e8abe..8f19a8b 100644 --- a/src/declarative/qml/qmlpropertycache_p.h +++ b/src/declarative/qml/qmlpropertycache_p.h @@ -83,7 +83,8 @@ public: IsEnumType = 0x00000010, IsQmlList = 0x00000020, IsQList = 0x00000040, - IsQmlBinding = 0x00000080 + IsQmlBinding = 0x00000080, + IsQScriptValue = 0x00000100 }; Q_DECLARE_FLAGS(Flags, Flag) diff --git a/src/declarative/qml/qmlrewrite.cpp b/src/declarative/qml/qmlrewrite.cpp index 32e2fef..2964a75 100644 --- a/src/declarative/qml/qmlrewrite.cpp +++ b/src/declarative/qml/qmlrewrite.cpp @@ -49,6 +49,22 @@ DEFINE_BOOL_CONFIG_OPTION(rewriteDump, QML_REWRITE_DUMP); namespace QmlRewrite { +bool SharedBindingTester::isSharable(const QString &code) +{ + Engine engine; + NodePool pool(QString(), &engine); + Lexer lexer(&engine); + Parser parser(&engine); + lexer.setCode(code, 0); + parser.parseStatement(); + if (!parser.statement()) + return false; + + _sharable = true; + AST::Node::acceptChild(parser.statement(), this); + return _sharable; +} + QString RewriteBinding::operator()(const QString &code, bool *ok) { Engine engine; diff --git a/src/declarative/qml/qmlrewrite_p.h b/src/declarative/qml/qmlrewrite_p.h index a5cb841..a04a0db 100644 --- a/src/declarative/qml/qmlrewrite_p.h +++ b/src/declarative/qml/qmlrewrite_p.h @@ -63,6 +63,17 @@ QT_BEGIN_NAMESPACE namespace QmlRewrite { using namespace QmlJS; +class SharedBindingTester : protected AST::Visitor +{ + bool _sharable; +public: + bool isSharable(const QString &code); + + virtual bool visit(AST::FunctionDeclaration *) { _sharable = false; return false; } + virtual bool visit(AST::FunctionExpression *) { _sharable = false; return false; } + virtual bool visit(AST::CallExpression *) { _sharable = false; return false; } +}; + class RewriteBinding: protected AST::Visitor { unsigned _position; diff --git a/src/declarative/qml/qmlworkerscript.cpp b/src/declarative/qml/qmlworkerscript.cpp index a6428dd..d4fcf4a 100644 --- a/src/declarative/qml/qmlworkerscript.cpp +++ b/src/declarative/qml/qmlworkerscript.cpp @@ -55,15 +55,15 @@ class WorkerDataEvent : public QEvent public: enum Type { WorkerData = QEvent::User }; - WorkerDataEvent(int workerId, QmlWorkerScriptEngine::Data *data); + WorkerDataEvent(int workerId, const QVariant &data); virtual ~WorkerDataEvent(); int workerId() const; - QmlWorkerScriptEngine::Data *takeData(); + QVariant data() const; private: int m_id; - QmlWorkerScriptEngine::Data *m_data; + QVariant m_data; }; class WorkerLoadEvent : public QEvent @@ -81,31 +81,64 @@ private: QUrl m_url; }; +class WorkerRemoveEvent : public QEvent +{ +public: + enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; + + WorkerRemoveEvent(int workerId); + + int workerId() const; + +private: + int m_id; +}; + class QmlWorkerScriptEnginePrivate : public QObject { public: QmlWorkerScriptEnginePrivate(); - QScriptEngine *workerEngine; + struct ScriptEngine : public QScriptEngine + { + ScriptEngine(QmlWorkerScriptEnginePrivate *parent) : p(parent) {} + QmlWorkerScriptEnginePrivate *p; + }; + ScriptEngine *workerEngine; + static QmlWorkerScriptEnginePrivate *get(QScriptEngine *e) { + return static_cast<ScriptEngine *>(e)->p; + } QMutex m_lock; QWaitCondition m_wait; + struct WorkerScript { + WorkerScript(); + + int id; + bool initialized; + QmlWorkerScript *owner; + QScriptValue object; + + QScriptValue callback; + }; + + QHash<int, WorkerScript *> workers; QScriptValue getWorker(int); int m_nextId; - QHash<int, QScriptValue> m_workers; static QVariant scriptValueToVariant(const QScriptValue &); static QScriptValue variantToScriptValue(const QVariant &, QScriptEngine *); - static QScriptValue onmessage(QScriptContext *ctxt, QScriptEngine *engine); + static QScriptValue onMessage(QScriptContext *ctxt, QScriptEngine *engine); + static QScriptValue sendMessage(QScriptContext *ctxt, QScriptEngine *engine); protected: virtual bool event(QEvent *); private: - void processMessage(int, QmlWorkerScriptEngine::Data *); + void processMessage(int, const QVariant &); void processLoad(int, const QUrl &); }; @@ -114,34 +147,75 @@ QmlWorkerScriptEnginePrivate::QmlWorkerScriptEnginePrivate() { } -QScriptValue QmlWorkerScriptEnginePrivate::onmessage(QScriptContext *ctxt, QScriptEngine *engine) +QScriptValue QmlWorkerScriptEnginePrivate::onMessage(QScriptContext *ctxt, QScriptEngine *engine) { + QmlWorkerScriptEnginePrivate *p = QmlWorkerScriptEnginePrivate::get(engine); + + int id = ctxt->thisObject().data().toVariant().toInt(); + + WorkerScript *script = p->workers.value(id); + if (!script) + return engine->undefinedValue(); + if (ctxt->argumentCount() >= 1) - ctxt->thisObject().setData(ctxt->argument(0)); + script->callback = ctxt->argument(0); + + return script->callback; +} + +QScriptValue QmlWorkerScriptEnginePrivate::sendMessage(QScriptContext *ctxt, QScriptEngine *engine) +{ + if (!ctxt->argumentCount()) + return engine->undefinedValue(); + + QmlWorkerScriptEnginePrivate *p = QmlWorkerScriptEnginePrivate::get(engine); + + int id = ctxt->thisObject().data().toVariant().toInt(); + + WorkerScript *script = p->workers.value(id); + if (!script) + return engine->undefinedValue(); - return ctxt->thisObject().data(); + p->m_lock.lock(); + if (script->owner) + QCoreApplication::postEvent(script->owner, + new WorkerDataEvent(0, scriptValueToVariant(ctxt->argument(0)))); + p->m_lock.unlock(); + + return engine->undefinedValue(); } QScriptValue QmlWorkerScriptEnginePrivate::getWorker(int id) { - QHash<int, QScriptValue>::Iterator iter = m_workers.find(id); - if (iter == m_workers.end()) { - QScriptValue worker = workerEngine->newObject(); + QHash<int, WorkerScript *>::ConstIterator iter = workers.find(id); + + if (iter == workers.end()) + return workerEngine->nullValue(); + + WorkerScript *script = *iter; + if (!script->initialized) { - worker.setProperty(QLatin1String("onmessage"), workerEngine->newFunction(onmessage), - QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + script->initialized = true; + script->object = workerEngine->newObject(); - iter = m_workers.insert(id, worker); + QScriptValue api = workerEngine->newObject(); + api.setData(script->id); + + api.setProperty(QLatin1String("onMessage"), workerEngine->newFunction(onMessage), + QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + api.setProperty(QLatin1String("sendMessage"), workerEngine->newFunction(sendMessage)); + + script->object.setProperty(QLatin1String("WorkerScript"), api); } - return *iter; + return script->object; } bool QmlWorkerScriptEnginePrivate::event(QEvent *event) { if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - processMessage(workerEvent->workerId(), workerEvent->takeData()); + processMessage(workerEvent->workerId(), workerEvent->data()); return true; } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event); @@ -152,19 +226,18 @@ bool QmlWorkerScriptEnginePrivate::event(QEvent *event) } } -void QmlWorkerScriptEnginePrivate::processMessage(int id, QmlWorkerScriptEngine::Data *data) +void QmlWorkerScriptEnginePrivate::processMessage(int id, const QVariant &data) { - QScriptValue worker = getWorker(id); - QScriptValue onmessage = worker.data(); + WorkerScript *script = workers.value(id); + if (!script) + return; - if (onmessage.isFunction()) { + if (script->callback.isFunction()) { QScriptValue args = workerEngine->newArray(1); - args.setProperty(0, variantToScriptValue(data->var, workerEngine)); + args.setProperty(0, variantToScriptValue(data, workerEngine)); - onmessage.call(worker, args); + script->callback.call(script->object, args); } - - if (data) delete data; } void QmlWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) @@ -190,14 +263,80 @@ void QmlWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) } } -WorkerDataEvent::WorkerDataEvent(int workerId, QmlWorkerScriptEngine::Data *data) +QVariant QmlWorkerScriptEnginePrivate::scriptValueToVariant(const QScriptValue &value) +{ + if (value.isBool()) { + return QVariant(value.toBool()); + } else if (value.isString()) { + return QVariant(value.toString()); + } else if (value.isNumber()) { + return QVariant((qreal)value.toNumber()); + } else if (value.isArray()) { + QVariantList list; + + quint32 length = (quint32)value.property("length").toNumber(); + + for (quint32 ii = 0; ii < length; ++ii) { + QVariant v = scriptValueToVariant(ii); + list << v; + } + + return QVariant(list); + } else if (value.isObject()) { + QVariantHash hash; + + QScriptValueIterator iter(value); + + while (iter.hasNext()) { + iter.next(); + hash.insert(iter.name(), scriptValueToVariant(iter.value())); + } + + return QVariant(hash); + } + + return QVariant(); + +} + +QScriptValue QmlWorkerScriptEnginePrivate::variantToScriptValue(const QVariant &value, QScriptEngine *engine) +{ + if (value.type() == QVariant::Bool) { + return QScriptValue(value.toBool()); + } else if (value.type() == QVariant::String) { + return QScriptValue(value.toString()); + } else if (value.type() == (QVariant::Type)QMetaType::QReal) { + return QScriptValue(value.toReal()); + } else if (value.type() == (QVariant::Type)QMetaType::QVariantList) { + QVariantList list = qvariant_cast<QVariantList>(value); + QScriptValue rv = engine->newArray(list.count()); + + for (quint32 ii = 0; ii < list.count(); ++ii) + rv.setProperty(ii, variantToScriptValue(list.at(ii), engine)); + + return rv; + } else if (value.type() == (QVariant::Type)QMetaType::QVariantHash) { + + QVariantHash hash = qvariant_cast<QVariantHash>(value); + + QScriptValue rv = engine->newObject(); + + for (QVariantHash::ConstIterator iter = hash.begin(); iter != hash.end(); ++iter) + rv.setProperty(iter.key(), variantToScriptValue(iter.value(), engine)); + + return rv; + } else { + return engine->nullValue(); + } +} + +WorkerDataEvent::WorkerDataEvent(int workerId, const QVariant &data) : QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) { } WorkerDataEvent::~WorkerDataEvent() { - if (m_data) { delete m_data; m_data = 0; } } int WorkerDataEvent::workerId() const @@ -205,11 +344,9 @@ int WorkerDataEvent::workerId() const return m_id; } -QmlWorkerScriptEngine::Data *WorkerDataEvent::takeData() +QVariant WorkerDataEvent::data() const { - QmlWorkerScriptEngine::Data *rv = m_data; - m_data = 0; - return rv; + return m_data; } WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) @@ -227,6 +364,16 @@ QUrl WorkerLoadEvent::url() const return m_url; } +WorkerRemoveEvent::WorkerRemoveEvent(int workerId) +: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) +{ +} + +int WorkerRemoveEvent::workerId() const +{ + return m_id; +} + QmlWorkerScriptEngine::QmlWorkerScriptEngine(QObject *parent) : QThread(parent), d(new QmlWorkerScriptEnginePrivate) { @@ -234,6 +381,7 @@ QmlWorkerScriptEngine::QmlWorkerScriptEngine(QObject *parent) start(QThread::LowPriority); d->m_wait.wait(&d->m_lock); d->moveToThread(this); + d->m_lock.unlock(); } QmlWorkerScriptEngine::~QmlWorkerScriptEngine() @@ -241,48 +389,44 @@ QmlWorkerScriptEngine::~QmlWorkerScriptEngine() delete d; d = 0; } -QmlWorkerScriptEngine::WorkerScript::WorkerScript() -: engine(0), id(0) +QmlWorkerScriptEnginePrivate::WorkerScript::WorkerScript() +: id(-1), initialized(false), owner(0) { } -void QmlWorkerScriptEngine::WorkerScript::executeUrl(const QUrl &url) +int QmlWorkerScriptEngine::registerWorkerScript(QmlWorkerScript *owner) { - engine->executeUrl(this, url); -} + QmlWorkerScriptEnginePrivate::WorkerScript *script = new QmlWorkerScriptEnginePrivate::WorkerScript; + script->id = d->m_nextId++; + script->owner = owner; -void QmlWorkerScriptEngine::WorkerScript::sendMessage(Data *data) -{ - engine->sendMessage(this, data); + d->m_lock.lock(); + d->workers.insert(script->id, script); + d->m_lock.unlock(); + + return script->id; } -void QmlWorkerScriptEngine::executeUrl(WorkerScript *script, const QUrl &data) +void QmlWorkerScriptEngine::removeWorkerScript(int id) { - QCoreApplication::postEvent(d, new WorkerLoadEvent(script->id, data)); + QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); } -/*! - Ownership of \a data transfers to QmlWorkerScriptEngine. It can not be modified by - the caller. -*/ -void QmlWorkerScriptEngine::sendMessage(WorkerScript *script, Data *data) +void QmlWorkerScriptEngine::executeUrl(int id, const QUrl &url) { - QCoreApplication::postEvent(d, new WorkerDataEvent(script->id, data)); + QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); } -QmlWorkerScriptEngine::WorkerScript *QmlWorkerScriptEngine::createWorkerScript() +void QmlWorkerScriptEngine::sendMessage(int id, const QVariant &data) { - WorkerScript *rv = new WorkerScript; - rv->engine = this; - rv->id = d->m_nextId++; - return rv; + QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); } void QmlWorkerScriptEngine::run() { d->m_lock.lock(); - d->workerEngine = new QScriptEngine; + d->workerEngine = new QmlWorkerScriptEnginePrivate::ScriptEngine(d); d->m_wait.wakeAll(); @@ -294,80 +438,13 @@ void QmlWorkerScriptEngine::run() } QmlWorkerScript::QmlWorkerScript(QObject *parent) -: QObject(parent), m_script(0) +: QObject(parent), m_engine(0), m_scriptId(-1) { } QmlWorkerScript::~QmlWorkerScript() { - if (m_script) { delete m_script; m_script = 0; } -} - -QVariant QmlWorkerScriptEnginePrivate::scriptValueToVariant(const QScriptValue &value) -{ - if (value.isBool()) { - return QVariant(value.toBool()); - } else if (value.isString()) { - return QVariant(value.toString()); - } else if (value.isNumber()) { - return QVariant((qreal)value.toNumber()); - } else if (value.isArray()) { - QVariantList list; - - quint32 length = (quint32)value.property("length").toNumber(); - - for (quint32 ii = 0; ii < length; ++ii) { - QVariant v = scriptValueToVariant(ii); - list << v; - } - - return QVariant(list); - } else if (value.isObject()) { - QVariantHash hash; - - QScriptValueIterator iter(value); - - while (iter.hasNext()) { - iter.next(); - hash.insert(iter.name(), scriptValueToVariant(iter.value())); - } - - return QVariant(hash); - } - - return QVariant(); - -} - -QScriptValue QmlWorkerScriptEnginePrivate::variantToScriptValue(const QVariant &value, QScriptEngine *engine) -{ - if (value.type() == QVariant::Bool) { - return QScriptValue(value.toBool()); - } else if (value.type() == QVariant::String) { - return QScriptValue(value.toString()); - } else if (value.type() == (QVariant::Type)QMetaType::QReal) { - return QScriptValue(value.toReal()); - } else if (value.type() == (QVariant::Type)QMetaType::QVariantList) { - QVariantList list = qvariant_cast<QVariantList>(value); - QScriptValue rv = engine->newArray(list.count()); - - for (quint32 ii = 0; ii < list.count(); ++ii) - rv.setProperty(ii, variantToScriptValue(list.at(ii), engine)); - - return rv; - } else if (value.type() == (QVariant::Type)QMetaType::QVariantHash) { - - QVariantHash hash = qvariant_cast<QVariantHash>(value); - - QScriptValue rv = engine->newObject(); - - for (QVariantHash::ConstIterator iter = hash.begin(); iter != hash.end(); ++iter) - rv.setProperty(iter.key(), variantToScriptValue(iter.value(), engine)); - - return rv; - } else { - return engine->nullValue(); - } + if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); } QUrl QmlWorkerScript::source() const @@ -382,37 +459,55 @@ void QmlWorkerScript::setSource(const QUrl &source) m_source = source; - if (m_script) - m_script->executeUrl(m_source); + if (m_engine) + m_engine->executeUrl(m_scriptId, m_source); emit sourceChanged(); } void QmlWorkerScript::sendMessage(const QScriptValue &message) { - if (!m_script) { + if (!m_engine) { qWarning("QmlWorkerScript: Attempt to send message before WorkerScript establishment"); return; } - QmlWorkerScriptEngine::Data *data = new QmlWorkerScriptEngine::Data; - data->var = QmlWorkerScriptEnginePrivate::scriptValueToVariant(message); - m_script->sendMessage(data); + m_engine->sendMessage(m_scriptId, QmlWorkerScriptEnginePrivate::scriptValueToVariant(message)); } void QmlWorkerScript::componentComplete() { - if (!m_script) { + if (!m_engine) { QmlEngine *engine = qmlEngine(this); if (!engine) { qWarning("QmlWorkerScript: componentComplete() called without qmlEngine() set"); return; } - m_script = QmlEnginePrivate::get(engine)->getWorkerScriptEngine()->createWorkerScript(); + + m_engine = QmlEnginePrivate::get(engine)->getWorkerScriptEngine(); + m_scriptId = m_engine->registerWorkerScript(this); if (m_source.isValid()) - m_script->executeUrl(m_source); + m_engine->executeUrl(m_scriptId, m_source); } } +bool QmlWorkerScript::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + QmlEngine *engine = qmlEngine(this); + if (engine) { + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); + QScriptValue value = + QmlWorkerScriptEnginePrivate::variantToScriptValue(workerEvent->data(), scriptEngine); + emit message(value); + } + return true; + } else { + return QObject::event(event); + } +} + + QML_DEFINE_TYPE(Qt, 4, 6, WorkerScript, QmlWorkerScript); diff --git a/src/declarative/qml/qmlworkerscript_p.h b/src/declarative/qml/qmlworkerscript_p.h index 90861f8..b7106c5 100644 --- a/src/declarative/qml/qmlworkerscript_p.h +++ b/src/declarative/qml/qmlworkerscript_p.h @@ -59,6 +59,7 @@ #include <QtScript/qscriptvalue.h> #include <QtCore/qurl.h> +class QmlWorkerScript; class QmlWorkerScriptEnginePrivate; class QmlWorkerScriptEngine : public QThread { @@ -67,26 +68,10 @@ public: QmlWorkerScriptEngine(QObject *parent = 0); virtual ~QmlWorkerScriptEngine(); - class Data; - class WorkerScript { - public: - void sendMessage(Data *); - void executeUrl(const QUrl &); - - private: - WorkerScript(); - friend class QmlWorkerScriptEngine; - QmlWorkerScriptEngine *engine; - int id; - }; - WorkerScript *createWorkerScript(); - - struct Data { - QVariant var; - }; - - void executeUrl(WorkerScript *, const QUrl &); - void sendMessage(WorkerScript *, Data *); + int registerWorkerScript(QmlWorkerScript *); + void removeWorkerScript(int); + void executeUrl(int, const QUrl &); + void sendMessage(int, const QVariant &); protected: virtual void run(); @@ -112,12 +97,15 @@ public slots: signals: void sourceChanged(); + void message(const QScriptValue &messageObject); protected: virtual void componentComplete(); + virtual bool event(QEvent *); private: - QmlWorkerScriptEngine::WorkerScript *m_script; + QmlWorkerScriptEngine *m_engine; + int m_scriptId; QUrl m_source; }; QML_DECLARE_TYPE(QmlWorkerScript); |