diff options
-rw-r--r-- | src/qml/compiler/qqmltypecompiler.cpp | 66 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler_p.h | 14 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4script.cpp | 18 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4script_p.h | 4 | ||||
-rw-r--r-- | src/qml/qml/qqmlbinding.cpp | 17 | ||||
-rw-r--r-- | src/qml/qml/qqmlexpression.cpp | 23 | ||||
-rw-r--r-- | src/qml/qml/qqmlexpression_p.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 6 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/scriptString7.qml | 6 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 22 |
10 files changed, 145 insertions, 33 deletions
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index ad1321b1e6..d79143fdc3 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -203,6 +203,13 @@ bool QQmlTypeCompiler::compile() // Compile JS binding expressions and signal handlers if (!document->javaScriptCompilationUnit) { + { + // We can compile script strings ahead of time, but they must be compiled + // without type optimizations as their scope is always entirely dynamic. + QQmlScriptStringScanner sss(this); + sss.scan(); + } + QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, compiledData->importCache, &document->jsGenerator.stringTable); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) @@ -278,6 +285,8 @@ bool QQmlTypeCompiler::compile() compiledData->totalParserStatusCount = parserStatusCount; compiledData->totalObjectCount = objectCount; + Q_ASSERT(compiledData->propertyCaches.count() == static_cast<int>(compiledData->qmlUnit->nObjects)); + return errors.isEmpty(); } @@ -330,9 +339,10 @@ int QQmlTypeCompiler::rootObjectIndex() const void QQmlTypeCompiler::setPropertyCaches(const QVector<QQmlPropertyCache *> &caches) { - Q_ASSERT(compiledData->propertyCaches.isEmpty()); compiledData->propertyCaches = caches; Q_ASSERT(caches.count() >= document->indexOfRootObject); + if (compiledData->rootPropertyCache) + compiledData->rootPropertyCache->release(); compiledData->rootPropertyCache = caches.at(document->indexOfRootObject); compiledData->rootPropertyCache->addref(); } @@ -1255,6 +1265,42 @@ void QQmlAliasAnnotator::annotateBindingsToAliases() } } +QQmlScriptStringScanner::QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(*typeCompiler->qmlObjects()) + , propertyCaches(typeCompiler->propertyCaches()) +{ + +} + +void QQmlScriptStringScanner::scan() +{ + const int scriptStringMetaType = qMetaTypeId<QQmlScriptString>(); + for (int i = 0; i < qmlObjects.count(); ++i) { + QQmlPropertyCache *propertyCache = propertyCaches.at(i); + if (!propertyCache) + continue; + + const QmlIR::Object *obj = qmlObjects.at(i); + + QmlIR::PropertyResolver resolver(propertyCache); + QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); + + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + bool notInRevision = false; + QQmlPropertyData *pd = binding->propertyNameIndex != 0 ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; + if (!pd || pd->propType != scriptStringMetaType) + continue; + + QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); + if (foe) + foe->disableAcceleratedLookups = true; + } + } +} + QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) , enginePrivate(typeCompiler->enginePrivate()) @@ -1331,6 +1377,10 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI qmlObjects->append(syntheticComponent); const int componentIndex = qmlObjects->count() - 1; + // Keep property caches symmetric + QQmlPropertyCache *componentCache = enginePrivate->cache(&QQmlComponent::staticMetaObject); + componentCache->addref(); + propertyCaches.append(componentCache); QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>(); *syntheticBinding = *binding; @@ -1355,7 +1405,7 @@ bool QQmlComponentAndAliasResolver::resolve() const int objCountWithoutSynthesizedComponents = qmlObjects->count(); for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) { const QmlIR::Object *obj = qmlObjects->at(i); - QQmlPropertyCache *cache = propertyCaches.value(i); + QQmlPropertyCache *cache = propertyCaches.at(i); if (obj->inheritedTypeNameIndex == 0 && !cache) continue; @@ -1427,6 +1477,10 @@ bool QQmlComponentAndAliasResolver::resolve() resolveAliases(); + // Implicit component insertion may have added objects and thus we also need + // to extend the symmetric propertyCaches. + compiler->setPropertyCaches(propertyCaches); + return true; } @@ -1472,7 +1526,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases() foreach (int objectIndex, _objectsWithAliases) { const QmlIR::Object *obj = qmlObjects->at(objectIndex); - QQmlPropertyCache *propertyCache = propertyCaches.value(objectIndex); + QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); Q_ASSERT(propertyCache); int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); @@ -1528,7 +1582,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases() flags |= QML_ALIAS_FLAG_PTR; propertyFlags |= QQmlPropertyData::IsQObjectDerived; } else { - QQmlPropertyCache *targetCache = propertyCaches.value(targetObjectIndex); + QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); Q_ASSERT(targetCache); QmlIR::PropertyResolver resolver(targetCache); @@ -2249,7 +2303,7 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co } else if (property->isQList()) { const int listType = enginePrivate->listType(property->propType); if (!QQmlMetaType::isInterface(listType)) { - QQmlPropertyCache *source = propertyCaches.value(binding->value.objectIndex); + QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); if (!canCoerce(listType, source)) { recordError(binding->valueLocation, tr("Cannot assign object to list")); return false; @@ -2274,7 +2328,7 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co bool isAssignable = false; // Determine isAssignable value if (propertyMetaObject) { - QQmlPropertyCache *c = propertyCaches.value(binding->value.objectIndex); + QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); while (c && !isAssignable) { isAssignable |= c == propertyMetaObject; c = c->parent(); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index d54bc1b37b..3b449bf9bd 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -207,6 +207,18 @@ private: const QVector<QQmlPropertyCache *> propertyCaches; }; +class QQmlScriptStringScanner : public QQmlCompilePass +{ +public: + QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler); + + void scan(); + +private: + const QList<QmlIR::Object*> &qmlObjects; + const QVector<QQmlPropertyCache *> propertyCaches; +}; + class QQmlComponentAndAliasResolver : public QQmlCompilePass { Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) @@ -238,7 +250,7 @@ protected: QList<int> _objectsWithAliases; QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes; - const QVector<QQmlPropertyCache *> propertyCaches; + QVector<QQmlPropertyCache *> propertyCaches; QVector<QByteArray> *vmeMetaObjectData; QHash<int, int> *objectIndexToIdForRoot; QHash<int, QHash<int, int> > *objectIndexToIdPerComponent; diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 15298017c7..e489b99ec5 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -54,6 +54,7 @@ #include <private/qqmlengine_p.h> #include <qv4jsir_p.h> #include <qv4codegen_p.h> +#include <qml/qqmlcontextwrapper_p.h> #include <QtCore/QDebug> #include <QtCore/QString> @@ -69,8 +70,9 @@ QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, Function *f, Objec setVTable(staticVTable()); function = f; - function->compilationUnit->ref(); - needsActivation = function->needsActivation(); + if (function) + function->compilationUnit->ref(); + needsActivation = function ? function->needsActivation() : false; Scope s(scope); ScopedValue protectThis(s, this); @@ -108,7 +110,8 @@ ReturnedValue QmlBindingWrapper::call(Managed *that, CallData *) Scope scope(engine); QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that); - Q_ASSERT(This->function); + if (!This->function) + return QV4::Encode::undefined(); CallContext *ctx = This->qmlContext; std::fill(ctx->locals, ctx->locals + ctx->function->varCount(), Primitive::undefinedValue()); @@ -129,6 +132,15 @@ void QmlBindingWrapper::markObjects(Managed *m, ExecutionEngine *e) wrapper->qmlContext->mark(e); } +Returned<FunctionObject> *QmlBindingWrapper::createQmlCallableForFunction(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *scopeObject, Function *runtimeFunction) +{ + QV4::Scope valueScope(engine); + QV4::ScopedObject qmlScopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(engine->v8Engine, qmlContext, scopeObject)); + QV4::Scoped<QV4::QmlBindingWrapper> wrapper(valueScope, new (engine->memoryManager) QV4::QmlBindingWrapper(engine->rootContext, qmlScopeObject)); + QV4::ScopedFunctionObject function(valueScope, QV4::FunctionObject::createScriptFunction(wrapper->context(), runtimeFunction)); + return function->asReturned<FunctionObject>(); +} + DEFINE_OBJECT_VTABLE(QmlBindingWrapper); struct CompilationUnitHolder : public QV4::Object diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index ec06565ddc..0165778f4b 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -49,6 +49,8 @@ QT_BEGIN_NAMESPACE +class QQmlContextData; + namespace QV4 { struct ExecutionContext; @@ -65,6 +67,8 @@ struct QmlBindingWrapper : FunctionObject { CallContext *context() const { return qmlContext; } + static Returned<FunctionObject> *createQmlCallableForFunction(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *scopeObject, QV4::Function *runtimeFunction); + private: Object *qml; CallContext *qmlContext; diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 71c0f0b709..e9c48baa86 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -88,11 +88,8 @@ QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt) if (QQmlCompiledData *cdata = typeData->compiledData()) { QV4::ExecutionEngine *v4 = engine->v4engine(); QV4::Scope valueScope(v4); - QV4::ScopedObject scopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(v4->v8Engine, ctxtdata, obj)); - QV4::Scoped<QV4::QmlBindingWrapper> wrapper(valueScope, new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, scopeObject)); - QV4::ExecutionContext *qmlContext = wrapper->context(); QV4::Function *runtimeFunction = cdata->compilationUnit->runtimeFunctions[cdata->customParserBindings[id]]; - QV4::ScopedValue function(valueScope, QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction)); + QV4::ScopedValue function(valueScope, QV4::QmlBindingWrapper::createQmlCallableForFunction(v4, ctxtdata, obj, runtimeFunction)); rv = new QQmlBinding(function, obj, ctxtdata); } @@ -128,7 +125,7 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte return; QString url; - QString code; + QV4::Function *runtimeFunction = 0; QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine()); @@ -138,17 +135,23 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte if (QQmlCompiledData *cdata = typeData->compiledData()) { url = cdata->name; + if (scriptPrivate->bindingId != QQmlBinding::Invalid) + runtimeFunction = cdata->compilationUnit->runtimeFunctions.at(scriptPrivate->bindingId); } typeData->release(); } - code = scriptPrivate->script; setNotifyOnValueChanged(true); QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); setScopeObject(obj ? obj : scriptPrivate->scope); - v4function = qmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber); + if (runtimeFunction) { + v4function = QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxtdata, scopeObject(), runtimeFunction); + } else { + QString code = scriptPrivate->script; + v4function = qmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber); + } } QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt) diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 6034d3fdea..e3e5dff4d8 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -78,17 +78,10 @@ void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QOb expressionFunctionValid = false; } -void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, - QObject *me, const QString &srcUrl, - quint16 lineNumber, quint16 columnNumber) +void QQmlExpressionPrivate::init(QQmlContextData *ctxt, QV4::Function *runtimeFunction, QObject *me) { - url = srcUrl; - line = lineNumber; - column = columnNumber; - - expression = expr; - - expressionFunctionValid = false; + expressionFunctionValid = true; + function = QV4::QmlBindingWrapper::createQmlCallableForFunction(QQmlEnginePrivate::getV4Engine(ctxt->engine), ctxt, me, runtimeFunction); QQmlAbstractExpression::setContext(ctxt); setScopeObject(me); @@ -156,9 +149,9 @@ QQmlExpression::QQmlExpression(const QQmlScriptString &script, QQmlContext *ctxt if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid())) return; - bool defaultConstruction = true; QQmlContextData *evalCtxtData = QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context); QObject *scopeObject = scope ? scope : scriptPrivate->scope; + QV4::Function *runtimeFunction = 0; if (scriptPrivate->context) { QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context); @@ -171,13 +164,19 @@ QQmlExpression::QQmlExpression(const QQmlScriptString &script, QQmlContext *ctxt d->url = cdata->name; d->line = scriptPrivate->lineNumber; d->column = scriptPrivate->columnNumber; + + if (scriptPrivate->bindingId != QQmlBinding::Invalid) + runtimeFunction = cdata->compilationUnit->runtimeFunctions.at(scriptPrivate->bindingId); } typeData->release(); } } - if (defaultConstruction) + if (runtimeFunction) { + d->expression = scriptPrivate->script; + d->init(evalCtxtData, runtimeFunction, scopeObject); + } else d->init(evalCtxtData, scriptPrivate->script, scopeObject); } diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index e84d193837..5488459962 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -77,7 +77,7 @@ public: ~QQmlExpressionPrivate(); void init(QQmlContextData *, const QString &, QObject *); - void init(QQmlContextData *, const QString &, QObject *, const QString &, quint16, quint16); + void init(QQmlContextData *, QV4::Function *runtimeFunction, QObject *); QVariant value(bool *isUndefined = 0); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index f6d1e81f3f..ff32266588 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -744,7 +744,7 @@ bool QQmlObjectCreator::setPropertyBinding(QQmlPropertyData *property, const QV4 // ### resolve this at compile time if (property && property->propType == qMetaTypeId<QQmlScriptString>()) { QQmlScriptString ss(binding->valueAsScriptString(&qmlUnit->header), context->asQQmlContext(), _scopeObject); - ss.d.data()->bindingId = QQmlBinding::Invalid; + ss.d.data()->bindingId = binding->value.compiledScriptIndex; ss.d.data()->lineNumber = binding->location.line; ss.d.data()->columnNumber = binding->location.column; ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String; @@ -1125,7 +1125,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (isComponent) return instance; - QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(index); + QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.at(index); Q_ASSERT(!cache.isNull()); if (installPropertyCache) { if (ddata->propertyCache) @@ -1273,7 +1273,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * QV4::Scope valueScope(v4); QV4::ScopedValue scopeObjectProtector(valueScope); - QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(index); + QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.at(index); QQmlVMEMetaObject *vmeMetaObject = 0; const QByteArray data = vmeMetaObjectData.value(index); diff --git a/tests/auto/qml/qqmllanguage/data/scriptString7.qml b/tests/auto/qml/qqmllanguage/data/scriptString7.qml new file mode 100644 index 0000000000..a9d5b47e2b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/scriptString7.qml @@ -0,0 +1,6 @@ +import Test 1.0 + +MyTypeObject { + intProperty: 100; + scriptProperty: intProperty; +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index ede499b336..978c8ed089 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -1888,6 +1888,28 @@ void tst_qqmllanguage::scriptString() QVERIFY(object != 0); QCOMPARE(object->scriptProperty().isUndefinedLiteral(), true); } + { + QQmlComponent component(&engine, testFileUrl("scriptString7.qml")); + VERIFY_ERRORS(0); + + MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); + QVERIFY(object != 0); + QQmlScriptString ss = object->scriptProperty(); + + { + QQmlExpression expr(ss, /*context*/0, object); + QCOMPARE(expr.evaluate().toInt(), int(100)); + } + + { + SimpleObjectWithCustomParser testScope; + QVERIFY(testScope.metaObject()->indexOfProperty("intProperty") != object->metaObject()->indexOfProperty("intProperty")); + + testScope.setIntProperty(42); + QQmlExpression expr(ss, /*context*/0, &testScope); + QCOMPARE(expr.evaluate().toInt(), int(42)); + } + } } // Check that default property assignments are correctly spliced into explicit |