From cb70f2096cbc66e6e8b5324e0e9508c788144e7e Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 2 Dec 2009 12:35:38 +1000 Subject: Cache binding closures --- src/declarative/qml/qmlcompileddata.cpp | 1 + src/declarative/qml/qmlcompiler.cpp | 9 +++++ src/declarative/qml/qmlcompiler_p.h | 1 + src/declarative/qml/qmlcontextscriptclass.cpp | 33 ++++++++++++++-- src/declarative/qml/qmlcontextscriptclass_p.h | 1 + src/declarative/qml/qmlengine.cpp | 2 +- src/declarative/qml/qmlengine_p.h | 2 + src/declarative/qml/qmlexpression.cpp | 56 +++++++++++++++++++++------ src/declarative/qml/qmlexpression_p.h | 2 + src/declarative/qml/qmlrewrite.cpp | 16 ++++++++ src/declarative/qml/qmlrewrite_p.h | 11 ++++++ 11 files changed, 118 insertions(+), 16 deletions(-) diff --git a/src/declarative/qml/qmlcompileddata.cpp b/src/declarative/qml/qmlcompileddata.cpp index 44d2745..3428574 100644 --- a/src/declarative/qml/qmlcompileddata.cpp +++ b/src/declarative/qml/qmlcompileddata.cpp @@ -168,6 +168,7 @@ QmlCompiledData::~QmlCompiledData() importCache->release(); qDeleteAll(programs); + qDeleteAll(cachedValues); } 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..a0e9418 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -2529,12 +2529,21 @@ 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(); + if (isSharable) + pc |= 0x80000000; + output->programs.append(0); + output->cachedValues.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..ca26062 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -114,6 +114,7 @@ public: QList locations; QList bytecode; QList programs; + QList cachedValues; QList propertyCaches; QList contextCaches; QList scripts; 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 context; QGuard 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 9afb81e..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) 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..703fcf5 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->cachedValues.at(progIdx)) { + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + scriptContext->pushScope(ep->contextClass->newSharedContext()); + dd->cachedValues[progIdx] = new QScriptValue(scriptEngine->evaluate(data->expression, data->url.toString(), data->line)); + scriptEngine->popContext(); + } + + data->expressionFunction = *dd->cachedValues.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->programs.at(progIdx)) { + dd->programs[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->programs[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/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; -- cgit v0.12