Qml JavaScript code generation cleanups
authorSimon Hausmann <simon.hausmann@digia.com>
Fri, 18 Oct 2013 13:36:40 +0000 (15:36 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Sun, 20 Oct 2013 19:11:54 +0000 (21:11 +0200)
* Run the binding expressions, functions and signal handlers through
  the V4 codegen _per_ component, and run the isel at the end for the
  entire file. We need to do per-component codegen because we want to
  set up the correct id and object scopes, which are different for the
  root component and anonymous components.
* Changed V4IR::Module to allow for the concept of "qml modules" where
  there is no root function defined. This is a logical consequence of
  running v4 codegen multiple times with different input but the same
  V4IR::Module.

Change-Id: Ib3a719f83507cbab7c2e4e145ccad5b663c795cf
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/compiler/qv4compileddata.cpp
src/qml/compiler/qv4compileddata_p.h
src/qml/compiler/qv4compiler.cpp
src/qml/compiler/qv4isel_p.cpp
src/qml/compiler/qv4jsir.cpp
src/qml/compiler/qv4jsir_p.h
src/qml/qml/qqmlcompiler.cpp
src/qml/qml/qqmlcompiler_p.h

index 887edc0..b485dcc 100644 (file)
@@ -140,7 +140,10 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
     std::sort(runtimeFunctionsSortedByAddress.begin(), runtimeFunctionsSortedByAddress.end(), functionSortHelper);
 #endif
 
-    return runtimeFunctions[data->indexOfRootFunction];
+    if (data->indexOfRootFunction != -1)
+        return runtimeFunctions[data->indexOfRootFunction];
+    else
+        return 0;
 }
 
 void CompilationUnit::unlink()
index 6784707..bf6794e 100644 (file)
@@ -161,7 +161,7 @@ struct Unit
     uint offsetToRegexpTable;
     uint jsClassTableSize;
     uint offsetToJSClassTable;
-    uint indexOfRootFunction;
+    qint32 indexOfRootFunction;
     quint32 sourceFileIndex;
 
     QString stringAt(int idx) const {
index 8cd4c8e..7d8c188 100644 (file)
@@ -198,6 +198,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(int *total
     unit->offsetToRegexpTable = unit->offsetToLookupTable + unit->lookupTableSize * CompiledData::Lookup::calculateSize();
     unit->jsClassTableSize = jsClasses.count();
     unit->offsetToJSClassTable = unit->offsetToRegexpTable + unit->regexpTableSize * CompiledData::RegExp::calculateSize();
+    unit->indexOfRootFunction = -1;
     unit->sourceFileIndex = getStringId(irModule->fileName);
 
     // write strings and string table
index 500c2bd..483cb8e 100644 (file)
@@ -80,9 +80,6 @@ EvalISelFactory::~EvalISelFactory()
 
 QV4::CompiledData::CompilationUnit *EvalInstructionSelection::compile(bool generateUnitData)
 {
-    Function *rootFunction = irModule->rootFunction;
-    if (!rootFunction)
-        return 0;
     for (int i = 0; i < irModule->functions.size(); ++i)
         run(i);
 
index 869bf4a..50afcf2 100644 (file)
@@ -610,8 +610,10 @@ Function *Module::newFunction(const QString &name, Function *outer)
     Function *f = new Function(this, outer, name);
     functions.append(f);
     if (!outer) {
-        assert(!rootFunction);
-        rootFunction = f;
+        if (!isQmlModule) {
+            assert(!rootFunction);
+            rootFunction = f;
+        }
     } else {
         outer->nestedFunctions.append(f);
     }
index d0782a3..7b0ee52 100644 (file)
@@ -682,10 +682,14 @@ struct Q_QML_EXPORT Module {
     QVector<Function *> functions;
     Function *rootFunction;
     QString fileName;
+    bool isQmlModule; // implies rootFunction is always 0
 
     Function *newFunction(const QString &name, Function *outer);
 
-    Module() : rootFunction(0) {}
+    Module()
+        : rootFunction(0)
+        , isQmlModule(false)
+    {}
     ~Module();
 
     void setFileName(const QString &name);
index 697d255..2cfb074 100644 (file)
@@ -810,9 +810,8 @@ bool QQmlCompiler::compile(QQmlEngine *engine,
     this->unit = unit;
     this->unitRoot = root;
     this->output = out;
-    this->functionsToCompile.clear();
-    this->compiledMetaMethods.clear();
-    this->compiledSignalHandlers.clear();
+    this->jsModule.reset(new QQmlJS::V4IR::Module);
+    this->jsModule->isQmlModule = true;
 
     // Compile types
     const QList<QQmlTypeData::TypeReference>  &resolvedTypes = unit->resolvedTypes();
@@ -919,58 +918,10 @@ void QQmlCompiler::compileTree(QQmlScript::Object *tree)
     if (!buildObject(tree, BindingContext()) || !completeComponentBuild())
         return;
 
-    const QQmlScript::Parser &parser = unit->parser();
-    QQmlJS::Engine *jsEngine = parser.jsEngine();
-    QQmlJS::MemoryPool *pool = jsEngine->pool();
-
-    foreach (JSBindingReference *root, allBindingReferenceRoots) {
-        for (JSBindingReference *b = root; b; b = b->nextReference) {
-            JSBindingReference &binding = *b;
-
-            QQmlJS::AST::Node *node = binding.expression.asAST();
-            // Always wrap this in an ExpressionStatement, to make sure that
-            // property var foo: function() { ... } results in a closure initialization.
-            if (!node->statementCast()) {
-                AST::ExpressionNode *expr = node->expressionCast();
-                node = new (pool) AST::ExpressionStatement(expr);
-            }
-
-            functionsToCompile.append(node);
-            binding.compiledIndex = functionsToCompile.count() - 1;
-        }
-    }
-
-    if (!functionsToCompile.isEmpty()) {
-        JSCodeGen jsCodeGen;
-
-        V4IR::Module jsModule;
-        const QString &sourceCode = jsEngine->code();
-        AST::UiProgram *qmlRoot = parser.qmlRoot();
-
-        const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(unit->finalUrlString(), sourceCode, &jsModule, jsEngine, qmlRoot, functionsToCompile);
-
-        foreach (JSBindingReference *root, allBindingReferenceRoots) {
-            for (JSBindingReference *b = root; b; b = b->nextReference) {
-                JSBindingReference &binding = *b;
-                functionsToCompile.append(binding.expression.asAST());
-                binding.compiledIndex = runtimeFunctionIndices[binding.compiledIndex];
-            }
-        }
-
-        foreach (const CompiledMetaMethod &cmm, compiledMetaMethods) {
-            typedef QQmlVMEMetaData VMD;
-            VMD *vmd = (QQmlVMEMetaData *)cmm.obj->synthdata.data();
-            VMD::MethodData &md = *(vmd->methodData() + cmm.methodIndex);
-            md.runtimeFunctionIndex = runtimeFunctionIndices.at(cmm.compiledFunctionIndex);
-        }
-
-        foreach (const CompiledSignalHandlerExpression &expr, compiledSignalHandlers) {
-            expr.signal->signalData.functionIndex = runtimeFunctionIndices.at(expr.compiledHandlerIndex);
-        }
-
+    if (!jsModule->functions.isEmpty()) {
         QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
-        QV4::Compiler::JSUnitGenerator jsUnitGenerator(&jsModule);
-        QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &jsModule, &jsUnitGenerator));
+        QV4::Compiler::JSUnitGenerator jsUnitGenerator(jsModule.data());
+        QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, jsModule.data(), &jsUnitGenerator));
         isel->setUseFastLookups(false);
         QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/true);
         output->compilationUnit = jsUnit;
@@ -1385,7 +1336,7 @@ void QQmlCompiler::genObjectBody(QQmlScript::Object *obj)
         } else if (v->type == Value::SignalExpression) {
 
             Instruction::StoreSignal store;
-            store.runtimeFunctionIndex = v->signalData.functionIndex;
+            store.runtimeFunctionIndex = compileState->runtimeFunctionIndices.at(v->signalData.functionIndex);
             store.handlerName = output->indexForString(prop->name().toString());
             store.parameters = output->indexForString(obj->metatype->signalParameterStringForJS(prop->index));
             store.signalIndex = prop->index;
@@ -1788,17 +1739,11 @@ bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *o
             prop->index = obj->metatype->originalClone(prop->index);
             prop->values.first()->signalData.signalExpressionContextStack = ctxt.stack;
 
-            CompiledSignalHandlerExpression expr;
-            expr.signal = prop->values.first();
-
             QList<QByteArray> parameters = obj->metatype->signalParameterNames(prop->index);
 
             AST::FunctionDeclaration *funcDecl = convertSignalHandlerExpressionToFunctionDeclaration(unit->parser().jsEngine(), prop->values.first()->value.asAST(), propName.toString(), parameters);
-            functionsToCompile.append(funcDecl);
-
-            expr.compiledHandlerIndex = functionsToCompile.count() - 1;
-            compiledSignalHandlers.append(expr);
-            prop->values.first()->signalData.functionIndex = 0; // To be filled in before gen()
+            compileState->functionsToCompile.append(funcDecl);
+            prop->values.first()->signalData.functionIndex = compileState->functionsToCompile.count() - 1;
 
             QString errorString;
             obj->metatype->signalParameterStringForJS(prop->index, &errorString);
@@ -3305,12 +3250,12 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod
         vmd->methodCount++;
         md = methodData;
 
-        CompiledMetaMethod cmm;
+        QQmlCompilerTypes::ComponentCompileState::CompiledMetaMethod cmm;
         cmm.obj = obj;
         cmm.methodIndex = vmd->methodCount - 1;
-        functionsToCompile.append(s->funcDecl);
-        cmm.compiledFunctionIndex = functionsToCompile.count() - 1;
-        compiledMetaMethods.append(cmm);
+        compileState->functionsToCompile.append(s->funcDecl);
+        cmm.compiledFunctionIndex = compileState->functionsToCompile.count() - 1;
+        compileState->compiledMetaMethods.append(cmm);
     }
 
     if (aliasCount)
@@ -3679,15 +3624,51 @@ bool QQmlCompiler::completeComponentBuild()
          aliasObject = compileState->aliasingObjects.next(aliasObject)) 
         COMPILE_CHECK(buildDynamicMetaAliases(aliasObject));
 
+    const QQmlScript::Parser &parser = unit->parser();
+    QQmlJS::Engine *jsEngine = parser.jsEngine();
+    QQmlJS::MemoryPool *pool = jsEngine->pool();
+
     for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {
 
         JSBindingReference &binding = *b;
         binding.dataType = BindingReference::QtScript;
 
+        QQmlJS::AST::Node *node = binding.expression.asAST();
+        // Always wrap this in an ExpressionStatement, to make sure that
+        // property var foo: function() { ... } results in a closure initialization.
+        if (!node->statementCast()) {
+            AST::ExpressionNode *expr = node->expressionCast();
+            node = new (pool) AST::ExpressionStatement(expr);
+        }
+
+        compileState->functionsToCompile.append(node);
+        binding.compiledIndex = compileState->functionsToCompile.count() - 1;
+
         if (componentStats)
             componentStats->componentStat.scriptBindings.append(b->value->location);
     }
-    allBindingReferenceRoots.append(compileState->bindings.first());
+
+    if (!compileState->functionsToCompile.isEmpty()) {
+        JSCodeGen jsCodeGen;
+
+        const QString &sourceCode = jsEngine->code();
+        AST::UiProgram *qmlRoot = parser.qmlRoot();
+
+        const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(unit->finalUrlString(), sourceCode, jsModule.data(), jsEngine, qmlRoot, compileState->functionsToCompile);
+        compileState->runtimeFunctionIndices = runtimeFunctionIndices;
+
+        for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {
+            JSBindingReference &binding = *b;
+            binding.compiledIndex = runtimeFunctionIndices[binding.compiledIndex];
+        }
+
+        foreach (const QQmlCompilerTypes::ComponentCompileState::CompiledMetaMethod &cmm, compileState->compiledMetaMethods) {
+            typedef QQmlVMEMetaData VMD;
+            VMD *vmd = (QQmlVMEMetaData *)cmm.obj->synthdata.data();
+            VMD::MethodData &md = *(vmd->methodData() + cmm.methodIndex);
+            md.runtimeFunctionIndex = runtimeFunctionIndices.at(cmm.compiledFunctionIndex);
+        }
+    }
 
     // Check pop()'s matched push()'s
     Q_ASSERT(compileState->objectDepth.depth() == 0);
index c8e5c90..142d8c6 100644 (file)
@@ -302,6 +302,16 @@ namespace QQmlCompilerTypes {
         typedef QFieldList<O, &O::nextAliasingObject> AliasingObjectsList;
         AliasingObjectsList aliasingObjects;
         QQmlScript::Object *root;
+        QList<QQmlJS::AST::Node*> functionsToCompile;
+        QVector<int> runtimeFunctionIndices;
+
+        struct CompiledMetaMethod
+        {
+            QQmlScript::Object *obj;
+            int methodIndex;
+            int compiledFunctionIndex; // index in functionToCompile
+        };
+        QList<CompiledMetaMethod> compiledMetaMethods;
     };
 };
 
@@ -461,21 +471,7 @@ private:
     int cachedComponentTypeRef;
     int cachedTranslationContextIndex;
 
-    QList<QQmlJS::AST::Node*> functionsToCompile;
-    QList<QQmlCompilerTypes::JSBindingReference*> allBindingReferenceRoots;
-    struct CompiledMetaMethod
-    {
-        QQmlScript::Object *obj;
-        int methodIndex;
-        int compiledFunctionIndex; // index in functionToCompile
-    };
-    QList<CompiledMetaMethod> compiledMetaMethods;
-    struct CompiledSignalHandlerExpression
-    {
-        QQmlScript::Value *signal;
-        int compiledHandlerIndex; // index in functionsToCompile
-    };
-    QList<CompiledSignalHandlerExpression> compiledSignalHandlers;
+    QScopedPointer<QQmlJS::V4IR::Module> jsModule;
 
     // Compiler component statistics.  Only collected if QML_COMPILER_STATS=1
     struct ComponentStat