aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-10-23 16:24:58 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-10-29 21:56:07 +0100
commitba6fc15d729304c136447242de2410fbf4f020cd (patch)
tree3ad8a9575de295c9102eae125f64246bec854852 /src/qml
parentc32265bfc562db23b7c894306ec61fd22111a7b1 (diff)
Speed up id object lookups
We can resolve lookups for objects referenced by id at QML compile time and use a run-time helper to extract the id object out of the QML context data by index instead of name. Dependencies to id objects are also tracked at compile time and registered separately before entering the generated function code. The lookup of id objects is encoded in the IR as special member lookups. Members will also then in the future be used to for property lookups in context and scope properties, as well as any other property lookups in QObjects where we can determine the meta-object. Change-Id: I36cf3ceb11b51a983da6cad5b61c3bf574acc20a Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/compiler/qqmlcodegenerator.cpp41
-rw-r--r--src/qml/compiler/qqmlcodegenerator_p.h26
-rw-r--r--src/qml/compiler/qv4codegen.cpp12
-rw-r--r--src/qml/compiler/qv4codegen_p.h5
-rw-r--r--src/qml/compiler/qv4compileddata_p.h13
-rw-r--r--src/qml/compiler/qv4compiler.cpp27
-rw-r--r--src/qml/compiler/qv4compiler_p.h1
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h9
-rw-r--r--src/qml/compiler/qv4isel_masm.cpp5
-rw-r--r--src/qml/compiler/qv4isel_masm_p.h1
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp8
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h1
-rw-r--r--src/qml/compiler/qv4isel_p.cpp5
-rw-r--r--src/qml/compiler/qv4isel_p.h1
-rw-r--r--src/qml/compiler/qv4jsir.cpp23
-rw-r--r--src/qml/compiler/qv4jsir_p.h89
-rw-r--r--src/qml/compiler/qv4regalloc.cpp6
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp13
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp8
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp4
-rw-r--r--src/qml/qml/qqmlcompiler.cpp16
-rw-r--r--src/qml/qml/qqmlcontextwrapper.cpp26
-rw-r--r--src/qml/qml/qqmlcontextwrapper_p.h7
-rw-r--r--src/qml/qml/qqmlscript.cpp10
-rw-r--r--src/qml/qml/qqmlscript_p.h2
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