diff options
26 files changed, 320 insertions, 41 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index 2aa5aa5a3c..cbf58599ac 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -1201,18 +1201,23 @@ int QmlUnitGenerator::getStringId(const QString &str) const QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output) { return generateJSCodeForFunctionsAndBindings(fileName, output->code, &output->jsModule, &output->jsParserEngine, - output->program, output->functions); + output->program, /* ### */output->program, output->functions); } QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, - QQmlJS::Engine *jsEngine, AST::UiProgram *qmlRoot, const QList<AST::Node*> &functions) + QQmlJS::Engine *jsEngine, AST::UiProgram *qmlRoot, AST::Node *contextRoot, + const QList<AST::Node*> &functions, + const ObjectIdMapping &objectIds) { + this->idObjects = objectIds; + QVector<int> runtimeFunctionIndices(functions.size()); _module = jsModule; _module->setFileName(fileName); - QmlScanner scan(this, sourceCode); - scan.begin(qmlRoot, QmlBinding); + ScanFunctions scan(this, sourceCode, GlobalCode); + scan.enterEnvironment(0, QmlBinding); + scan.enterQmlScope(qmlRoot, "context scope"); foreach (AST::Node *node, functions) { Q_ASSERT(node != qmlRoot); AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(node); @@ -1221,7 +1226,8 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QString &fil scan(function ? function->body : node); scan.leaveEnvironment(); } - scan.end(); + scan.leaveEnvironment(); + scan.leaveEnvironment(); _env = 0; _function = _module->functions.at(defineFunction(QString("context scope"), qmlRoot, 0, 0)); @@ -1267,16 +1273,25 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QString &fil return runtimeFunctionIndices; } - -void JSCodeGen::QmlScanner::begin(AST::Node *rootNode, CompilationMode compilationMode) +V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col) const { - enterEnvironment(0, compilationMode); - enterFunction(rootNode, "context scope", 0, 0, 0, /*isExpression*/false); -} + V4IR::Expr *result = 0; + // Implement QML lookup semantics in the current file context. -void JSCodeGen::QmlScanner::end() -{ - leaveEnvironment(); + // Look for IDs first. + foreach (const IdMapping &mapping, idObjects) + if (name == mapping.name) { + result = _block->QML_CONTEXT_ID_MEMBER(mapping.name, mapping.idIndex, line, col); + break; + } + + if (result) { + _function->hasQmlDependencies = true; + return result; + } + + // fall back to name lookup at run-time. + return 0; } SignalHandlerConverter::SignalHandlerConverter(QQmlEnginePrivate *enginePrivate, ParsedQML *parsedQML, diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h index 1830b62772..0f4c2b145a 100644 --- a/src/qml/compiler/qqmlcodegenerator_p.h +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -349,24 +349,24 @@ struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen : QQmlJS::Codegen(/*strict mode*/false) {} + struct IdMapping + { + QString name; + int idIndex; + }; + typedef QVector<IdMapping> ObjectIdMapping; + // Returns mapping from input functions to index in V4IR::Module::functions / compiledData->runtimeFunctions QVector<int> generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output); QVector<int> generateJSCodeForFunctionsAndBindings(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, - QQmlJS::Engine *jsEngine, AST::UiProgram *qmlRoot, const QList<AST::Node*> &functions); - -private: - struct QmlScanner : public ScanFunctions - { - QmlScanner(JSCodeGen *cg, const QString &sourceCode) - : ScanFunctions(cg, sourceCode, /*default program mode*/GlobalCode) - , codeGen(cg) - {} + QQmlJS::Engine *jsEngine, AST::UiProgram *qmlRoot, AST::Node *contextRoot, const QList<AST::Node*> &functions, + const ObjectIdMapping &objectIds = ObjectIdMapping()); - void begin(AST::Node *rootNode, CompilationMode compilationMode); - void end(); +protected: + virtual V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col) const; - JSCodeGen *codeGen; - }; +private: + ObjectIdMapping idObjects; }; } // namespace QtQml diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 70dbdb1af5..cb15c2c885 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1448,6 +1448,10 @@ V4IR::Expr *Codegen::identifier(const QString &name, int line, int col) f = f->outer; } + // This hook allows implementing QML lookup semantics + if (V4IR::Expr *fallback = fallbackNameLookup(name, line, col)) + return fallback; + if (!e->parent && (!f || !f->insideWithOrCatch) && _env->compilationMode != EvalCode && e->compilationMode != QmlBinding) return _block->GLOBALNAME(name, line, col); @@ -1456,6 +1460,14 @@ V4IR::Expr *Codegen::identifier(const QString &name, int line, int col) } +V4IR::Expr *Codegen::fallbackNameLookup(const QString &name, int line, int col) const +{ + Q_UNUSED(name) + Q_UNUSED(line) + Q_UNUSED(col) + return 0; +} + bool Codegen::visit(IdentifierExpression *ast) { if (hasError) diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index dee9e13097..369df712c5 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -323,6 +323,8 @@ protected: void variableDeclarationList(AST::VariableDeclarationList *ast); V4IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + // Hook provided to implement QML lookup semantics + virtual V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col) const; // nodes virtual bool visit(AST::ArgumentList *ast); @@ -466,6 +468,9 @@ protected: void enterEnvironment(AST::Node *node, CompilationMode compilationMode); void leaveEnvironment(); + void enterQmlScope(AST::Node *ast, const QString &name) + { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0, /*isExpression*/false); } + protected: using Visitor::visit; using Visitor::endVisit; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index eda9751980..60a697e53e 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -232,6 +232,12 @@ struct Function quint32 nInnerFunctions; quint32 innerFunctionsOffset; Location location; + + // Qml Extensions Begin + quint32 nDependingIdObjects; + quint32 dependingIdObjectsOffset; + // Qml Extensions End + // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] // quint32 offsetForInnerFunctions[nInnerFunctions] @@ -240,9 +246,12 @@ struct Function const quint32 *formalsTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + formalsOffset); } const quint32 *localsTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + localsOffset); } const quint32 *lineNumberMapping() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + lineNumberMappingOffset); } + const quint32 *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); } + + inline bool hasQmlDependencies() const { return nDependingIdObjects; } - static int calculateSize(int nFormals, int nLocals, int nInnerfunctions, int lineNumberMappings) { - return (sizeof(Function) + (nFormals + nLocals + nInnerfunctions + 2 * lineNumberMappings) * sizeof(quint32) + 7) & ~0x7; + static int calculateSize(int nFormals, int nLocals, int nInnerfunctions, int lineNumberMappings, int nIdObjectDependencies) { + return (sizeof(Function) + (nFormals + nLocals + nInnerfunctions + 2 * lineNumberMappings + nIdObjectDependencies) * sizeof(quint32) + 7) & ~0x7; } }; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 2d14c0f69a..0b3e85352e 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -169,6 +169,14 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(int *total registerString(*f->formals.at(i)); for (int i = 0; i < f->locals.size(); ++i) registerString(*f->locals.at(i)); + + if (f->hasQmlDependencies) { + QQmlJS::V4IR::QmlDependenciesCollector depCollector; + QSet<int> idObjectDeps = depCollector.run(f); + if (!idObjectDeps.isEmpty()) + qmlIdObjectDependenciesPerFunction.insert(f, idObjectDeps); + } + } int unitSize = QV4::CompiledData::Unit::calculateSize(headerSize, strings.size(), irModule->functions.size(), regexps.size(), @@ -184,7 +192,9 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(int *total if (lineNumberMapping != lineNumberMappingsPerFunction.constEnd()) lineNumberMappingCount = lineNumberMapping->count() / 2; - functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), lineNumberMappingCount); + const int qmlIdDepsCount = f->hasQmlDependencies ? qmlIdObjectDependenciesPerFunction.value(f).count() : 0; + + functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), lineNumberMappingCount, qmlIdDepsCount); } const int totalSize = unitSize + functionDataSize + stringDataSize + jsClassDataSize; @@ -309,6 +319,14 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QQmlJS::V4 function->nInnerFunctions = irFunction->nestedFunctions.size(); function->innerFunctionsOffset = function->lineNumberMappingOffset + function->nLineNumberMappingEntries * 2 * sizeof(quint32); + function->nDependingIdObjects = 0; + QSet<int> qmlIdObjectDeps; + if (irFunction->hasQmlDependencies) { + qmlIdObjectDeps = qmlIdObjectDependenciesPerFunction.value(irFunction); + function->nDependingIdObjects = qmlIdObjectDeps.count(); + function->dependingIdObjectsOffset = function->innerFunctionsOffset + function->nInnerFunctions * sizeof(quint32); + } + function->location.line = irFunction->line; function->location.column = irFunction->column; @@ -333,7 +351,12 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QQmlJS::V4 for (int i = 0; i < irFunction->nestedFunctions.size(); ++i) innerFunctions[i] = functionOffsets.value(irFunction->nestedFunctions.at(i)); - return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions, function->nLineNumberMappingEntries); + // write QML dependencies + quint32 *writtenIdDeps = (quint32 *)(f + function->dependingIdObjectsOffset); + foreach (int id, qmlIdObjectDeps) + *writtenIdDeps++ = id; + + return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions, function->nLineNumberMappingEntries, function->nDependingIdObjects); } diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 42ef3242ea..40b3fe25c0 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -92,6 +92,7 @@ struct Q_QML_EXPORT JSUnitGenerator { QList<QList<CompiledData::JSClassMember> > jsClasses; uint jsClassDataSize; uint headerSize; + QHash<QQmlJS::V4IR::Function *, QSet<int> > qmlIdObjectDependenciesPerFunction; }; } diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 4c1fe360d1..f0b8983038 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -104,7 +104,8 @@ QT_BEGIN_NAMESPACE F(AddNumberParams, addNumberParams) \ F(MulNumberParams, mulNumberParams) \ F(SubNumberParams, subNumberParams) \ - F(LoadThis, loadThis) + F(LoadThis, loadThis) \ + F(LoadIdObject, loadIdObject) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -502,6 +503,11 @@ union Instr MOTH_INSTR_HEADER Param result; }; + struct instr_loadIdObject { + MOTH_INSTR_HEADER + Param result; + int id; + }; instr_common common; instr_ret ret; @@ -559,6 +565,7 @@ union Instr instr_mulNumberParams mulNumberParams; instr_subNumberParams subNumberParams; instr_loadThis loadThis; + instr_loadIdObject loadIdObject; static int size(Type type); }; diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp index 461f028c99..1f00af3972 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -948,6 +948,11 @@ void InstructionSelection::loadThisObject(V4IR::Temp *temp) #endif } +void InstructionSelection::loadIdObject(int id, V4IR::Temp *temp) +{ + generateFunctionCall(temp, __qmljs_get_id_object, Assembler::ContextRegister, Assembler::TrustedImm32(id)); +} + void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { if (targetTemp->kind == V4IR::Temp::PhysicalRegister) { diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h index 40d1aa5275..8178866656 100644 --- a/src/qml/compiler/qv4isel_masm_p.h +++ b/src/qml/compiler/qv4isel_masm_p.h @@ -1469,6 +1469,7 @@ protected: virtual void callSubscript(V4IR::Expr *base, V4IR::Expr *index, V4IR::ExprList *args, V4IR::Temp *result); virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadIdObject(int id, V4IR::Temp *temp); virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); virtual void loadString(const QString &str, V4IR::Temp *targetTemp); virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index a25da4d4ab..2db30aa7f8 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -419,6 +419,14 @@ void InstructionSelection::loadThisObject(V4IR::Temp *temp) addInstruction(load); } +void InstructionSelection::loadIdObject(int id, V4IR::Temp *temp) +{ + Instruction::LoadIdObject load; + load.result = getResultParam(temp); + load.id = id; + addInstruction(load); +} + void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { assert(sourceConst); diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index f5a14e15ae..df5c71ce8c 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -114,6 +114,7 @@ protected: virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadIdObject(int id, V4IR::Temp *temp); virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); virtual void loadString(const QString &str, V4IR::Temp *targetTemp); virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 851d5661ff..b9341163de 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -135,7 +135,10 @@ void IRDecoder::visitMove(V4IR::Move *s) return; } } else if (V4IR::Member *m = s->source->asMember()) { - if (m->base->asTemp() || m->base->asConst()) { + if (m->type == V4IR::Member::MemberByObjectId) { + loadIdObject(m->objectId, t); + return; + } else if (m->base->asTemp() || m->base->asConst()) { getProperty(m->base, *m->name, t); return; } diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index e3146add66..6e607d901c 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -140,6 +140,7 @@ public: // to implement by subclasses: virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; virtual void loadThisObject(V4IR::Temp *temp) = 0; + virtual void loadIdObject(int id, V4IR::Temp *temp) = 0; virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) = 0; virtual void loadString(const QString &str, V4IR::Temp *targetTemp) = 0; virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) = 0; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 1c9620e1f5..86a13cfe99 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -421,6 +421,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_define_object_literal"; case V4IR::Name::builtin_setup_argument_object: return "builtin_setup_argument_object"; + case V4IR::Name::builtin_qml_id_scope: + return "builtin_qml_id_scope"; } return "builtin_(###FIXME)"; }; @@ -817,6 +819,14 @@ Expr *BasicBlock::MEMBER(Expr *base, const QString *name) return e; } +Expr *BasicBlock::QML_CONTEXT_ID_MEMBER(const QString &id, int objectId, quint32 line, quint32 column) +{ + Member*e = function->New<Member>(); + Name *base = NAME(Name::builtin_qml_id_scope, line, column); + e->initQmlIdObject(base, function->newString(id), objectId); + return e; +} + Stmt *BasicBlock::EXP(Expr *expr) { if (isTerminated()) @@ -1003,7 +1013,18 @@ void CloneExpr::visitSubscript(Subscript *e) void CloneExpr::visitMember(Member *e) { - cloned = block->MEMBER(clone(e->base), e->name); + Member *m = static_cast<Member*>(block->MEMBER(clone(e->base), e->name)); + if (e->type == Member::MemberByObjectId) { + m->type = e->type; + m->objectId = e->objectId; + } + cloned = m; +} + +void QmlDependenciesCollector::visitPhi(Phi *s) { + s->targetTemp->accept(this); + foreach (Expr *e, s->d->incoming) + e->accept(this); } } // end of namespace IR diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index fe8425ab71..1f69ac4964 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -322,7 +322,8 @@ struct Name: Expr { builtin_define_array, builtin_define_getter_setter, builtin_define_object_literal, - builtin_setup_argument_object + builtin_setup_argument_object, + builtin_qml_id_scope }; const QString *id; @@ -512,13 +513,31 @@ struct Subscript: Expr { }; struct Member: Expr { + enum MemberType { + MemberByName, + // QML extensions + MemberByObjectId // lookup in context's id values + }; + + MemberType type; Expr *base; const QString *name; + int objectId; void init(Expr *base, const QString *name) { + this->type = MemberByName; this->base = base; this->name = name; + this->objectId = -1; + } + + void initQmlIdObject(Expr *base, const QString *name, int objectId) + { + this->type = MemberByObjectId; + this->base = base; + this->name = name; + this->objectId = objectId; } virtual void accept(ExprVisitor *v) { v->visitMember(this); } @@ -695,7 +714,8 @@ struct Function { uint isNamedExpression : 1; uint hasTry: 1; uint hasWith: 1; - uint unused : 26; + uint hasQmlDependencies : 1; + uint unused : 25; // Location of declaration in source code (-1 if not specified) int line; @@ -716,6 +736,7 @@ struct Function { , isNamedExpression(false) , hasTry(false) , hasWith(false) + , hasQmlDependencies(false) , unused(0) , line(-1) , column(-1) @@ -810,6 +831,7 @@ struct BasicBlock { Expr *NEW(Expr *base, ExprList *args = 0); Expr *SUBSCRIPT(Expr *base, Expr *index); Expr *MEMBER(Expr *base, const QString *name); + Expr *QML_CONTEXT_ID_MEMBER(const QString &id, int idIndex, quint32 line, quint32 column); Stmt *EXP(Expr *expr); @@ -909,6 +931,69 @@ private: V4IR::Expr *cloned; }; +struct QmlDependenciesCollector : public V4IR::StmtVisitor, V4IR::ExprVisitor +{ + QSet<int> run(Function *function) + { + QSet<int> dependencies; + qSwap(_usedIdObjects, dependencies); + for (int i = 0; i < function->basicBlocks.count(); ++i) { + BasicBlock *bb = function->basicBlocks.at(i); + for (int j = 0; j < bb->statements.count(); ++j) { + Stmt *s = bb->statements.at(j); + s->accept(this); + } + } + qSwap(_usedIdObjects, dependencies); + return dependencies; + } + +protected: + QSet<int> _usedIdObjects; + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitSubscript(Subscript *e) { + e->base->accept(this); + e->index->accept(this); + } + + virtual void visitMember(Member *e) { + e->base->accept(this); + if (e->type == Member::MemberByObjectId) + _usedIdObjects.insert(e->objectId); + } + + virtual void visitExp(Exp *s) {s->expr->accept(this);} + virtual void visitMove(Move *s) { + s->source->accept(this); + s->target->accept(this); + } + + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitPhi(Phi *s); +}; + } // end of namespace IR } // end of namespace QQmlJS diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp index 9c90388c9e..e2d627b809 100644 --- a/src/qml/compiler/qv4regalloc.cpp +++ b/src/qml/compiler/qv4regalloc.cpp @@ -334,6 +334,12 @@ protected: // IRDecoder addDef(temp); } + virtual void loadIdObject(int id, V4IR::Temp *temp) + { + addDef(temp); + addCall(); + } + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { addDef(targetTemp); diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 7c61a34190..523ef9c1e7 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -54,6 +54,7 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> +#include <private/qqmlcontextwrapper_p.h> #include <qv4jsir_p.h> #include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" @@ -448,6 +449,9 @@ ReturnedValue ScriptFunction::construct(Managed *that, CallData *callData) callData->thisObject = obj.asReturnedValue(); ExecutionContext *ctx = context->newCallContext(f.getPointer(), callData); + if (f->function->compiledFunction->hasQmlDependencies()) + QmlContextWrapper::registerQmlDependencies(v4, f->function->compiledFunction); + ExecutionContextSaver ctxSaver(context); ScopedValue result(scope, f->function->code(ctx, f->function->codeData)); if (result->isObject()) @@ -474,6 +478,9 @@ ReturnedValue ScriptFunction::call(Managed *that, CallData *callData) } } + if (f->function->compiledFunction->hasQmlDependencies()) + QmlContextWrapper::registerQmlDependencies(ctx->engine, f->function->compiledFunction); + ExecutionContextSaver ctxSaver(context); return f->function->code(ctx, f->function->codeData); } @@ -535,6 +542,9 @@ ReturnedValue SimpleScriptFunction::construct(Managed *that, CallData *callData) callData->thisObject = obj; ExecutionContext *ctx = context->newCallContext(stackSpace, scope.alloc(f->varCount), f.getPointer(), callData); + if (f->function->compiledFunction->hasQmlDependencies()) + QmlContextWrapper::registerQmlDependencies(v4, f->function->compiledFunction); + ExecutionContextSaver ctxSaver(context); Scoped<Object> result(scope, f->function->code(ctx, f->function->codeData)); @@ -564,6 +574,9 @@ ReturnedValue SimpleScriptFunction::call(Managed *that, CallData *callData) } } + if (f->function->compiledFunction->hasQmlDependencies()) + QmlContextWrapper::registerQmlDependencies(v4, f->function->compiledFunction); + ExecutionContextSaver ctxSaver(context); return f->function->code(ctx, f->function->codeData); } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 5678294115..0f23520610 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -51,6 +51,8 @@ #include "qv4function_p.h" #include "private/qlocale_tools_p.h" #include "qv4scopedvalue_p.h" +#include <private/qqmlcontextwrapper_p.h> +#include "qv4qobjectwrapper_p.h" #include <QtCore/qmath.h> #include <QtCore/qnumeric.h> @@ -1175,6 +1177,12 @@ ReturnedValue __qmljs_lookup_runtime_regexp(ExecutionContext *ctx, int id) return ctx->compilationUnit->runtimeRegularExpressions[id].asReturnedValue(); } +ReturnedValue __qmljs_get_id_object(ExecutionContext *ctx, int id) +{ + QQmlContextData *context = QmlContextWrapper::callingContext(ctx->engine); + return QObjectWrapper::wrap(ctx->engine, context->idValues[id].data()); +} + } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 54ade9384b..1ad7c84b9c 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -164,6 +164,8 @@ QV4::ReturnedValue __qmljs_construct_global_lookup(QV4::ExecutionContext *contex QV4::ReturnedValue __qmljs_get_element(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::ValueRef index); void __qmljs_set_element(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::ValueRef index, const QV4::ValueRef value); +QV4::ReturnedValue __qmljs_get_id_object(ExecutionContext *ctx, int id); + // For each QV4::ReturnedValue __qmljs_foreach_iterator_object(QV4::ExecutionContext *ctx, const QV4::ValueRef in); QV4::ReturnedValue __qmljs_foreach_next_property_name(const ValueRef foreach_iterator); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index bd4bd65911..a5a94cd149 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -532,6 +532,10 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, VALUE(instr.result) = context->callData->thisObject; MOTH_END_INSTR(LoadThis) + MOTH_BEGIN_INSTR(LoadIdObject) + VALUE(instr.result) = __qmljs_get_id_object(context, instr.id); + MOTH_END_INSTR(LoadIdObject) + #ifdef MOTH_THREADED_INTERPRETER // nothing to do #else diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index 57114ebead..0462c0b61a 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -3654,7 +3654,21 @@ bool QQmlCompiler::completeComponentBuild() 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); + JSCodeGen::ObjectIdMapping idMapping; + if (compileState->ids.count() > 0) { + idMapping.reserve(compileState->ids.count()); + for (Object *o = compileState->ids.first(); o; o = compileState->ids.next(o)) { + JSCodeGen::IdMapping m; + m.name = o->id; + m.idIndex = o->idIndex; + idMapping << m; + } + } + + const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(unit->finalUrlString(), sourceCode, jsModule.data(), jsEngine, + qmlRoot, compileState->root->astNode, + compileState->functionsToCompile, + idMapping); compileState->runtimeFunctionIndices = runtimeFunctionIndices; for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) { diff --git a/src/qml/qml/qqmlcontextwrapper.cpp b/src/qml/qml/qqmlcontextwrapper.cpp index d3bcf6d3fd..5e798e20ee 100644 --- a/src/qml/qml/qqmlcontextwrapper.cpp +++ b/src/qml/qml/qqmlcontextwrapper.cpp @@ -47,9 +47,10 @@ #include <private/qv4engine_p.h> #include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> #include <private/qv4objectproto_p.h> #include <private/qv4mm_p.h> +#include <private/qv4function_p.h> +#include <private/qv4compileddata_p.h> #include <private/qqmltypewrapper_p.h> #include <private/qqmllistwrapper_p.h> @@ -357,4 +358,27 @@ void QmlContextWrapper::destroy(Managed *that) static_cast<QmlContextWrapper *>(that)->~QmlContextWrapper(); } +void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const CompiledData::Function *compiledFunction) +{ + // Let the caller check and avoid the function call :) + Q_ASSERT(compiledFunction->hasQmlDependencies()); + + QQmlEnginePrivate *ep = engine->v8Engine->engine() ? QQmlEnginePrivate::get(engine->v8Engine->engine()) : 0; + if (!ep) + return; + QQmlEnginePrivate::PropertyCapture *capture = ep->propertyCapture; + if (!capture) + return; + + QV4::Scope scope(engine); + QV4::Scoped<QmlContextWrapper> contextWrapper(scope, engine->qmlContextObject()->getPointer()->as<QmlContextWrapper>()); + QQmlContextData *qmlContext = contextWrapper->getContext(); + + const quint32 *dependency = compiledFunction->qmlIdObjectDependencyTable(); + const int dependencyCount = compiledFunction->nDependingIdObjects; + for (int i = 0; i < dependencyCount; ++i, ++dependency) + capture->captureProperty(&qmlContext->idValues[*dependency].bindings); + +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontextwrapper_p.h b/src/qml/qml/qqmlcontextwrapper_p.h index 86ad4e5616..d85f440b15 100644 --- a/src/qml/qml/qqmlcontextwrapper_p.h +++ b/src/qml/qml/qqmlcontextwrapper_p.h @@ -59,11 +59,16 @@ #include <private/qv4value_p.h> #include <private/qv4object_p.h> #include <private/qqmlcontext_p.h> +#include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE namespace QV4 { +namespace CompiledData { +struct Function; +} + struct Q_QML_EXPORT QmlContextWrapper : Object { Q_MANAGED @@ -86,6 +91,8 @@ struct Q_QML_EXPORT QmlContextWrapper : Object static void put(Managed *m, const StringRef name, const ValueRef value); static void destroy(Managed *that); + static void registerQmlDependencies(ExecutionEngine *context, const CompiledData::Function *compiledFunction); + QV8Engine *v8; // ### temporary, remove bool readOnly; diff --git a/src/qml/qml/qqmlscript.cpp b/src/qml/qml/qqmlscript.cpp index 9fd06aa934..6cb23ec07c 100644 --- a/src/qml/qml/qqmlscript.cpp +++ b/src/qml/qml/qqmlscript.cpp @@ -500,7 +500,7 @@ public: protected: - QQmlScript::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment, + QQmlScript::Object *defineObjectBinding(AST::Node *node, AST::UiQualifiedId *propertyName, bool onAssignment, const QString &objectType, AST::SourceLocation typeLocation, LocationSpan location, @@ -659,7 +659,8 @@ QString ProcessAST::asString(AST::UiQualifiedId *node) const } QQmlScript::Object * -ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName, +ProcessAST::defineObjectBinding(AST::Node *node, + AST::UiQualifiedId *propertyName, bool onAssignment, const QString &objectType, AST::SourceLocation typeLocation, @@ -731,6 +732,7 @@ ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName, obj->type = _parser->findOrCreateTypeId(objectType, obj); obj->typeReference = _parser->_refTypes.at(obj->type); obj->location = location; + obj->astNode = node; if (propertyCount) { Property *prop = currentProperty(); @@ -1130,7 +1132,7 @@ bool ProcessAST::visit(AST::UiObjectDefinition *node) const QString objectType = asString(node->qualifiedTypeNameId); const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken; - defineObjectBinding(/*propertyName = */ 0, false, objectType, + defineObjectBinding(node, /*propertyName = */ 0, false, objectType, typeLocation, l, node->initializer); return false; @@ -1146,7 +1148,7 @@ bool ProcessAST::visit(AST::UiObjectBinding *node) const QString objectType = asString(node->qualifiedTypeNameId); const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken; - defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType, + defineObjectBinding(node, node->qualifiedId, node->hasOnToken, objectType, typeLocation, l, node->initializer); return false; diff --git a/src/qml/qml/qqmlscript_p.h b/src/qml/qml/qqmlscript_p.h index 86bbc1fb3a..b36fdc8861 100644 --- a/src/qml/qml/qqmlscript_p.h +++ b/src/qml/qml/qqmlscript_p.h @@ -335,6 +335,8 @@ public: QQmlPropertyCache *metatype; + QQmlJS::AST::Node *astNode; // responsible for the creation of this object + // The synthesized metaobject, if QML added signals or properties to // this type. Otherwise null QByteArray synthdata; // Generated by compiler |