diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2013-10-24 14:51:02 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-10-31 10:50:38 +0100 |
commit | 34c85bb56c92316a6ce1c79d25f9653fec14791c (patch) | |
tree | 6d3d43de33fa53a1353c52506e989ae126f1361b /src/qml | |
parent | bb7d26ebb0c2e7a9f06a030be8bfcd00e346e06f (diff) |
Initial support for resolving meta-property access for the scope and context objects at QML compile time
This avoids having to do a string lookup for ids and in the import cache at
run-time, before we can do a string hash lookup in the property cache. Instead
we resolve final properties in the context and scope object at compile time and
look them up at run-time using their index instead. The dependencies to these
properties are also tracked separately and recorded in the compiled data.
This is merely the initial patch. There's a lot left to do, such as having
specialized getter and setters for specific property types. Setters are missing
altogether right now and will fall back to name lookup.
Change-Id: If3cb4e7c9454ef4850a615f0935b311c9395b165
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml')
29 files changed, 534 insertions, 125 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index fc485e82f2..7b1984fce0 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -1198,22 +1198,31 @@ int QmlUnitGenerator::getStringId(const QString &str) const return jsUnitGenerator->getStringId(str); } -JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, Engine *jsEngine, AST::UiProgram *qmlRoot) +JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, Engine *jsEngine, AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports) : QQmlJS::Codegen(/*strict mode*/false) , sourceCode(sourceCode) , jsEngine(jsEngine) , qmlRoot(qmlRoot) + , imports(imports) { _module = jsModule; _module->setFileName(fileName); } -QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(AST::Node *contextRoot, - const QList<AST::Node*> &functions, - const ObjectIdMapping &objectIds) +void JSCodeGen::beginContextScope(const JSCodeGen::ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject) { - this->idObjects = objectIds; + _idObjects = objectIds; + _contextObject = contextObject; + _scopeObject = 0; +} +void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject) +{ + _scopeObject = scopeObject; +} + +QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<AST::Node*> &functions) +{ QVector<int> runtimeFunctionIndices(functions.size()); ScanFunctions scan(this, sourceCode, GlobalCode); @@ -1274,18 +1283,77 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(AST::Node *context return runtimeFunctionIndices; } -V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col) const +static QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup) +{ + *propertyExistsButForceNameLookup = false; + QQmlPropertyData *pd = cache->property(name, /*object*/0, /*context*/0); + + // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time + if (pd && pd->isFunction()) { + *propertyExistsButForceNameLookup = true; + pd = 0; + } + + if (pd && !cache->isAllowedInRevision(pd)) + pd = 0; + + return pd; +} + +V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col) { V4IR::Expr *result = 0; // Implement QML lookup semantics in the current file context. + // + // Note: We do not check if properties of the qml scope object or context object + // are final. That's because QML tries to get as close as possible to lexical scoping, + // which means in terms of properties that only those visible at compile time are chosen. + // I.e. access to a "foo" property declared within the same QML component as "property int foo" + // will always access that instance and as integer. If a sub-type implements its own property string foo, + // then that one is not chosen for accesses from within this file, because it wasn't visible at compile + // time. This corresponds to the logic in QQmlPropertyCache::findProperty to find the property associated + // with the correct QML context. // Look for IDs first. - foreach (const IdMapping &mapping, idObjects) + foreach (const IdMapping &mapping, _idObjects) if (name == mapping.name) { - result = _block->QML_CONTEXT_ID_MEMBER(mapping.name, mapping.idIndex, line, col); + result = _block->QML_CONTEXT_ID_MEMBER(_block->NAME(V4IR::Name::builtin_qml_id_scope, line, col), + _function->newString(mapping.name), mapping.idIndex); break; } + if (!result) { + QQmlTypeNameCache::Result r = imports->query(name); + if (r.isValid()) + return 0; // TODO: We can't do fast lookup for these yet. + } + + if (!result && _scopeObject) { + bool propertyExistsButForceNameLookup = false; + QQmlPropertyData *pd = lookupQmlCompliantProperty(_scopeObject, name, &propertyExistsButForceNameLookup); + if (propertyExistsButForceNameLookup) + return 0; + if (pd) { + int base = _block->newTemp(); + move(_block->TEMP(base), _block->NAME(V4IR::Name::builtin_qml_scope_object, line, col)); + result = _block->QML_QOBJECT_PROPERTY(_block->TEMP(base), + _function->newString(name), pd); + } + } + + if (!result && _contextObject) { + bool propertyExistsButForceNameLookup = false; + QQmlPropertyData *pd = lookupQmlCompliantProperty(_contextObject, name, &propertyExistsButForceNameLookup); + if (propertyExistsButForceNameLookup) + return 0; + if (pd) { + int base = _block->newTemp(); + move(_block->TEMP(base), _block->NAME(V4IR::Name::builtin_qml_context_object, line, col)); + result = _block->QML_QOBJECT_PROPERTY(_block->TEMP(base), + _function->newString(name), pd); + } + } + if (result) { _function->hasQmlDependencies = true; return result; diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h index 1b72e96097..43bc979205 100644 --- a/src/qml/compiler/qqmlcodegenerator_p.h +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -55,6 +55,8 @@ QT_BEGIN_NAMESPACE +class QQmlTypeNameCache; + namespace QtQml { using namespace QQmlJS; @@ -346,7 +348,7 @@ private: struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen { JSCodeGen(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, - QQmlJS::Engine *jsEngine, AST::UiProgram *qmlRoot); + QQmlJS::Engine *jsEngine, AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports); struct IdMapping { @@ -355,18 +357,24 @@ struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen }; typedef QVector<IdMapping> ObjectIdMapping; + void beginContextScope(const ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject); + void beginObjectScope(QQmlPropertyCache *scopeObject); + // Returns mapping from input functions to index in V4IR::Module::functions / compiledData->runtimeFunctions - QVector<int> generateJSCodeForFunctionsAndBindings(AST::Node *contextRoot, const QList<AST::Node*> &functions, const ObjectIdMapping &objectIds = ObjectIdMapping()); + QVector<int> generateJSCodeForFunctionsAndBindings(const QList<AST::Node*> &functions); protected: - virtual V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col) const; + virtual V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col); private: QString sourceCode; QQmlJS::Engine *jsEngine; // needed for memory pool AST::UiProgram *qmlRoot; + QQmlTypeNameCache *imports; - ObjectIdMapping idObjects; + ObjectIdMapping _idObjects; + QQmlPropertyCache *_contextObject; + QQmlPropertyCache *_scopeObject; }; } // namespace QtQml diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index cb15c2c885..0be8791d73 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1424,7 +1424,7 @@ V4IR::Expr *Codegen::identifier(const QString &name, int line, int col) Environment *e = _env; V4IR::Function *f = _function; - while (f && e->parent && e->compilationMode != QmlBinding) { + while (f && e->parent) { if (f->insideWithOrCatch || (f->isNamedExpression && f->name == name)) return _block->NAME(name, line, col); @@ -1460,7 +1460,7 @@ V4IR::Expr *Codegen::identifier(const QString &name, int line, int col) } -V4IR::Expr *Codegen::fallbackNameLookup(const QString &name, int line, int col) const +V4IR::Expr *Codegen::fallbackNameLookup(const QString &name, int line, int col) { Q_UNUSED(name) Q_UNUSED(line) @@ -1955,7 +1955,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _env->enter("arguments", Environment::VariableDeclaration); // variables in global code are properties of the global context object, not locals as with other functions. - if (_env->compilationMode == FunctionCode) { + if (_env->compilationMode == FunctionCode || _env->compilationMode == QmlBinding) { unsigned t = 0; for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) { const QString &local = it.key(); @@ -2012,7 +2012,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body ? member.function->body->elements : 0); - if (! _env->parent || _env->compilationMode == QmlBinding) { + if (! _env->parent) { move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), _block->CLOSURE(function)); } else { diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 369df712c5..f5cdd27efa 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -72,7 +72,10 @@ public: GlobalCode, EvalCode, FunctionCode, - QmlBinding + QmlBinding // This is almost the same as EvalCode, except: + // * function declarations are moved to the return address when encountered + // * return statements are allowed everywhere (like in FunctionCode) + // * variable declarations are treated as true locals (like in FunctionCode) }; void generateFromProgram(const QString &fileName, @@ -324,7 +327,7 @@ protected: 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; + virtual V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col); // nodes virtual bool visit(AST::ArgumentList *ast); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 60a697e53e..1c0d72e521 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -235,7 +235,11 @@ struct Function // Qml Extensions Begin quint32 nDependingIdObjects; - quint32 dependingIdObjectsOffset; + quint32 dependingIdObjectsOffset; // Array of resolved ID objects + quint32 nDependingContextProperties; + quint32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index) + quint32 nDependingScopeProperties; + quint32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index) // Qml Extensions End // quint32 formalsIndex[nFormals] @@ -247,11 +251,13 @@ struct Function 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); } + const quint32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); } + const quint32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); } - inline bool hasQmlDependencies() const { return nDependingIdObjects; } + inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } - 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; + static int calculateSize(int nFormals, int nLocals, int nInnerfunctions, int lineNumberMappings, int nIdObjectDependencies, int nPropertyDependencies) { + return (sizeof(Function) + (nFormals + nLocals + nInnerfunctions + 2 * lineNumberMappings + nIdObjectDependencies + 2 * nPropertyDependencies) * sizeof(quint32) + 7) & ~0x7; } }; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 0b3e85352e..4ee34d8aec 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -43,6 +43,7 @@ #include <qv4compileddata_p.h> #include <qv4isel_p.h> #include <qv4engine_p.h> +#include <private/qqmlpropertycache_p.h> QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QQmlJS::V4IR::Module *module, int headerSize) : irModule(module) @@ -172,9 +173,19 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(int *total if (f->hasQmlDependencies) { QQmlJS::V4IR::QmlDependenciesCollector depCollector; - QSet<int> idObjectDeps = depCollector.run(f); + + QSet<int> idObjectDeps; + QSet<QQmlPropertyData*> contextPropertyDeps; + QSet<QQmlPropertyData*> scopePropertyDeps; + + depCollector.run(f, &idObjectDeps, &contextPropertyDeps, &scopePropertyDeps); + if (!idObjectDeps.isEmpty()) qmlIdObjectDependenciesPerFunction.insert(f, idObjectDeps); + if (!contextPropertyDeps.isEmpty()) + qmlContextPropertyDependenciesPerFunction.insert(f, contextPropertyDeps); + if (!scopePropertyDeps.isEmpty()) + qmlScopePropertyDependenciesPerFunction.insert(f, scopePropertyDeps); } } @@ -192,9 +203,24 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(int *total if (lineNumberMapping != lineNumberMappingsPerFunction.constEnd()) lineNumberMappingCount = lineNumberMapping->count() / 2; - const int qmlIdDepsCount = f->hasQmlDependencies ? qmlIdObjectDependenciesPerFunction.value(f).count() : 0; + int qmlIdDepsCount = 0; + int qmlPropertyDepsCount = 0; + + if (f->hasQmlDependencies) { + IdDependencyHash::ConstIterator idIt = qmlIdObjectDependenciesPerFunction.find(f); + if (idIt != qmlIdObjectDependenciesPerFunction.constEnd()) + qmlIdDepsCount += idIt->count(); + + PropertyDependencyHash::ConstIterator it = qmlContextPropertyDependenciesPerFunction.find(f); + if (it != qmlContextPropertyDependenciesPerFunction.constEnd()) + qmlPropertyDepsCount += it->count(); - functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), lineNumberMappingCount, qmlIdDepsCount); + it = qmlScopePropertyDependenciesPerFunction.find(f); + if (it != qmlScopePropertyDependenciesPerFunction.constEnd()) + qmlPropertyDepsCount += it->count(); + } + + functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), lineNumberMappingCount, qmlIdDepsCount, qmlPropertyDepsCount); } const int totalSize = unitSize + functionDataSize + stringDataSize + jsClassDataSize; @@ -292,7 +318,7 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QQmlJS::V4 { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; - QHash<QQmlJS::V4IR::Function *, QVector<uint> >::ConstIterator lineNumberMapping = lineNumberMappingsPerFunction.find(irFunction); + quint32 currentOffset = sizeof(QV4::CompiledData::Function); function->index = index; function->nameIndex = getStringId(*irFunction->name); @@ -306,25 +332,55 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QQmlJS::V4 if (irFunction->isNamedExpression) function->flags |= CompiledData::Function::IsNamedExpression; function->nFormals = irFunction->formals.size(); - function->formalsOffset = sizeof(QV4::CompiledData::Function); + function->formalsOffset = currentOffset; + currentOffset += function->nFormals * sizeof(quint32); + function->nLocals = irFunction->locals.size(); - function->localsOffset = function->formalsOffset + function->nFormals * sizeof(quint32); + function->localsOffset = currentOffset; + currentOffset += function->nLocals * sizeof(quint32); function->nLineNumberMappingEntries = 0; + QHash<QQmlJS::V4IR::Function *, QVector<uint> >::ConstIterator lineNumberMapping = lineNumberMappingsPerFunction.find(irFunction); if (lineNumberMapping != lineNumberMappingsPerFunction.constEnd()) { function->nLineNumberMappingEntries = lineNumberMapping->count() / 2; } - function->lineNumberMappingOffset = function->localsOffset + function->nLocals * sizeof(quint32); + function->lineNumberMappingOffset = currentOffset; + currentOffset += function->nLineNumberMappingEntries * 2 * sizeof(quint32); function->nInnerFunctions = irFunction->nestedFunctions.size(); - function->innerFunctionsOffset = function->lineNumberMappingOffset + function->nLineNumberMappingEntries * 2 * sizeof(quint32); + function->innerFunctionsOffset = currentOffset; + currentOffset += function->nInnerFunctions * sizeof(quint32); function->nDependingIdObjects = 0; + function->nDependingContextProperties = 0; + function->nDependingScopeProperties = 0; + QSet<int> qmlIdObjectDeps; + QSet<QQmlPropertyData*> qmlContextPropertyDeps; + QSet<QQmlPropertyData*> qmlScopePropertyDeps; + if (irFunction->hasQmlDependencies) { qmlIdObjectDeps = qmlIdObjectDependenciesPerFunction.value(irFunction); - function->nDependingIdObjects = qmlIdObjectDeps.count(); - function->dependingIdObjectsOffset = function->innerFunctionsOffset + function->nInnerFunctions * sizeof(quint32); + qmlContextPropertyDeps = qmlContextPropertyDependenciesPerFunction.value(irFunction); + qmlScopePropertyDeps = qmlScopePropertyDependenciesPerFunction.value(irFunction); + + if (!qmlIdObjectDeps.isEmpty()) { + function->nDependingIdObjects = qmlIdObjectDeps.count(); + function->dependingIdObjectsOffset = currentOffset; + currentOffset += function->nDependingIdObjects * sizeof(quint32); + } + + if (!qmlContextPropertyDeps.isEmpty()) { + function->nDependingContextProperties = qmlContextPropertyDeps.count(); + function->dependingContextPropertiesOffset = currentOffset; + currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2; + } + + if (!qmlScopePropertyDeps.isEmpty()) { + function->nDependingScopeProperties = qmlScopePropertyDeps.count(); + function->dependingScopePropertiesOffset = currentOffset; + currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2; + } } function->location.line = irFunction->line; @@ -352,11 +408,24 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QQmlJS::V4 innerFunctions[i] = functionOffsets.value(irFunction->nestedFunctions.at(i)); // write QML dependencies - quint32 *writtenIdDeps = (quint32 *)(f + function->dependingIdObjectsOffset); + quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset); foreach (int id, qmlIdObjectDeps) - *writtenIdDeps++ = id; + *writtenDeps++ = id; + + writtenDeps = (quint32 *)(f + function->dependingContextPropertiesOffset); + foreach (QQmlPropertyData *property, qmlContextPropertyDeps) { + *writtenDeps++ = property->coreIndex; + *writtenDeps++ = property->notifyIndex; + } + + writtenDeps = (quint32 *)(f + function->dependingScopePropertiesOffset); + foreach (QQmlPropertyData *property, qmlScopePropertyDeps) { + *writtenDeps++ = property->coreIndex; + *writtenDeps++ = property->notifyIndex; + } - return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions, function->nLineNumberMappingEntries, function->nDependingIdObjects); + return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions, function->nLineNumberMappingEntries, + function->nDependingIdObjects, function->nDependingContextProperties + function->nDependingScopeProperties); } diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 40b3fe25c0..6338babb5a 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -46,6 +46,8 @@ QT_BEGIN_NAMESPACE +class QQmlPropertyData; + namespace QV4 { namespace CompiledData { @@ -92,7 +94,13 @@ struct Q_QML_EXPORT JSUnitGenerator { QList<QList<CompiledData::JSClassMember> > jsClasses; uint jsClassDataSize; uint headerSize; - QHash<QQmlJS::V4IR::Function *, QSet<int> > qmlIdObjectDependenciesPerFunction; + + typedef QHash<QQmlJS::V4IR::Function *, QSet<int> > IdDependencyHash; + IdDependencyHash qmlIdObjectDependenciesPerFunction; + + typedef QHash<QQmlJS::V4IR::Function *, QSet<QQmlPropertyData*> > PropertyDependencyHash; + PropertyDependencyHash qmlContextPropertyDependenciesPerFunction; + PropertyDependencyHash qmlScopePropertyDependenciesPerFunction; }; } diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 6734d93ae0..d1619962f5 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -65,6 +65,7 @@ QT_BEGIN_NAMESPACE F(GetLookup, getLookup) \ F(StoreProperty, storeProperty) \ F(SetLookup, setLookup) \ + F(LoadQObjectProperty, loadQObjectProperty) \ F(Push, push) \ F(CallValue, callValue) \ F(CallProperty, callProperty) \ @@ -122,7 +123,9 @@ QT_BEGIN_NAMESPACE F(MulNumberParams, mulNumberParams) \ F(SubNumberParams, subNumberParams) \ F(LoadThis, loadThis) \ - F(LoadIdObject, loadIdObject) + F(LoadQmlIdObject, loadQmlIdObject) \ + F(LoadQmlContextObject, loadQmlContextObject) \ + F(LoadQmlScopeObject, loadQmlScopeObject) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -275,6 +278,12 @@ union Instr Param base; Param result; }; + struct instr_loadQObjectProperty { + MOTH_INSTR_HEADER + int propertyIndex; + Param base; + Param result; + }; struct instr_storeProperty { MOTH_INSTR_HEADER int name; @@ -623,11 +632,19 @@ union Instr MOTH_INSTR_HEADER Param result; }; - struct instr_loadIdObject { + struct instr_loadQmlIdObject { MOTH_INSTR_HEADER Param result; int id; }; + struct instr_loadQmlContextObject { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_loadQmlScopeObject { + MOTH_INSTR_HEADER + Param result; + }; instr_common common; instr_ret ret; @@ -643,6 +660,7 @@ union Instr instr_storeElement storeElement; instr_loadProperty loadProperty; instr_getLookup getLookup; + instr_loadQObjectProperty loadQObjectProperty; instr_storeProperty storeProperty; instr_setLookup setLookup; instr_push push; @@ -702,7 +720,9 @@ union Instr instr_mulNumberParams mulNumberParams; instr_subNumberParams subNumberParams; instr_loadThis loadThis; - instr_loadIdObject loadIdObject; + instr_loadQmlIdObject loadQmlIdObject; + instr_loadQmlContextObject loadQmlContextObject; + instr_loadQmlScopeObject loadQmlScopeObject; static int size(Type type); }; diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp index 1f00af3972..a012273c12 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -953,6 +953,16 @@ void InstructionSelection::loadIdObject(int id, V4IR::Temp *temp) generateFunctionCall(temp, __qmljs_get_id_object, Assembler::ContextRegister, Assembler::TrustedImm32(id)); } +void InstructionSelection::loadQmlContextObject(V4IR::Temp *temp) +{ + generateFunctionCall(temp, __qmljs_get_context_object, Assembler::ContextRegister); +} + +void InstructionSelection::loadQmlScopeObject(V4IR::Temp *temp) +{ + generateFunctionCall(temp, __qmljs_get_scope_object, Assembler::ContextRegister); +} + void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { if (targetTemp->kind == V4IR::Temp::PhysicalRegister) { @@ -1031,6 +1041,11 @@ void InstructionSelection::getProperty(V4IR::Expr *base, const QString &name, V4 } } +void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyIndex, V4IR::Temp *target) +{ + generateFunctionCall(target, __qmljs_get_qobject_property, Assembler::ContextRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex)); +} + void InstructionSelection::setProperty(V4IR::Expr *source, V4IR::Expr *targetBase, const QString &targetName) { diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h index 8178866656..46144f22d1 100644 --- a/src/qml/compiler/qv4isel_masm_p.h +++ b/src/qml/compiler/qv4isel_masm_p.h @@ -1470,6 +1470,8 @@ protected: 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 loadQmlContextObject(V4IR::Temp *temp); + virtual void loadQmlScopeObject(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); @@ -1478,6 +1480,7 @@ protected: virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); virtual void getProperty(V4IR::Expr *base, const QString &name, V4IR::Temp *target); virtual void setProperty(V4IR::Expr *source, V4IR::Expr *targetBase, const QString &targetName); + virtual void getQObjectProperty(V4IR::Expr *base, int propertyIndex, V4IR::Temp *target); virtual void getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target); virtual void setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex); virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index a2e702dac4..ca0977e057 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -456,12 +456,26 @@ void InstructionSelection::loadThisObject(V4IR::Temp *temp) void InstructionSelection::loadIdObject(int id, V4IR::Temp *temp) { - Instruction::LoadIdObject load; + Instruction::LoadQmlIdObject load; load.result = getResultParam(temp); load.id = id; addInstruction(load); } +void InstructionSelection::loadQmlContextObject(V4IR::Temp *temp) +{ + Instruction::LoadQmlContextObject load; + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::loadQmlScopeObject(V4IR::Temp *temp) +{ + Instruction::LoadQmlScopeObject load; + load.result = getResultParam(temp); + addInstruction(load); +} + void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { assert(sourceConst); @@ -555,6 +569,15 @@ void InstructionSelection::setProperty(V4IR::Expr *source, V4IR::Expr *targetBas addInstruction(store); } +void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyIndex, V4IR::Temp *target) +{ + Instruction::LoadQObjectProperty load; + load.base = getParam(base); + load.propertyIndex = propertyIndex; + load.result = getResultParam(target); + addInstruction(load); +} + void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target) { Instruction::LoadElement load; diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index df5c71ce8c..0b44bf35ca 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -115,6 +115,8 @@ protected: 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 loadQmlContextObject(V4IR::Temp *temp); + virtual void loadQmlScopeObject(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); @@ -123,6 +125,7 @@ protected: virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); virtual void getProperty(V4IR::Expr *base, const QString &name, V4IR::Temp *target); virtual void setProperty(V4IR::Expr *source, V4IR::Expr *targetBase, const QString &targetName); + virtual void getQObjectProperty(V4IR::Expr *base, int propertyIndex, V4IR::Temp *target); virtual void getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target); virtual void setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex); virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index b9341163de..1a56ddabf1 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -46,6 +46,7 @@ #include "qv4isel_util_p.h" #include "qv4functionobject_p.h" #include "qv4function_p.h" +#include <private/qqmlpropertycache_p.h> #include <QString> @@ -100,8 +101,12 @@ void IRDecoder::visitMove(V4IR::Move *s) } } else if (V4IR::Temp *t = s->target->asTemp()) { if (V4IR::Name *n = s->source->asName()) { - if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. + if (n->id && *n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. loadThisObject(t); + else if (n->builtin == V4IR::Name::builtin_qml_context_object) + loadQmlContextObject(t); + else if (n->builtin == V4IR::Name::builtin_qml_scope_object) + loadQmlScopeObject(t); else getActivationProperty(n, t); return; @@ -138,6 +143,9 @@ void IRDecoder::visitMove(V4IR::Move *s) if (m->type == V4IR::Member::MemberByObjectId) { loadIdObject(m->objectId, t); return; + } else if (m->type == V4IR::Member::MemberOfQObject) { + getQObjectProperty(m->base, m->property->coreIndex, 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 6e607d901c..5b9dbafb50 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -141,6 +141,8 @@ public: // to implement by subclasses: 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 loadQmlContextObject(V4IR::Temp *temp) = 0; + virtual void loadQmlScopeObject(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; @@ -148,6 +150,7 @@ public: // to implement by subclasses: virtual void setActivationProperty(V4IR::Expr *source, const QString &targetName) = 0; virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target) = 0; virtual void getProperty(V4IR::Expr *base, const QString &name, V4IR::Temp *target) = 0; + virtual void getQObjectProperty(V4IR::Expr *base, int propertyIndex, V4IR::Temp *targetTemp) = 0; virtual void setProperty(V4IR::Expr *source, V4IR::Expr *targetBase, const QString &targetName) = 0; virtual void getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target) = 0; virtual void setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex) = 0; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 86a13cfe99..a3654a1bea 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -42,6 +42,7 @@ #include "qv4jsir_p.h" #include <private/qqmljsast_p.h> +#include <private/qqmlpropertycache_p.h> #include <QtCore/qtextstream.h> #include <QtCore/qdebug.h> #include <QtCore/qset.h> @@ -423,6 +424,10 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_setup_argument_object"; case V4IR::Name::builtin_qml_id_scope: return "builtin_qml_id_scope"; + case V4IR::Name::builtin_qml_scope_object: + return "builtin_qml_scope_object"; + case V4IR::Name::builtin_qml_context_object: + return "builtin_qml_context_object"; } return "builtin_(###FIXME)"; }; @@ -532,6 +537,8 @@ void Member::dump(QTextStream &out) const { base->dump(out); out << '.' << *name; + if (type == MemberOfQObject) + out << " (meta-property " << property->coreIndex << " <" << QMetaType::typeName(property->propType) << ">)"; } void Exp::dump(QTextStream &out, Mode) @@ -819,11 +826,18 @@ 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) +Expr *BasicBlock::QML_CONTEXT_ID_MEMBER(Expr *base, const QString *id, int objectId) { Member*e = function->New<Member>(); - Name *base = NAME(Name::builtin_qml_id_scope, line, column); - e->initQmlIdObject(base, function->newString(id), objectId); + Q_ASSERT(base->asName() && base->asName()->builtin == Name::builtin_qml_id_scope); + e->initQmlIdObject(base, id, objectId); + return e; +} + +Expr *BasicBlock::QML_QOBJECT_PROPERTY(Expr *base, const QString *id, QQmlPropertyData *property) +{ + Member*e = function->New<Member>(); + e->initMetaProperty(base, id, property); return e; } @@ -1013,12 +1027,30 @@ void CloneExpr::visitSubscript(Subscript *e) void CloneExpr::visitMember(Member *e) { - 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; + if (e->type == Member::MemberByName) + cloned = block->MEMBER(clone(e->base), e->name); + else if (e->type == Member::MemberByObjectId) + cloned = block->QML_CONTEXT_ID_MEMBER(clone(e->base), e->name, e->objectId); + else if (e->type == Member::MemberOfQObject) + cloned = block->QML_QOBJECT_PROPERTY(clone(e->base), e->name, e->property); + else + Q_ASSERT(!"Unimplemented!"); +} + +void QmlDependenciesCollector::visitMember(Member *e) { + e->base->accept(this); + if (e->type == Member::MemberByObjectId) + _usedIdObjects.insert(e->objectId); + else if (e->type == Member::MemberOfQObject + && !e->property->isFunction()) { // only non-functions have notifyIndex + + if (Name *base = e->base->asName()) { + if (base->builtin == Name::builtin_qml_context_object) + _usedContextProperties.insert(e->property); + else if (base->builtin == Name::builtin_qml_scope_object) + _usedScopeProperties.insert(e->property); + } } - cloned = m; } void QmlDependenciesCollector::visitPhi(Phi *s) { diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 1f69ac4964..7e7a972c54 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -72,6 +72,7 @@ QT_BEGIN_NAMESPACE class QTextStream; class QQmlType; +class QQmlPropertyData; namespace QV4 { struct ExecutionContext; @@ -323,7 +324,9 @@ struct Name: Expr { builtin_define_getter_setter, builtin_define_object_literal, builtin_setup_argument_object, - builtin_qml_id_scope + builtin_qml_id_scope, + builtin_qml_context_object, + builtin_qml_scope_object }; const QString *id; @@ -516,13 +519,15 @@ struct Member: Expr { enum MemberType { MemberByName, // QML extensions - MemberByObjectId // lookup in context's id values + MemberByObjectId, // lookup in context's id values + MemberOfQObject }; MemberType type; Expr *base; const QString *name; int objectId; + QQmlPropertyData *property; void init(Expr *base, const QString *name) { @@ -530,6 +535,7 @@ struct Member: Expr { this->base = base; this->name = name; this->objectId = -1; + this->property = 0; } void initQmlIdObject(Expr *base, const QString *name, int objectId) @@ -538,6 +544,15 @@ struct Member: Expr { this->base = base; this->name = name; this->objectId = objectId; + this->property = 0; + } + + void initMetaProperty(Expr *base, const QString *name, QQmlPropertyData *property) + { + this->type = MemberOfQObject; + this->base = base; + this->name = name; + this->property = property; } virtual void accept(ExprVisitor *v) { v->visitMember(this); } @@ -831,7 +846,8 @@ 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); + Expr *QML_CONTEXT_ID_MEMBER(Expr *base, const QString *id, int idIndex); + Expr *QML_QOBJECT_PROPERTY(Expr *base, const QString *id, QQmlPropertyData *property); Stmt *EXP(Expr *expr); @@ -933,10 +949,14 @@ private: struct QmlDependenciesCollector : public V4IR::StmtVisitor, V4IR::ExprVisitor { - QSet<int> run(Function *function) + void run(Function *function, QSet<int> *idObjectDependencies, QSet<QQmlPropertyData*> *contextPropertyDependencies, QSet<QQmlPropertyData*> *scopePropertyDependencies) { - QSet<int> dependencies; - qSwap(_usedIdObjects, dependencies); + QSet<int> idProperties; + QSet<QQmlPropertyData*> contextProperties; + QSet<QQmlPropertyData*> scopeProperties; + qSwap(_usedIdObjects, idProperties); + qSwap(_usedContextProperties, contextProperties); + qSwap(_usedScopeProperties, scopeProperties); for (int i = 0; i < function->basicBlocks.count(); ++i) { BasicBlock *bb = function->basicBlocks.at(i); for (int j = 0; j < bb->statements.count(); ++j) { @@ -944,12 +964,19 @@ struct QmlDependenciesCollector : public V4IR::StmtVisitor, V4IR::ExprVisitor s->accept(this); } } - qSwap(_usedIdObjects, dependencies); - return dependencies; + qSwap(_usedScopeProperties, scopeProperties); + qSwap(_usedContextProperties, contextProperties); + qSwap(_usedIdObjects, idProperties); + + *idObjectDependencies = idProperties; + *contextPropertyDependencies = contextProperties; + *scopePropertyDependencies = scopeProperties; } protected: QSet<int> _usedIdObjects; + QSet<QQmlPropertyData*> _usedContextProperties; + QSet<QQmlPropertyData*> _usedScopeProperties; virtual void visitConst(Const *) {} virtual void visitString(String *) {} @@ -976,11 +1003,7 @@ protected: e->index->accept(this); } - virtual void visitMember(Member *e) { - e->base->accept(this); - if (e->type == Member::MemberByObjectId) - _usedIdObjects.insert(e->objectId); - } + virtual void visitMember(Member *e); virtual void visitExp(Exp *s) {s->expr->accept(this);} virtual void visitMove(Move *s) { diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp index e2d627b809..84efdbb920 100644 --- a/src/qml/compiler/qv4regalloc.cpp +++ b/src/qml/compiler/qv4regalloc.cpp @@ -340,6 +340,18 @@ protected: // IRDecoder addCall(); } + virtual void loadQmlContextObject(Temp *temp) + { + addDef(temp); + addCall(); + } + + virtual void loadQmlScopeObject(Temp *temp) + { + addDef(temp); + addCall(); + } + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { addDef(targetTemp); @@ -388,6 +400,13 @@ protected: // IRDecoder addCall(); } + virtual void getQObjectProperty(V4IR::Expr *base, int /*propertyIndex*/, V4IR::Temp *target) + { + addDef(target); + addUses(base->asTemp(), Use::CouldHaveRegister); + addCall(); + } + virtual void getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target) { addDef(target); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index a4bfc93c36..22e2019112 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -329,16 +329,23 @@ ReturnedValue QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextD if (hasProperty) *hasProperty = true; - if (result->isFunction() && !result->isVarProperty()) { - if (result->isVMEFunction()) { + return getProperty(ctx, result); +} + +ReturnedValue QObjectWrapper::getProperty(ExecutionContext *ctx, QQmlPropertyData *property) +{ + QV4::Scope scope(ctx); + + if (property->isFunction() && !property->isVarProperty()) { + if (property->isVMEFunction()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); Q_ASSERT(vmemo); - return vmemo->vmeMethod(result->coreIndex); - } else if (result->isV4Function()) { + return vmemo->vmeMethod(property->coreIndex); + } else if (property->isV4Function()) { QV4::Scoped<QV4::Object> qmlcontextobject(scope, ctx->engine->qmlContextObject()); - return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex, qmlcontextobject); - } else if (result->isSignalHandler()) { - QV4::Scoped<QV4::QmlSignalHandler> handler(scope, new (ctx->engine->memoryManager) QV4::QmlSignalHandler(ctx->engine, m_object, result->coreIndex)); + return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, property->coreIndex, qmlcontextobject); + } else if (property->isSignalHandler()) { + QV4::Scoped<QV4::QmlSignalHandler> handler(scope, new (ctx->engine->memoryManager) QV4::QmlSignalHandler(ctx->engine, m_object, property->coreIndex)); QV4::ScopedString connect(scope, ctx->engine->newIdentifier(QStringLiteral("connect"))); QV4::ScopedString disconnect(scope, ctx->engine->newIdentifier(QStringLiteral("disconnect"))); @@ -347,45 +354,46 @@ ReturnedValue QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextD return handler.asReturnedValue(); } else { - return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex); + return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, property->coreIndex); } } QQmlEnginePrivate *ep = ctx->engine->v8Engine->engine() ? QQmlEnginePrivate::get(ctx->engine->v8Engine->engine()) : 0; - if (result->hasAccessors()) { + if (property->hasAccessors()) { QQmlNotifier *n = 0; QQmlNotifier **nptr = 0; - if (ep && ep->propertyCapture && result->accessors->notifier) + if (ep && ep->propertyCapture && property->accessors->notifier) nptr = &n; - QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(ctx->engine->v8Engine, m_object, *result, nptr)); + QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(ctx->engine->v8Engine, m_object, *property, nptr)); - if (result->accessors->notifier) { + if (property->accessors->notifier) { if (n) ep->captureProperty(n); } else { - ep->captureProperty(m_object, result->coreIndex, result->notifyIndex); + ep->captureProperty(m_object, property->coreIndex, property->notifyIndex); } return rv.asReturnedValue(); } - if (ep && !result->isConstant()) - ep->captureProperty(m_object, result->coreIndex, result->notifyIndex); + if (ep && !property->isConstant()) + ep->captureProperty(m_object, property->coreIndex, property->notifyIndex); - if (result->isVarProperty()) { + if (property->isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); Q_ASSERT(vmemo); - return vmemo->vmeProperty(result->coreIndex); - } else if (result->isDirect()) { - return LoadProperty<ReadAccessor::Direct>(ctx->engine->v8Engine, m_object, *result, 0); + return vmemo->vmeProperty(property->coreIndex); + } else if (property->isDirect()) { + return LoadProperty<ReadAccessor::Direct>(ctx->engine->v8Engine, m_object, *property, 0); } else { - return LoadProperty<ReadAccessor::Indirect>(ctx->engine->v8Engine, m_object, *result, 0); + return LoadProperty<ReadAccessor::Indirect>(ctx->engine->v8Engine, m_object, *property, 0); } } + ReturnedValue QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty) { QV4::Scope scope(ctx); @@ -603,6 +611,21 @@ ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) } } +ReturnedValue QObjectWrapper::getProperty(ExecutionContext *ctx, int propertyIndex) +{ + if (QQmlData::wasDeleted(m_object)) + return QV4::Encode::null(); + QQmlData *ddata = QQmlData::get(m_object, /*create*/false); + if (!ddata) + return QV4::Encode::undefined(); + + QQmlPropertyCache *cache = ddata->propertyCache; + Q_ASSERT(cache); + QQmlPropertyData *property = cache->property(propertyIndex); + Q_ASSERT(property); // We resolved this property earlier, so it better exist! + return getProperty(ctx, property); +} + ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QQmlData *ddata, QObject *object) { QQmlEngine *qmlEngine = engine->v8Engine->engine(); diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index eadbacc096..6f886f0522 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -94,7 +94,11 @@ struct Q_QML_EXPORT QObjectWrapper : public QV4::Object using Object::get; + ReturnedValue getProperty(ExecutionContext *ctx, int propertyIndex); + private: + ReturnedValue getProperty(ExecutionContext *ctx, QQmlPropertyData *property); + static ReturnedValue create(ExecutionEngine *engine, QQmlData *ddata, QObject *object); QObjectWrapper(ExecutionEngine *engine, QObject *object); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 0f23520610..aa8ab1c172 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1183,6 +1183,30 @@ ReturnedValue __qmljs_get_id_object(ExecutionContext *ctx, int id) return QObjectWrapper::wrap(ctx->engine, context->idValues[id].data()); } +ReturnedValue __qmljs_get_context_object(ExecutionContext *ctx) +{ + QQmlContextData *context = QmlContextWrapper::callingContext(ctx->engine); + return QObjectWrapper::wrap(ctx->engine, context->contextObject); +} + +ReturnedValue __qmljs_get_scope_object(ExecutionContext *ctx) +{ + Scope scope(ctx); + QV4::Scoped<QmlContextWrapper> c(scope, ctx->engine->qmlContextObject()->getPointer()->as<QmlContextWrapper>()); + return QObjectWrapper::wrap(ctx->engine, c->getScopeObject()); +} + +ReturnedValue __qmljs_get_qobject_property(ExecutionContext *ctx, const ValueRef object, int propertyIndex) +{ + Scope scope(ctx); + QV4::Scoped<QObjectWrapper> wrapper(scope, object); + if (!wrapper) { + ctx->throwTypeError(QStringLiteral("Cannot read property of null")); + return Encode::undefined(); + } + return wrapper->getProperty(ctx, propertyIndex); +} + } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index e04baadf53..464595a380 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -165,6 +165,9 @@ QV4::ReturnedValue __qmljs_get_element(QV4::ExecutionContext *ctx, const QV4::Va 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); +QV4::ReturnedValue __qmljs_get_context_object(ExecutionContext *ctx); +QV4::ReturnedValue __qmljs_get_scope_object(ExecutionContext *ctx); +QV4::ReturnedValue __qmljs_get_qobject_property(ExecutionContext *ctx, const ValueRef object, int propertyIndex); // For each QV4::ReturnedValue __qmljs_foreach_iterator_object(QV4::ExecutionContext *ctx, const QV4::ValueRef in); diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 6e2c26edaf..f1903cf323 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -207,8 +207,7 @@ void Script::parse() inheritedLocals.append(*i ? (*i)->toQString() : QString()); RuntimeCodegen cg(scope, strictMode); - cg.generateFromProgram(sourceFile, sourceCode, program, &module, - parseAsBinding ? QQmlJS::Codegen::QmlBinding : QQmlJS::Codegen::EvalCode, inheritedLocals); + cg.generateFromProgram(sourceFile, sourceCode, program, &module, QQmlJS::Codegen::EvalCode, inheritedLocals); if (v4->hasException) return; @@ -286,7 +285,7 @@ Function *Script::function() return vmFunction; } -CompiledData::CompilationUnit *Script::precompile(ExecutionEngine *engine, const QUrl &url, const QString &source, bool parseAsBinding, QList<QQmlError> *reportedErrors) +CompiledData::CompilationUnit *Script::precompile(ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors) { using namespace QQmlJS; using namespace QQmlJS::AST; @@ -330,7 +329,7 @@ CompiledData::CompilationUnit *Script::precompile(ExecutionEngine *engine, const } QQmlJS::Codegen cg(/*strict mode*/false); - cg.generateFromProgram(url.toString(), source, program, &module, parseAsBinding ? QQmlJS::Codegen::QmlBinding : QQmlJS::Codegen::GlobalCode); + cg.generateFromProgram(url.toString(), source, program, &module, QQmlJS::Codegen::EvalCode); errors = cg.errors(); if (!errors.isEmpty()) { if (reportedErrors) diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 52ad4dd78c..657923062b 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -100,9 +100,7 @@ struct Q_QML_EXPORT Script { Function *function(); - static CompiledData::CompilationUnit *precompile(ExecutionEngine *engine, const QUrl &url, const QString &source, - bool parseAsBinding, - QList<QQmlError> *reportedErrors = 0); + static CompiledData::CompilationUnit *precompile(ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors = 0); static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, ObjectRef scopeObject); }; diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 5a7b33a5d5..1797f0b2ee 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -305,6 +305,10 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, CHECK_EXCEPTION; MOTH_END_INSTR(SetLookup) + MOTH_BEGIN_INSTR(LoadQObjectProperty) + STOREVALUE(instr.result, __qmljs_get_qobject_property(context, VALUEPTR(instr.base), instr.propertyIndex)); + MOTH_END_INSTR(LoadQObjectProperty) + MOTH_BEGIN_INSTR(Push) TRACE(inline, "stack size: %u", instr.value); stackSize = instr.value; @@ -628,9 +632,17 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, VALUE(instr.result) = context->callData->thisObject; MOTH_END_INSTR(LoadThis) - MOTH_BEGIN_INSTR(LoadIdObject) + MOTH_BEGIN_INSTR(LoadQmlIdObject) VALUE(instr.result) = __qmljs_get_id_object(context, instr.id); - MOTH_END_INSTR(LoadIdObject) + MOTH_END_INSTR(LoadQmlIdObject) + + MOTH_BEGIN_INSTR(LoadQmlContextObject) + VALUE(instr.result) = __qmljs_get_context_object(context); + MOTH_END_INSTR(LoadContextObject) + + MOTH_BEGIN_INSTR(LoadQmlScopeObject) + VALUE(instr.result) = __qmljs_get_scope_object(context); + MOTH_END_INSTR(LoadScopeObject) #ifdef MOTH_THREADED_INTERPRETER // nothing to do diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index 58495bb1bf..3f57d7c2ef 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -1336,7 +1336,7 @@ void QQmlCompiler::genObjectBody(QQmlScript::Object *obj) } else if (v->type == Value::SignalExpression) { Instruction::StoreSignal store; - store.runtimeFunctionIndex = compileState->runtimeFunctionIndices.at(v->signalData.functionIndex); + store.runtimeFunctionIndex = compileState->jsCompileData[v->signalData.signalScopeObject].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; @@ -1738,12 +1738,15 @@ bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *o //to ensure all parameters are available (see qqmlboundsignal constructor for more details) prop->index = obj->metatype->originalClone(prop->index); prop->values.first()->signalData.signalExpressionContextStack = ctxt.stack; + prop->values.first()->signalData.signalScopeObject = ctxt.object; QList<QByteArray> parameters = obj->metatype->signalParameterNames(prop->index); AST::FunctionDeclaration *funcDecl = convertSignalHandlerExpressionToFunctionDeclaration(unit->parser().jsEngine(), prop->values.first()->value.asAST(), propName.toString(), parameters); - compileState->functionsToCompile.append(funcDecl); - prop->values.first()->signalData.functionIndex = compileState->functionsToCompile.count() - 1; + + ComponentCompileState::PerObjectCompileData *cd = &compileState->jsCompileData[ctxt.object]; + cd->functionsToCompile.append(funcDecl); + prop->values.first()->signalData.functionIndex = cd->functionsToCompile.count() - 1; QString errorString; obj->metatype->signalParameterStringForJS(prop->index, &errorString); @@ -3250,12 +3253,13 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod vmd->methodCount++; md = methodData; - QQmlCompilerTypes::ComponentCompileState::CompiledMetaMethod cmm; - cmm.obj = obj; + ComponentCompileState::PerObjectCompileData *cd = &compileState->jsCompileData[obj]; + + ComponentCompileState::CompiledMetaMethod cmm; cmm.methodIndex = vmd->methodCount - 1; - compileState->functionsToCompile.append(s->funcDecl); - cmm.compiledFunctionIndex = compileState->functionsToCompile.count() - 1; - compileState->compiledMetaMethods.append(cmm); + cd->functionsToCompile.append(s->funcDecl); + cmm.compiledFunctionIndex = cd->functionsToCompile.count() - 1; + cd->compiledMetaMethods.append(cmm); } if (aliasCount) @@ -3641,18 +3645,19 @@ bool QQmlCompiler::completeComponentBuild() node = new (pool) AST::ExpressionStatement(expr); } - compileState->functionsToCompile.append(node); - binding.compiledIndex = compileState->functionsToCompile.count() - 1; + ComponentCompileState::PerObjectCompileData *cd = &compileState->jsCompileData[b->bindingContext.object]; + cd->functionsToCompile.append(node); + binding.compiledIndex = cd->functionsToCompile.count() - 1; if (componentStats) componentStats->componentStat.scriptBindings.append(b->value->location); } - if (!compileState->functionsToCompile.isEmpty()) { + if (!compileState->jsCompileData.isEmpty()) { const QString &sourceCode = jsEngine->code(); AST::UiProgram *qmlRoot = parser.qmlRoot(); - JSCodeGen jsCodeGen(unit->finalUrlString(), sourceCode, jsModule.data(), jsEngine, qmlRoot); + JSCodeGen jsCodeGen(unit->finalUrlString(), sourceCode, jsModule.data(), jsEngine, qmlRoot, output->importCache); JSCodeGen::ObjectIdMapping idMapping; if (compileState->ids.count() > 0) { @@ -3665,21 +3670,28 @@ bool QQmlCompiler::completeComponentBuild() } } - const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(compileState->root->astNode, - compileState->functionsToCompile, - idMapping); - compileState->runtimeFunctionIndices = runtimeFunctionIndices; + jsCodeGen.beginContextScope(idMapping, compileState->root->metatype); - for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) { - JSBindingReference &binding = *b; - binding.compiledIndex = runtimeFunctionIndices[binding.compiledIndex]; + for (QHash<QQmlScript::Object *, ComponentCompileState::PerObjectCompileData>::Iterator it = compileState->jsCompileData.begin(); + it != compileState->jsCompileData.end(); ++it) { + QQmlScript::Object *scopeObject = it.key(); + ComponentCompileState::PerObjectCompileData *cd = &it.value(); + + jsCodeGen.beginObjectScope(scopeObject->metatype); + + cd->runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(cd->functionsToCompile); + + foreach (const QQmlCompilerTypes::ComponentCompileState::CompiledMetaMethod &cmm, cd->compiledMetaMethods) { + typedef QQmlVMEMetaData VMD; + VMD *vmd = (QQmlVMEMetaData *)scopeObject->synthdata.data(); + VMD::MethodData &md = *(vmd->methodData() + cmm.methodIndex); + md.runtimeFunctionIndex = cd->runtimeFunctionIndices.at(cmm.compiledFunctionIndex); + } } - 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); + for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) { + JSBindingReference &binding = *b; + binding.compiledIndex = compileState->jsCompileData[binding.bindingContext.object].runtimeFunctionIndices[binding.compiledIndex]; } } diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index 142d8c68b1..2e3e6b8f4c 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -302,16 +302,21 @@ 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; + struct PerObjectCompileData + { + QList<QQmlJS::AST::Node*> functionsToCompile; + QVector<int> runtimeFunctionIndices; + QVector<CompiledMetaMethod> compiledMetaMethods; + }; + QHash<QQmlScript::Object *, PerObjectCompileData> jsCompileData; }; }; diff --git a/src/qml/qml/qqmlcontextwrapper.cpp b/src/qml/qml/qqmlcontextwrapper.cpp index 5e798e20ee..832d9421c2 100644 --- a/src/qml/qml/qqmlcontextwrapper.cpp +++ b/src/qml/qml/qqmlcontextwrapper.cpp @@ -374,10 +374,27 @@ void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const C 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); + const quint32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); + const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; + for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) + capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings); + + const quint32 *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable(); + const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties; + for (int i = 0; i < contextPropertyDependencyCount; ++i) { + const int propertyIndex = *contextPropertyDependency++; + const int notifyIndex = *contextPropertyDependency++; + capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex); + } + + QObject *scopeObject = contextWrapper->getScopeObject(); + const quint32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); + const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties; + for (int i = 0; i < scopePropertyDependencyCount; ++i) { + const int propertyIndex = *scopePropertyDependency++; + const int notifyIndex = *scopePropertyDependency++; + capture->captureProperty(scopeObject, propertyIndex, notifyIndex); + } } diff --git a/src/qml/qml/qqmlscript_p.h b/src/qml/qml/qqmlscript_p.h index b36fdc8861..fac31add5c 100644 --- a/src/qml/qml/qqmlscript_p.h +++ b/src/qml/qml/qqmlscript_p.h @@ -229,6 +229,7 @@ public: // Used by compiler struct SignalData { int signalExpressionContextStack; + Object *signalScopeObject; int functionIndex; // before gen() index in functionsToCompile, then index in runtime functions }; union { diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 6f718e5b5b..b3bc5eb4a1 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2357,8 +2357,8 @@ void QQmlTypeData::compile() // Compile JS binding expressions and signal handlers - JSCodeGen jsCodeGen(finalUrlString(), parsedQML->code, &parsedQML->jsModule, &parsedQML->jsParserEngine, parsedQML->program); - const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(/*### context root*/0, parsedQML->functions); + JSCodeGen jsCodeGen(finalUrlString(), parsedQML->code, &parsedQML->jsModule, &parsedQML->jsParserEngine, parsedQML->program, m_compiledData->importCache); + const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(parsedQML->functions); QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); @@ -2920,7 +2920,7 @@ void QQmlScriptBlob::done() QList<QQmlError> errors; QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); - m_scriptData->m_precompiledScript = QV4::Script::precompile(v4, m_scriptData->url, m_source, /*parseAsBinding*/true, &errors); + m_scriptData->m_precompiledScript = QV4::Script::precompile(v4, m_scriptData->url, m_source, &errors); if (m_scriptData->m_precompiledScript) m_scriptData->m_precompiledScript->ref(); m_source.clear(); |