Add support pre-compiled bindings for QML custom parsers
authorSimon Hausmann <simon.hausmann@digia.com>
Wed, 27 Nov 2013 12:46:58 +0000 (13:46 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 29 Nov 2013 18:11:02 +0000 (19:11 +0100)
For example the x property in

    PropertyChanges {
        target: foo
        x: someItem.x - other.width / 2
    }

was compiled at run-time dynamically, which produces slower code (no type
information available) and slows down the type instantiation, because the
compilation happens every time at instantiation time (or later).

With this change, when the custom parser behind PropertyChanges requests a
binding ID for "x", the right hand side will be added to the bindings to
compile, then compiled and later at run-time the QQmlBinding constructor that
takes a QQmlBinding::Identifier can retrieve the correct compiled function from
the QV4::CompiledData::CompilationUnit.

Change-Id: I857fb2d39e82714b225bc9394b9904b795c6662b
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/qml/qqmlbinding.cpp
src/qml/qml/qqmlcompiler.cpp
src/qml/qml/qqmlcompiler_p.h
src/qml/qml/qqmlcustomparser.cpp

index 557267d..9e2fb07 100644 (file)
@@ -50,6 +50,7 @@
 #include <private/qqmltrace_p.h>
 #include <private/qqmlexpression_p.h>
 #include <private/qqmlscriptstring_p.h>
+#include <private/qqmlcontextwrapper_p.h>
 
 #include <QVariant>
 #include <QtCore/qdebug.h>
@@ -85,7 +86,14 @@ QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt,
         Q_ASSERT(typeData);
 
         if (QQmlCompiledData *cdata = typeData->compiledData()) {
-            rv = new QQmlBinding(cdata->primitives.at(id), obj, ctxtdata, url, lineNumber, 0);
+            QV4::ExecutionEngine *v4 = engine->v4engine();
+            QV4::Scope valueScope(v4);
+            QV4::ScopedObject scopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(v4->v8Engine, ctxtdata, obj));
+            QV4::Scoped<QV4::QmlBindingWrapper> wrapper(valueScope, new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, scopeObject));
+            QV4::ExecutionContext *qmlContext = wrapper->context();
+            QV4::Function *runtimeFunction = cdata->compilationUnit->runtimeFunctions[cdata->customParserBindings[id]];
+            QV4::ScopedValue function(valueScope, QV4::FunctionObject::creatScriptFunction(qmlContext, runtimeFunction));
+            rv = new QQmlBinding(function, obj, ctxtdata, url, lineNumber, 0);
         }
 
         typeData->release();
index 54fd002..150ea54 100644 (file)
@@ -2682,9 +2682,24 @@ const QMetaObject *QQmlCompiler::resolveType(const QString& name) const
     return qmltype->metaObject();
 }
 
-int QQmlCompiler::bindingIdentifier(const Variant &value)
+int QQmlCompiler::bindingIdentifier(const QString &name, const Variant &value, const BindingContext &ctxt)
 {
-    return output->indexForString(value.asScript());
+    JSBindingReference *reference = pool->New<JSBindingReference>();
+    reference->expression = value;
+    reference->property = pool->New<Property>();
+    reference->property->setName(name);
+    reference->value = 0;
+    reference->bindingContext = ctxt;
+    reference->bindingContext.owner++;
+
+    const int id = output->customParserBindings.count();
+    output->customParserBindings.append(0); // Filled in later.
+    reference->customParserBindingsIndex = id;
+
+    compileState->totalBindingsCount++;
+    compileState->bindings.prepend(reference);
+
+    return id;
 }
 
 // Ensures that the dynamic meta specification on obj is valid
@@ -3648,7 +3663,7 @@ bool QQmlCompiler::completeComponentBuild()
         binding.compiledIndex = cd->functionsToCompile.count() - 1;
         cd->expressionNames.insert(binding.compiledIndex, binding.property->name().toString().prepend(QStringLiteral("expression for ")));
 
-        if (componentStats)
+        if (componentStats && b->value)
             componentStats->componentStat.scriptBindings.append(b->value->location);
     }
 
@@ -3697,6 +3712,10 @@ bool QQmlCompiler::completeComponentBuild()
         for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {
             JSBindingReference &binding = *b;
             binding.compiledIndex = compileState->jsCompileData[binding.bindingContext.object].runtimeFunctionIndices[binding.compiledIndex];
+            if (!binding.value) { // Must be a binding requested from custom parser
+                Q_ASSERT(binding.customParserBindingsIndex >= 0 && binding.customParserBindingsIndex < output->customParserBindings.count());
+                output->customParserBindings[binding.customParserBindingsIndex] = binding.compiledIndex;
+            }
         }
     }
 
index 05c877d..443a80d 100644 (file)
@@ -150,6 +150,7 @@ public:
     // index in first hash is component index, hash inside maps from object index in that scope to integer id
     QHash<int, QHash<int, int> > objectIndexToIdPerComponent;
     QHash<int, int> objectIndexToIdForRoot;
+    QVector<int> customParserBindings; // index is binding identifier, value is compiled function index.
 
     bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); }
     bool isCompositeType() const { return !datas.at(qmlUnit->indexOfRootObject).isEmpty(); }
@@ -231,7 +232,7 @@ namespace QQmlCompilerTypes {
         QQmlScript::Value *value;
 
         int compiledIndex : 16;
-        int sharedIndex : 16;
+        int customParserBindingsIndex : 16;
 
         BindingContext bindingContext;
 
@@ -340,7 +341,7 @@ public:
 
     int evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue, bool *ok) const; // for QQmlCustomParser::evaluateEnum
     const QMetaObject *resolveType(const QString& name) const; // for QQmlCustomParser::resolveType
-    int bindingIdentifier(const QQmlScript::Variant& value); // for QQmlCustomParser::bindingIndex
+    int bindingIdentifier(const QString &name, const QQmlScript::Variant& value, const QQmlCompilerTypes::BindingContext &ctxt); // for QQmlCustomParser::bindingIndex
 
 private:
     typedef QQmlCompiledData::Instruction Instruction;
index eba2e14..19e4900 100644 (file)
@@ -313,8 +313,7 @@ const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const
 */
 QQmlBinding::Identifier QQmlCustomParser::bindingIdentifier(const QQmlScript::Variant &value, const QString& name)
 {
-    Q_UNUSED(name);
-    return compiler->bindingIdentifier(value);
+    return compiler->bindingIdentifier(name, value, QQmlCompilerTypes::BindingContext(object));
 }
 
 QT_END_NAMESPACE