diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-03-22 14:47:51 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-03-22 14:57:04 +0100 |
commit | a768780f36a9913d4371c4a61706fc90bbba18c5 (patch) | |
tree | 141606d8b87fcb932172444369fa0df5cf1acf9a /src | |
parent | b7eebec9597b38fed52710bc1dcc166d456a415d (diff) | |
parent | 56b3232a7d35fe2b856d1d87a7e1c59906b46681 (diff) |
Merge remote-tracking branch 'origin/5.13' into HEAD
Conflicts:
src/qml/compiler/qv4compileddata_p.h
src/qml/jit/qv4baselinejit.cpp
src/qml/jit/qv4jithelpers.cpp
src/qml/jsruntime/qv4lookup.cpp
src/qml/jsruntime/qv4runtime.cpp
src/qml/jsruntime/qv4runtimeapi_p.h
src/qml/jsruntime/qv4vme_moth.cpp
src/qml/qml/qqmltypemodule_p.h
Change-Id: If28793e9e08418457a11fc2c5832f03cab2fcc76
Diffstat (limited to 'src')
79 files changed, 1474 insertions, 1746 deletions
diff --git a/src/imports/layouts/qquickstacklayout.cpp b/src/imports/layouts/qquickstacklayout.cpp index 0b51d79bef..116e162aa9 100644 --- a/src/imports/layouts/qquickstacklayout.cpp +++ b/src/imports/layouts/qquickstacklayout.cpp @@ -97,6 +97,8 @@ \sa StackView */ +QT_BEGIN_NAMESPACE + QQuickStackLayout::QQuickStackLayout(QQuickItem *parent) : QQuickLayout(*new QQuickStackLayoutPrivate, parent) { @@ -345,4 +347,6 @@ bool QQuickStackLayout::shouldIgnoreItem(QQuickItem *item) const return ignored; } +QT_END_NAMESPACE + #include "moc_qquickstacklayout_p.cpp" diff --git a/src/imports/layouts/qquickstacklayout_p.h b/src/imports/layouts/qquickstacklayout_p.h index 8ba41720aa..46181c6f50 100644 --- a/src/imports/layouts/qquickstacklayout_p.h +++ b/src/imports/layouts/qquickstacklayout_p.h @@ -42,6 +42,8 @@ #include <qquicklayout_p.h> +QT_BEGIN_NAMESPACE + class QQuickStackLayoutPrivate; class QQuickStackLayout : public QQuickLayout @@ -105,4 +107,6 @@ private: bool explicitCurrentIndex; }; +QT_END_NAMESPACE + #endif // QQUICKSTACKLAYOUT_H diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index ea5efcfc66..a22dd7aa32 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -566,7 +566,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiQualifiedId *id) void IRBuilder::accept(QQmlJS::AST::Node *node) { - QQmlJS::AST::Node::acceptChild(node, this); + QQmlJS::AST::Node::accept(node, this); } bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, const QQmlJS::AST::SourceLocation &location, QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride) @@ -974,7 +974,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) foe->node = funDecl; foe->parentNode = funDecl; foe->nameIndex = registerString(funDecl->name.toString()); - foe->disableAcceleratedLookups = false; const int index = _object->functionsAndExpressions->append(foe); Function *f = New<Function>(); @@ -1098,7 +1097,6 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST expr->parentNode = parentNode; expr->nameIndex = registerString(QLatin1String("expression for ") + stringAt(binding->propertyNameIndex)); - expr->disableAcceleratedLookups = false; const int index = bindingsTarget()->functionsAndExpressions->append(expr); binding->value.compiledScriptIndex = index; // We don't need to store the binding script as string, except for script strings @@ -1825,19 +1823,13 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine, - QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, + QQmlJS::AST::UiProgram *qmlRoot, const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames) : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false) , sourceCode(sourceCode) , jsEngine(jsEngine) , qmlRoot(qmlRoot) - , imports(imports) , stringPool(stringPool) - , _disableAcceleratedLookups(false) - , _contextObject(nullptr) - , _scopeObject(nullptr) - , _qmlContextSlot(-1) - , _importedScriptsSlot(-1) { m_globalNames = globalNames; @@ -1845,18 +1837,6 @@ JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator * _fileNameIsUrl = true; } -void JSCodeGen::beginContextScope(const JSCodeGen::ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject) -{ - _idObjects = objectIds; - _contextObject = contextObject; - _scopeObject = nullptr; -} - -void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject) -{ - _scopeObject = scopeObject; -} - QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions) { auto qmlName = [&](const CompiledFunctionOrExpression &c) { @@ -1921,7 +1901,6 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil body = body->finish(); } - _disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups; int idx = defineFunction(name, function ? function : qmlFunction.parentNode, function ? function->formals : nullptr, body); @@ -1931,391 +1910,6 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil return runtimeFunctionIndices; } -int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body) -{ - int qmlContextTemp = -1; - int importedScriptsTemp = -1; - qSwap(_qmlContextSlot, qmlContextTemp); - qSwap(_importedScriptsSlot, importedScriptsTemp); - - int result = Codegen::defineFunction(name, ast, formals, body); - - qSwap(_importedScriptsSlot, importedScriptsTemp); - qSwap(_qmlContextSlot, qmlContextTemp); - - return result; -} - -#ifndef V4_BOOTSTRAP -QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name) -{ - QQmlPropertyData *pd = cache->property(name, /*object*/nullptr, /*context*/nullptr); - - if (pd && !cache->isAllowedInRevision(pd)) - return nullptr; - - return pd; -} - -enum MetaObjectResolverFlags { - AllPropertiesAreFinal = 0x1, - LookupsIncludeEnums = 0x2, - LookupsExcludeProperties = 0x4, - ResolveTypeInformationOnly = 0x8 -}; - -#if 0 -static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); - -static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index); - -static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, - const QV4::IR::MemberExpressionResolver *resolver, - QV4::IR::Member *member) -{ - QV4::IR::Type result = QV4::IR::VarType; - - QQmlType type = resolver->qmlType; - - if (member->name->constData()->isUpper()) { - bool ok = false; - int value = type.enumValue(qmlEngine, *member->name, &ok); - if (ok) { - member->setEnumValue(value); - return QV4::IR::SInt32Type; - } else { - int index = type.scopedEnumIndex(qmlEngine, *member->name, &ok); - if (ok) { - auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); - newResolver->owner = resolver->owner; - initScopedEnumResolver(newResolver, type, index); - return QV4::IR::DiscoveredType(newResolver); - } - } - } - - if (type.isCompositeSingleton()) { - QQmlRefPointer<QQmlTypeData> tdata = qmlEngine->typeLoader.getType(type.singletonInstanceInfo()->url); - Q_ASSERT(tdata); - tdata->release(); // Decrease the reference count added from QQmlTypeLoader::getType() - // When a singleton tries to reference itself, it may not be complete yet. - if (tdata->isComplete()) { - auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compilationUnit()->metaTypeId)); - newResolver->flags |= AllPropertiesAreFinal; - return newResolver->resolveMember(qmlEngine, newResolver, member); - } - } else if (type.isSingleton()) { - const QMetaObject *singletonMeta = type.singletonInstanceInfo()->instanceMetaObject; - if (singletonMeta) { // QJSValue-based singletons cannot be accelerated - auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, qmlEngine->cache(singletonMeta)); - member->kind = QV4::IR::Member::MemberOfSingletonObject; - return newResolver->resolveMember(qmlEngine, newResolver, member); - } - } -#if 0 - else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) { - // Right now the attached property IDs are not stable and cannot be embedded in the - // code that is cached on disk. - QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta); - auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, cache); - member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine)); - return newResolver->resolveMember(qmlEngine, newResolver, member); - } -#endif - - return result; -} - -static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType) -{ - Q_ASSERT(resolver); - - resolver->resolveMember = &resolveQmlType; - resolver->qmlType = qmlType; - resolver->typenameCache = 0; - resolver->flags = 0; -} - -static QV4::IR::DiscoveredType resolveImportNamespace( - QQmlEnginePrivate *, const QV4::IR::MemberExpressionResolver *resolver, - QV4::IR::Member *member) -{ - QV4::IR::Type result = QV4::IR::VarType; - QQmlTypeNameCache *typeNamespace = resolver->typenameCache; - const QQmlImportRef *importNamespace = resolver->import; - - QQmlTypeNameCache::Result r = typeNamespace->query(*member->name, importNamespace); - if (r.isValid()) { - member->freeOfSideEffects = true; - if (r.scriptIndex != -1) { - // TODO: remember the index and replace with subscript later. - result = QV4::IR::VarType; - } else if (r.type.isValid()) { - // TODO: Propagate singleton information, so that it is loaded - // through the singleton getter in the run-time. Until then we - // can't accelerate access :( - if (!r.type.isSingleton()) { - auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); - newResolver->owner = resolver->owner; - initQmlTypeResolver(newResolver, r.type); - return QV4::IR::DiscoveredType(newResolver); - } - } else { - Q_ASSERT(false); // How can this happen? - } - } - - return result; -} - -static void initImportNamespaceResolver(QV4::IR::MemberExpressionResolver *resolver, - QQmlTypeNameCache *imports, const QQmlImportRef *importNamespace) -{ - resolver->resolveMember = &resolveImportNamespace; - resolver->import = importNamespace; - resolver->typenameCache = imports; - resolver->flags = 0; -} - -static QV4::IR::DiscoveredType resolveMetaObjectProperty( - QQmlEnginePrivate *qmlEngine, const QV4::IR::MemberExpressionResolver *resolver, - QV4::IR::Member *member) -{ - QV4::IR::Type result = QV4::IR::VarType; - QQmlPropertyCache *metaObject = resolver->propertyCache; - - if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) { - const QMetaObject *mo = metaObject->createMetaObject(); - QByteArray enumName = member->name->toUtf8(); - for (int ii = mo->enumeratorCount() - 1; ii >= 0; --ii) { - QMetaEnum metaEnum = mo->enumerator(ii); - bool ok; - int value = metaEnum.keyToValue(enumName.constData(), &ok); - if (ok) { - member->setEnumValue(value); - return QV4::IR::SInt32Type; - } - } - } - - if (member->kind != QV4::IR::Member::MemberOfIdObjectsArray && member->kind != QV4::IR::Member::MemberOfSingletonObject && - qmlEngine && !(resolver->flags & LookupsExcludeProperties)) { - QQmlPropertyData *property = member->property; - if (!property && metaObject) { - if (QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0)) { - const bool isFinalProperty = (candidate->isFinal() || (resolver->flags & AllPropertiesAreFinal)) - && !candidate->isFunction(); - - if (lookupHints() - && !(resolver->flags & AllPropertiesAreFinal) - && !candidate->isFinal() - && !candidate->isFunction() - && candidate->isDirect()) { - qWarning() << "Hint: Access to property" << *member->name << "of" << metaObject->className() << "could be accelerated if it was marked as FINAL"; - } - - if (isFinalProperty && metaObject->isAllowedInRevision(candidate)) { - property = candidate; - member->inhibitTypeConversionOnWrite = true; - if (!(resolver->flags & ResolveTypeInformationOnly)) - member->property = candidate; // Cache for next iteration and isel needs it. - } - } - } - - if (property) { - // Enums cannot be mapped to IR types, they need to go through the run-time handling - // of accepting strings that will then be converted to the right values. - if (property->isEnum()) - return QV4::IR::VarType; - - switch (property->propType()) { - case QMetaType::Bool: result = QV4::IR::BoolType; break; - case QMetaType::Int: result = QV4::IR::SInt32Type; break; - case QMetaType::Double: result = QV4::IR::DoubleType; break; - case QMetaType::QString: result = QV4::IR::StringType; break; - default: - if (property->isQObject()) { - if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType())) { - auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, cache); - return QV4::IR::DiscoveredType(newResolver); - } - } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType())) { - if (QQmlPropertyCache *cache = qmlEngine->cache(valueTypeMetaObject)) { - auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, cache); - newResolver->flags |= ResolveTypeInformationOnly; - return QV4::IR::DiscoveredType(newResolver); - } - } - break; - } - } - } - - return result; -} - -static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject) -{ - Q_ASSERT(resolver); - - resolver->resolveMember = &resolveMetaObjectProperty; - resolver->propertyCache = metaObject; - resolver->flags = 0; -} - -static QV4::IR::DiscoveredType resolveScopedEnum(QQmlEnginePrivate *qmlEngine, - const QV4::IR::MemberExpressionResolver *resolver, - QV4::IR::Member *member) -{ - if (!member->name->constData()->isUpper()) - return QV4::IR::VarType; - - QQmlType type = resolver->qmlType; - int index = resolver->flags; - - bool ok = false; - int value = type.scopedEnumValue(qmlEngine, index, *member->name, &ok); - if (!ok) - return QV4::IR::VarType; - member->setEnumValue(value); - return QV4::IR::SInt32Type; -} - -static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index) -{ - Q_ASSERT(resolver); - - resolver->resolveMember = &resolveScopedEnum; - resolver->qmlType = qmlType; - resolver->flags = index; -} -#endif - -#endif // V4_BOOTSTRAP - -void JSCodeGen::beginFunctionBodyHook() -{ - _qmlContextSlot = bytecodeGenerator->newRegister(); - _importedScriptsSlot = bytecodeGenerator->newRegister(); - -#ifndef V4_BOOTSTRAP - Instruction::LoadQmlContext load; - load.result = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot(); - bytecodeGenerator->addInstruction(load); - -#if 0 - temp->type = QV4::IR::QObjectType; - temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - initMetaObjectResolver(temp->memberResolver, _scopeObject); - auto name = _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0); - name->type = temp->type; -#endif - - Instruction::LoadQmlImportedScripts loadScripts; - loadScripts.result = Reference::fromStackSlot(this, _importedScriptsSlot).stackSlot(); - bytecodeGenerator->addInstruction(loadScripts); -#endif -} - -QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name) -{ -#ifndef V4_BOOTSTRAP - if (_disableAcceleratedLookups) - return Reference(); - - // 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. - for (const IdMapping &mapping : qAsConst(_idObjects)) { - if (name == mapping.name) { - if (_context->contextType == QV4::Compiler::ContextType::Binding) - _context->idObjectDependencies.insert(mapping.idIndex); - - Instruction::LoadIdObject load; - load.base = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot(); - load.index = mapping.idIndex; - - Reference result = Reference::fromAccumulator(this); - bytecodeGenerator->addInstruction(load); - result.isReadonly = true; - return result; - } - } - - if (name.at(0).isUpper()) { - QQmlTypeNameCache::Result r = imports->query(name); - if (r.isValid()) { - if (r.scriptIndex != -1) { - Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot); - return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex))); - } else if (r.type.isValid()) { - return Reference::fromName(this, name); - } else { - Q_ASSERT(r.importNamespace); - return Reference::fromName(this, name); - } - } - } - - if (_scopeObject) { - QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name); - if (data) { - // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time - if (data->isFunction()) - return Reference::fromName(this, name); - - Reference base = Reference::fromStackSlot(this, _qmlContextSlot); - Reference::PropertyCapturePolicy capturePolicy; - if (!data->isConstant() && !data->isQmlBinding()) - capturePolicy = Reference::CaptureAtRuntime; - else - capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime; - return Reference::fromQmlScopeObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy); - } - } - - if (_contextObject) { - QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name); - if (data) { - // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time - if (data->isFunction()) - return Reference::fromName(this, name); - - Reference base = Reference::fromStackSlot(this, _qmlContextSlot); - Reference::PropertyCapturePolicy capturePolicy; - if (!data->isConstant() && !data->isQmlBinding()) - capturePolicy = Reference::CaptureAtRuntime; - else - capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime; - return Reference::fromQmlContextObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy); - } - } -#else - Q_UNUSED(name) -#endif // V4_BOOTSTRAP - return Reference(); -} - #ifndef V4_BOOTSTRAP QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const @@ -2435,7 +2029,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO b->value.compiledScriptIndex = functionIndices.count() - 1; QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); - foe->disableAcceleratedLookups = true; foe->nameIndex = 0; QQmlJS::AST::ExpressionNode *expr; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 8512b22fbd..298fe7dd92 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -278,7 +278,6 @@ struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression quint32 nameIndex = 0; - bool disableAcceleratedLookups = false; CompiledFunctionOrExpression *next = nullptr; }; @@ -431,6 +430,12 @@ public: bool visit(QQmlJS::AST::UiScriptBinding *ast) override; bool visit(QQmlJS::AST::UiSourceElement *ast) override; + void throwRecursionDepthError() override + { + recordError(AST::SourceLocation(), + QStringLiteral("Maximum statement or expression depth exceeded")); + } + void accept(QQmlJS::AST::Node *node); // returns index in _objects @@ -533,47 +538,16 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen { JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, - QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames); - - struct IdMapping - { - QString name; - int idIndex; - QQmlPropertyCache *type; - }; - typedef QVector<IdMapping> ObjectIdMapping; - - void beginContextScope(const ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject); - void beginObjectScope(QQmlPropertyCache *scopeObject); + const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames); // Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions); - int defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::StatementList *body) override; - -protected: - void beginFunctionBodyHook() override; - bool canAccelerateGlobalLookups() const override { return !_disableAcceleratedLookups; } - Reference fallbackNameLookup(const QString &name) override; - private: - // returns nullptr if lookup needs to happen by name - QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name); - QString sourceCode; QQmlJS::Engine *jsEngine; // needed for memory pool QQmlJS::AST::UiProgram *qmlRoot; - QQmlTypeNameCache *imports; const QV4::Compiler::StringTableGenerator *stringPool; - - bool _disableAcceleratedLookups; - ObjectIdMapping _idObjects; - QQmlPropertyCache *_contextObject; - QQmlPropertyCache *_scopeObject; - int _qmlContextSlot; - int _importedScriptsSlot; }; struct Q_QML_PRIVATE_EXPORT IRLoader { diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 70b048d737..66d3afc7a0 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -146,8 +146,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() document->jsModule.fileName = typeData->urlString(); document->jsModule.finalUrl = typeData->finalUrlString(); QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, - document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames()); - v4CodeGenerator.setUseFastLookups(false); + document->program, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames()); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return nullptr; @@ -767,10 +766,6 @@ void QQmlScriptStringScanner::scan() if (!pd || pd->propType() != scriptStringMetaType) continue; - QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); - if (foe) - foe->disableAcceleratedLookups = true; - QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); binding->stringIndex = compiler->registerString(script); } @@ -1324,24 +1319,6 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject) contextObject = componentBinding->value.objectIndex; } - QmlIR::JSCodeGen::ObjectIdMapping idMapping; - idMapping.reserve(obj->namedObjectsInComponent.size()); - for (int i = 0; i < obj->namedObjectsInComponent.size(); ++i) { - const int objectIndex = obj->namedObjectsInComponent.at(i); - QmlIR::JSCodeGen::IdMapping m; - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - m.name = stringAt(obj->idNameIndex); - m.idIndex = obj->id; - m.type = propertyCaches->at(objectIndex); - - auto *tref = resolvedType(obj->inheritedTypeNameIndex); - if (tref && tref->isFullyDynamicType) - m.type = nullptr; - - idMapping << m; - } - v4CodeGen->beginContextScope(idMapping, propertyCaches->at(contextObject)); - if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject)) return false; @@ -1355,16 +1332,9 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn return true; if (object->functionsAndExpressions->count > 0) { - QQmlPropertyCache *scopeObject = propertyCaches->at(scopeObjectIndex); - v4CodeGen->beginObjectScope(scopeObject); - QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; - for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) { - const bool haveCustomParser = customParsers.contains(object->inheritedTypeNameIndex); - if (haveCustomParser) - foe->disableAcceleratedLookups = true; + for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) functionsToCompile << *foe; - } const QVector<int> runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile); const QList<QQmlError> jsErrors = v4CodeGen->qmlErrors(); if (!jsErrors.isEmpty()) { diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 1149c9ff1e..a2bfd55332 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -100,9 +100,10 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) , hasError(false) { jsUnitGenerator->codeGeneratorName = QStringLiteral("moth"); + pushExpr(); } -const char *globalNames[] = { +const char *Codegen::s_globalNames[] = { "isNaN", "parseFloat", "String", @@ -182,7 +183,7 @@ void Codegen::generateFromProgram(const QString &fileName, // // Since this can be called from the loader thread we can't get the list // directly from the engine, so let's hardcode the most important ones here - for (const char **g = globalNames; *g != nullptr; ++g) + for (const char **g = s_globalNames; *g != nullptr; ++g) m_globalNames << QString::fromLatin1(*g); } @@ -264,7 +265,7 @@ Context *Codegen::enterBlock(Node *node) Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) { if (hasError) - return _expr.result(); + return exprResult(); if (expr.isConstant()) { auto v = Value::fromReturnedValue(expr.constant); @@ -310,7 +311,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) return Reference::fromAccumulator(this); } case PostIncrement: - if (!_expr.accept(nx) || requiresReturnValue) { + if (!exprAccept(nx) || requiresReturnValue) { Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::UPlus uplus = {}; @@ -330,13 +331,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) e.loadInAccumulator(); Instruction::Increment inc = {}; bytecodeGenerator->addTracingInstruction(inc); - if (_expr.accept(nx)) + if (exprAccept(nx)) return e.storeConsumeAccumulator(); else return e.storeRetainAccumulator(); } case PostDecrement: - if (!_expr.accept(nx) || requiresReturnValue) { + if (!exprAccept(nx) || requiresReturnValue) { Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::UPlus uplus = {}; @@ -356,7 +357,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) e.loadInAccumulator(); Instruction::Decrement dec = {}; bytecodeGenerator->addTracingInstruction(dec); - if (_expr.accept(nx)) + if (exprAccept(nx)) return e.storeConsumeAccumulator(); else return e.storeRetainAccumulator(); @@ -368,22 +369,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) void Codegen::addCJump() { - bytecodeGenerator->addCJumpInstruction(_expr.trueBlockFollowsCondition(), - _expr.iftrue(), _expr.iffalse()); -} - -void Codegen::accept(Node *node) -{ - if (hasError) - return; - - if (node) - node->accept(this); + const Result &expression = currentExpr(); + bytecodeGenerator->addCJumpInstruction(expression.trueBlockFollowsCondition(), + expression.iftrue(), expression.iffalse()); } void Codegen::statement(Statement *ast) { - RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); RegisterScope scope(this); bytecodeGenerator->setLocation(ast->firstSourceLocation()); @@ -399,23 +391,21 @@ void Codegen::statement(ExpressionNode *ast) if (! ast) { return; } else { - RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); RegisterScope scope(this); - Result r(nx); - qSwap(_expr, r); + pushExpr(Result(nx)); VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); qSwap(_volatileMemoryLocations, vLocs); accept(ast); qSwap(_volatileMemoryLocations, vLocs); - qSwap(_expr, r); + Reference result = popResult(); if (hasError) return; - if (r.result().loadTriggersSideEffect()) - r.result().loadInAccumulator(); // triggers side effects + if (result.loadTriggersSideEffect()) + result.loadInAccumulator(); // triggers side effects } } @@ -428,11 +418,9 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift if (!ast) return; - RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); - Result r(iftrue, iffalse, trueBlockFollowsCondition); - qSwap(_expr, r); + pushExpr(Result(iftrue, iffalse, trueBlockFollowsCondition)); accept(ast); - qSwap(_expr, r); + Result r = popExpr(); if (hasError) return; @@ -450,18 +438,6 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift } } -Codegen::Reference Codegen::expression(ExpressionNode *ast) -{ - RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); - Result r; - if (ast) { - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - } - return r.result(); -} - void Codegen::program(Program *ast) { if (ast) { @@ -875,17 +851,13 @@ bool Codegen::visit(ExportDeclaration *ast) Reference exportedValue; if (auto *fdecl = AST::cast<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) { - Result r; - qSwap(_expr, r); + pushExpr(); visit(static_cast<FunctionExpression*>(fdecl)); - qSwap(_expr, r); - exportedValue = r.result(); + exportedValue = popResult(); } else if (auto *classDecl = AST::cast<ClassDeclaration*>(ast->variableStatementOrDeclaration)) { - Result r; - qSwap(_expr, r); + pushExpr(); visit(static_cast<ClassExpression*>(classDecl)); - qSwap(_expr, r); - exportedValue = r.result(); + exportedValue = popResult(); } else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) { exportedValue = expression(expr); } @@ -1068,7 +1040,7 @@ bool Codegen::visit(ClassExpression *ast) (void) ctor.storeRetainAccumulator(); } - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -1151,7 +1123,7 @@ bool Codegen::visit(ArrayPattern *ast) } if (!it) { - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement); @@ -1246,7 +1218,7 @@ bool Codegen::visit(ArrayPattern *ast) } array.loadInAccumulator(); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -1262,7 +1234,7 @@ bool Codegen::visit(ArrayMemberExpression *ast) return false; if (base.isSuper()) { Reference index = expression(ast->expression).storeOnStack(); - _expr.setResult(Reference::fromSuperProperty(index)); + setExprResult(Reference::fromSuperProperty(index)); return false; } base = base.storeOnStack(); @@ -1272,17 +1244,17 @@ bool Codegen::visit(ArrayMemberExpression *ast) QString s = str->value.toString(); uint arrayIndex = QV4::String::toArrayIndex(s); if (arrayIndex == UINT_MAX) { - _expr.setResult(Reference::fromMember(base, str->value.toString())); + setExprResult(Reference::fromMember(base, str->value.toString())); return false; } Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex)); - _expr.setResult(Reference::fromSubscript(base, index)); + setExprResult(Reference::fromSubscript(base, index)); return false; } Reference index = expression(ast->expression); if (hasError) return false; - _expr.setResult(Reference::fromSubscript(base, index)); + setExprResult(Reference::fromSubscript(base, index)); return false; } @@ -1313,12 +1285,13 @@ bool Codegen::visit(BinaryExpression *ast) TailCallBlocker blockTailCalls(this); if (ast->op == QSOperator::And) { - if (_expr.accept(cx)) { + if (exprAccept(cx)) { auto iftrue = bytecodeGenerator->newLabel(); - condition(ast->left, &iftrue, _expr.iffalse(), true); + condition(ast->left, &iftrue, currentExpr().iffalse(), true); iftrue.link(); blockTailCalls.unblock(); - condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition()); + const Result &expr = currentExpr(); + condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition()); } else { auto iftrue = bytecodeGenerator->newLabel(); auto endif = bytecodeGenerator->newLabel(); @@ -1340,15 +1313,16 @@ bool Codegen::visit(BinaryExpression *ast) endif.link(); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); } return false; } else if (ast->op == QSOperator::Or) { - if (_expr.accept(cx)) { + if (exprAccept(cx)) { auto iffalse = bytecodeGenerator->newLabel(); - condition(ast->left, _expr.iftrue(), &iffalse, false); + condition(ast->left, currentExpr().iftrue(), &iffalse, false); iffalse.link(); - condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition()); + const Result &expr = currentExpr(); + condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition()); } else { auto iffalse = bytecodeGenerator->newLabel(); auto endif = bytecodeGenerator->newLabel(); @@ -1370,7 +1344,7 @@ bool Codegen::visit(BinaryExpression *ast) endif.link(); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); } return false; } else if (ast->op == QSOperator::Assign) { @@ -1381,9 +1355,9 @@ bool Codegen::visit(BinaryExpression *ast) return false; right = right.storeOnStack(); destructurePattern(p, right); - if (!_expr.accept(nx)) { + if (!exprAccept(nx)) { right.loadInAccumulator(); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); } return false; } @@ -1403,10 +1377,10 @@ bool Codegen::visit(BinaryExpression *ast) if (hasError) return false; r.loadInAccumulator(); - if (_expr.accept(nx)) - _expr.setResult(left.storeConsumeAccumulator()); + if (exprAccept(nx)) + setExprResult(left.storeConsumeAccumulator()); else - _expr.setResult(left.storeRetainAccumulator()); + setExprResult(left.storeRetainAccumulator()); return false; } @@ -1449,7 +1423,7 @@ bool Codegen::visit(BinaryExpression *ast) return false; binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator(); - _expr.setResult(left.storeRetainAccumulator()); + setExprResult(left.storeRetainAccumulator()); break; } @@ -1461,7 +1435,7 @@ bool Codegen::visit(BinaryExpression *ast) Reference right = expression(ast->right); if (hasError) return false; - _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left)); + setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left)); break; } // intentional fall-through! @@ -1487,7 +1461,7 @@ bool Codegen::visit(BinaryExpression *ast) Reference right; if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) { visit(rhs); - right = _expr.result(); + right = exprResult(); } else { left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it right = expression(ast->right); @@ -1495,7 +1469,7 @@ bool Codegen::visit(BinaryExpression *ast) if (hasError) return false; - _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right)); + setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right)); break; } @@ -1672,7 +1646,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::StrictEqual: { - if (_expr.accept(cx)) + if (exprAccept(cx)) return jumpBinop(oper, left, right); Instruction::CmpStrictEqual cmp; @@ -1683,7 +1657,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::StrictNotEqual: { - if (_expr.accept(cx)) + if (exprAccept(cx)) return jumpBinop(oper, left, right); Instruction::CmpStrictNotEqual cmp; @@ -1694,7 +1668,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::Equal: { - if (_expr.accept(cx)) + if (exprAccept(cx)) return jumpBinop(oper, left, right); Instruction::CmpEq cmp; @@ -1705,7 +1679,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::NotEqual: { - if (_expr.accept(cx)) + if (exprAccept(cx)) return jumpBinop(oper, left, right); Instruction::CmpNe cmp; @@ -1716,7 +1690,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::Gt: { - if (_expr.accept(cx)) + if (exprAccept(cx)) return jumpBinop(oper, left, right); Instruction::CmpGt cmp; @@ -1727,7 +1701,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::Ge: { - if (_expr.accept(cx)) + if (exprAccept(cx)) return jumpBinop(oper, left, right); Instruction::CmpGe cmp; @@ -1738,7 +1712,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::Lt: { - if (_expr.accept(cx)) + if (exprAccept(cx)) return jumpBinop(oper, left, right); Instruction::CmpLt cmp; @@ -1749,7 +1723,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; } case QSOperator::Le: - if (_expr.accept(cx)) + if (exprAccept(cx)) return jumpBinop(oper, left, right); Instruction::CmpLe cmp; @@ -1902,8 +1876,6 @@ bool Codegen::visit(CallExpression *ast) switch (base.type) { case Reference::Member: case Reference::Subscript: - case Reference::QmlScopeObject: - case Reference::QmlContextObject: base = base.asLValue(); break; case Reference::Name: @@ -1953,7 +1925,7 @@ bool Codegen::visit(CallExpression *ast) bytecodeGenerator->addInstruction(call); } - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -1965,21 +1937,7 @@ bool Codegen::visit(CallExpression *ast) void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject) { //### Do we really need all these call instructions? can's we load the callee in a temp? - if (base.type == Reference::QmlScopeObject) { - Instruction::CallScopeObjectProperty call; - call.base = base.qmlBase.stackSlot(); - call.name = base.qmlCoreIndex; - call.argc = calldata.argc; - call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); - } else if (base.type == Reference::QmlContextObject) { - Instruction::CallContextObjectProperty call; - call.base = base.qmlBase.stackSlot(); - call.name = base.qmlCoreIndex; - call.argc = calldata.argc; - call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); - } else if (base.type == Reference::Member) { + if (base.type == Reference::Member) { if (!disable_lookups && useFastLookups) { Instruction::CallPropertyLookup call; call.base = base.propertyBase.stackSlot(); @@ -2009,11 +1967,19 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.argv = calldata.argv; bytecodeGenerator->addTracingInstruction(call); } else if (!disable_lookups && useFastLookups && base.global) { - Instruction::CallGlobalLookup call; - call.index = registerGlobalGetterLookup(base.nameAsIndex()); - call.argc = calldata.argc; - call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + if (base.qmlGlobal) { + Instruction::CallQmlContextPropertyLookup call; + call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex()); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addTracingInstruction(call); + } else { + Instruction::CallGlobalLookup call; + call.index = registerGlobalGetterLookup(base.nameAsIndex()); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addTracingInstruction(call); + } } else { Instruction::CallName call; call.name = base.nameAsIndex(); @@ -2046,7 +2012,7 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio bytecodeGenerator->addTracingInstruction(call); } - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); } Codegen::Arguments Codegen::pushArgs(ArgumentList *args) @@ -2142,7 +2108,7 @@ bool Codegen::visit(ConditionalExpression *ast) ko.loadInAccumulator(); jump_endif.link(); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -2172,7 +2138,7 @@ bool Codegen::visit(DeleteExpression *ast) throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); return false; } - _expr.setResult(Reference::fromConst(this, QV4::Encode(false))); + setExprResult(Reference::fromConst(this, QV4::Encode(false))); return false; case Reference::Name: { if (_context->isStrict) { @@ -2182,7 +2148,7 @@ bool Codegen::visit(DeleteExpression *ast) Instruction::DeleteName del; del.name = expr.nameAsIndex(); bytecodeGenerator->addInstruction(del); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } case Reference::Member: { @@ -2197,7 +2163,7 @@ bool Codegen::visit(DeleteExpression *ast) del.base = expr.propertyBase.stackSlot(); del.index = index.stackSlot(); bytecodeGenerator->addInstruction(del); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } case Reference::Subscript: { @@ -2207,14 +2173,14 @@ bool Codegen::visit(DeleteExpression *ast) del.base = expr.elementBase; del.index = expr.elementSubscript.stackSlot(); bytecodeGenerator->addInstruction(del); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } default: break; } // [[11.4.1]] Return true if it's not a reference - _expr.setResult(Reference::fromConst(this, QV4::Encode(true))); + setExprResult(Reference::fromConst(this, QV4::Encode(true))); return false; } @@ -2223,7 +2189,7 @@ bool Codegen::visit(FalseLiteral *) if (hasError) return false; - _expr.setResult(Reference::fromConst(this, QV4::Encode(false))); + setExprResult(Reference::fromConst(this, QV4::Encode(false))); return false; } @@ -2232,7 +2198,7 @@ bool Codegen::visit(SuperLiteral *) if (hasError) return false; - _expr.setResult(Reference::fromSuper(this)); + setExprResult(Reference::fromSuper(this)); return false; } @@ -2250,12 +2216,12 @@ bool Codegen::visit(FieldMemberExpression *ast) if (_context->isArrowFunction || _context->contextType == ContextType::Eval) { Reference r = referenceForName(QStringLiteral("new.target"), false); r.isReadonly = true; - _expr.setResult(r); + setExprResult(r); return false; } Reference r = Reference::fromStackSlot(this, CallData::NewTarget); - _expr.setResult(r); + setExprResult(r); return false; } } @@ -2268,10 +2234,10 @@ bool Codegen::visit(FieldMemberExpression *ast) load.stringId = registerString(ast->name.toString()); bytecodeGenerator->addInstruction(load); Reference property = Reference::fromAccumulator(this).storeOnStack(); - _expr.setResult(Reference::fromSuperProperty(property)); + setExprResult(Reference::fromSuperProperty(property)); return false; } - _expr.setResult(Reference::fromMember(base, ast->name.toString())); + setExprResult(Reference::fromMember(base, ast->name.toString())); return false; } @@ -2281,12 +2247,15 @@ bool Codegen::visit(TaggedTemplate *ast) return false; RegisterScope scope(this); + return handleTaggedTemplate(expression(ast->base), ast); +} - int functionObject = -1, thisObject = -1; - - Reference base = expression(ast->base); +bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast) +{ if (hasError) return false; + + int functionObject = -1, thisObject = -1; switch (base.type) { case Reference::Member: case Reference::Subscript: @@ -2347,7 +2316,7 @@ bool Codegen::visit(FunctionExpression *ast) if (hasError) return false; loadClosure(function); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -2379,14 +2348,10 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, co return r; } - // This hook allows implementing QML lookup semantics - Reference fallback = fallbackNameLookup(name); - if (fallback.type != Reference::Invalid) - return fallback; - Reference r = Reference::fromName(this, name); - r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global); - if (!r.global && canAccelerateGlobalLookups() && m_globalNames.contains(name)) + r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global || resolved.type == Context::ResolvedName::QmlGlobal); + r.qmlGlobal = resolved.type == Context::ResolvedName::QmlGlobal; + if (!r.global && !r.qmlGlobal && m_globalNames.contains(name)) r.global = true; return r; } @@ -2402,18 +2367,12 @@ void Codegen::loadClosure(int closureId) } } -Codegen::Reference Codegen::fallbackNameLookup(const QString &name) -{ - Q_UNUSED(name) - return Reference(); -} - bool Codegen::visit(IdentifierExpression *ast) { if (hasError) return false; - _expr.setResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation())); + setExprResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation())); return false; } @@ -2463,7 +2422,7 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments) // set the result up as the thisObject Reference::fromAccumulator(this).storeOnStack(CallData::This); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); } bool Codegen::visit(NewExpression *ast) @@ -2512,7 +2471,7 @@ bool Codegen::visit(NotExpression *ast) return false; TailCallBlocker blockTailCalls(this); - _expr.setResult(unop(Not, expression(ast->expression))); + setExprResult(unop(Not, expression(ast->expression))); return false; } @@ -2521,10 +2480,10 @@ bool Codegen::visit(NullExpression *) if (hasError) return false; - if (_expr.accept(cx)) - bytecodeGenerator->jump().link(*_expr.iffalse()); + if (exprAccept(cx)) + bytecodeGenerator->jump().link(*currentExpr().iffalse()); else - _expr.setResult(Reference::fromConst(this, Encode::null())); + setExprResult(Reference::fromConst(this, Encode::null())); return false; } @@ -2534,7 +2493,7 @@ bool Codegen::visit(NumericLiteral *ast) if (hasError) return false; - _expr.setResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value))); + setExprResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value))); return false; } @@ -2646,8 +2605,7 @@ bool Codegen::visit(ObjectPattern *ast) call.argc = argc; call.args = Moth::StackSlot::createRegister(args); bytecodeGenerator->addInstruction(call); - Reference result = Reference::fromAccumulator(this); - _expr.setResult(result); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -2666,7 +2624,7 @@ bool Codegen::visit(PostDecrementExpression *ast) if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken)) return false; - _expr.setResult(unop(PostDecrement, expr)); + setExprResult(unop(PostDecrement, expr)); return false; } @@ -2686,7 +2644,7 @@ bool Codegen::visit(PostIncrementExpression *ast) if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken)) return false; - _expr.setResult(unop(PostIncrement, expr)); + setExprResult(unop(PostIncrement, expr)); return false; } @@ -2704,7 +2662,7 @@ bool Codegen::visit(PreDecrementExpression *ast) if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken)) return false; - _expr.setResult(unop(PreDecrement, expr)); + setExprResult(unop(PreDecrement, expr)); return false; } @@ -2723,7 +2681,7 @@ bool Codegen::visit(PreIncrementExpression *ast) if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken)) return false; - _expr.setResult(unop(PreIncrement, expr)); + setExprResult(unop(PreIncrement, expr)); return false; } @@ -2734,7 +2692,7 @@ bool Codegen::visit(RegExpLiteral *ast) auto r = Reference::fromStackSlot(this); r.isReadonly = true; - _expr.setResult(r); + setExprResult(r); Instruction::MoveRegExp instr; instr.regExpId = jsUnitGenerator->registerRegExp(ast); @@ -2750,7 +2708,7 @@ bool Codegen::visit(StringLiteral *ast) auto r = Reference::fromAccumulator(this); r.isReadonly = true; - _expr.setResult(r); + setExprResult(r); Instruction::LoadRuntimeString instr; instr.stringId = registerString(ast->value.toString()); @@ -2800,7 +2758,7 @@ bool Codegen::visit(TemplateLiteral *ast) auto r = Reference::fromAccumulator(this); r.isReadonly = true; - _expr.setResult(r); + setExprResult(r); return false; } @@ -2813,10 +2771,10 @@ bool Codegen::visit(ThisExpression *) if (_context->isArrowFunction) { Reference r = referenceForName(QStringLiteral("this"), false); r.isReadonly = true; - _expr.setResult(r); + setExprResult(r); return false; } - _expr.setResult(Reference::fromThis(this)); + setExprResult(Reference::fromThis(this)); return false; } @@ -2826,7 +2784,7 @@ bool Codegen::visit(TildeExpression *ast) return false; TailCallBlocker blockTailCalls(this); - _expr.setResult(unop(Compl, expression(ast->expression))); + setExprResult(unop(Compl, expression(ast->expression))); return false; } @@ -2835,7 +2793,7 @@ bool Codegen::visit(TrueLiteral *) if (hasError) return false; - _expr.setResult(Reference::fromConst(this, QV4::Encode(true))); + setExprResult(Reference::fromConst(this, QV4::Encode(true))); return false; } @@ -2861,7 +2819,7 @@ bool Codegen::visit(TypeOfExpression *ast) Instruction::TypeofValue instr; bytecodeGenerator->addInstruction(instr); } - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -2872,7 +2830,7 @@ bool Codegen::visit(UnaryMinusExpression *ast) return false; TailCallBlocker blockTailCalls(this); - _expr.setResult(unop(UMinus, expression(ast->expression))); + setExprResult(unop(UMinus, expression(ast->expression))); return false; } @@ -2882,7 +2840,7 @@ bool Codegen::visit(UnaryPlusExpression *ast) return false; TailCallBlocker blockTailCalls(this); - _expr.setResult(unop(UPlus, expression(ast->expression))); + setExprResult(unop(UPlus, expression(ast->expression))); return false; } @@ -2895,7 +2853,7 @@ bool Codegen::visit(VoidExpression *ast) TailCallBlocker blockTailCalls(this); statement(ast->expression); - _expr.setResult(Reference::fromConst(this, Encode::undefined())); + setExprResult(Reference::fromConst(this, Encode::undefined())); return false; } @@ -2909,7 +2867,7 @@ bool Codegen::visit(FunctionDeclaration * ast) if (_functionContext->contextType == ContextType::Binding) referenceForName(ast->name.toString(), true).loadInAccumulator(); - _expr.accept(nx); + exprAccept(nx); return false; } @@ -2965,7 +2923,7 @@ bool Codegen::visit(YieldExpression *ast) done.link(); lhsValue.loadInAccumulator(); - _expr.setResult(acc); + setExprResult(acc); return false; } @@ -2976,7 +2934,7 @@ bool Codegen::visit(YieldExpression *ast) BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume); emitReturn(acc); jump.link(); - _expr.setResult(acc); + setExprResult(acc); return false; } @@ -3115,8 +3073,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bytecodeGenerator->addInstruction(yield); } - beginFunctionBodyHook(); - statementList(body); if (!hasError) { @@ -3855,8 +3811,14 @@ QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading() class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor { VolatileMemoryLocations locs; + Codegen *parent; public: + VolatileMemoryLocationScanner(Codegen *parent) : + QQmlJS::AST::Visitor(parent->recursionDepth()), + parent(parent) + {} + Codegen::VolatileMemoryLocations scan(AST::Node *s) { s->accept(this); @@ -3921,25 +3883,41 @@ public: } } + void throwRecursionDepthError() override + { + parent->throwRecursionDepthError(); + } + private: - void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const { + void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) { class Collector: public QQmlJS::AST::Visitor { + private: QVector<QStringView> &ids; + VolatileMemoryLocationScanner *parent; + public: - Collector(QVector<QStringView> &ids): ids(ids) {} - virtual bool visit(IdentifierExpression *ie) { + Collector(QVector<QStringView> &ids, VolatileMemoryLocationScanner *parent) : + QQmlJS::AST::Visitor(parent->recursionDepth()), ids(ids), parent(parent) + {} + + bool visit(IdentifierExpression *ie) final { ids.append(ie->name); return false; } + + void throwRecursionDepthError() final + { + parent->throwRecursionDepthError(); + } }; - Collector collector(ids); + Collector collector(ids, this); node->accept(&collector); } }; -Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const +Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) { - VolatileMemoryLocationScanner scanner; + VolatileMemoryLocationScanner scanner(this); return scanner.scan(ast); } @@ -4041,10 +4019,6 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const return index == other.index; case Const: return constant == other.constant; - case QmlScopeObject: - case QmlContextObject: - return qmlCoreIndex == other.qmlCoreIndex && qmlNotifyIndex == other.qmlNotifyIndex - && capturePolicy == other.capturePolicy; } return true; } @@ -4102,9 +4076,7 @@ Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const Codegen::Reference Codegen::Reference::baseObject() const { - if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) { - return Reference::fromStackSlot(codegen, qmlBase.stackSlot()); - } else if (type == Reference::Member) { + if (type == Reference::Member) { RValue rval = propertyBase; if (!rval.isValid()) return Reference::fromConst(codegen, Encode::undefined()); @@ -4189,8 +4161,6 @@ bool Codegen::Reference::storeWipesAccumulator() const case Name: case Member: case Subscript: - case QmlScopeObject: - case QmlContextObject: return true; } } @@ -4270,18 +4240,6 @@ void Codegen::Reference::storeAccumulator() const store.index = elementSubscript.stackSlot(); codegen->bytecodeGenerator->addTracingInstruction(store); } return; - case QmlScopeObject: { - Instruction::StoreScopeObjectProperty store; - store.base = qmlBase; - store.propertyIndex = qmlCoreIndex; - codegen->bytecodeGenerator->addInstruction(store); - } return; - case QmlContextObject: { - Instruction::StoreContextObjectProperty store; - store.base = qmlBase; - store.propertyIndex = qmlCoreIndex; - codegen->bytecodeGenerator->addInstruction(store); - } return; case Invalid: case Accumulator: case Const: @@ -4396,9 +4354,15 @@ QT_WARNING_POP } } if (!disable_lookups && global) { - Instruction::LoadGlobalLookup load; - load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addTracingInstruction(load); + if (qmlGlobal) { + Instruction::LoadQmlContextPropertyLookup load; + load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex()); + codegen->bytecodeGenerator->addTracingInstruction(load); + } else { + Instruction::LoadGlobalLookup load; + load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); + codegen->bytecodeGenerator->addTracingInstruction(load); + } } else { Instruction::LoadName load; load.name = nameAsIndex(); @@ -4432,24 +4396,6 @@ QT_WARNING_POP load.base = elementBase; codegen->bytecodeGenerator->addTracingInstruction(load); } return; - case QmlScopeObject: { - Instruction::LoadScopeObjectProperty load; - load.base = qmlBase; - load.propertyIndex = qmlCoreIndex; - load.captureRequired = capturePolicy == CaptureAtRuntime; - codegen->bytecodeGenerator->addInstruction(load); - if (capturePolicy == CaptureAheadOfTime) - codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); - } return; - case QmlContextObject: { - Instruction::LoadContextObjectProperty load; - load.base = qmlBase; - load.propertyIndex = qmlCoreIndex; - load.captureRequired = capturePolicy == CaptureAtRuntime; - codegen->bytecodeGenerator->addInstruction(load); - if (capturePolicy == CaptureAheadOfTime) - codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); - } return; case Invalid: break; } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 4d7001fe64..ad86483132 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -184,16 +184,31 @@ public: Member, Subscript, Import, - QmlScopeObject, - QmlContextObject, - LastLValue = QmlContextObject, + LastLValue = Import, Const } type = Invalid; bool isLValue() const { return !isReadonly && type > Accumulator; } - Reference(Codegen *cg, Type type = Invalid) : type(type), constant(0), codegen(cg) {} - Reference(): constant(0) {} + Reference(Codegen *cg, Type t = Invalid) : Reference() + { + type = t; + codegen = cg; + } + + Reference() : + constant(0), + isArgOrEval(false), + isReadonly(false), + isReferenceToConst(false), + requiresTDZCheck(false), + subscriptRequiresTDZCheck(false), + stackSlotIsLocalOrArgument(false), + isVolatile(false), + global(false), + qmlGlobal(false) + {} + Reference(const Reference &) = default; Reference(Reference &&) = default; Reference &operator =(const Reference &) = default; @@ -206,10 +221,6 @@ public: bool isValid() const { return type != Invalid; } bool loadTriggersSideEffect() const { switch (type) { - case QmlScopeObject: - return capturePolicy != DontCapture; - case QmlContextObject: - return capturePolicy != DontCapture; case Name: case Member: case Subscript: @@ -228,28 +239,6 @@ public: return isStackSlot(); } - enum PropertyCapturePolicy { - /* - We're reading a property from the scope or context object, but it's a CONSTANT property, - so we don't need to register a dependency at all. - */ - DontCapture, - /* - We're reading the property of a QObject, and we know that it's the - scope object or context object, which we know very well. Instead of registering a - property capture every time, we can do that ahead of time and then register all those - captures in one shot in registerQmlDependencies(). - */ - CaptureAheadOfTime, - /* - We're reading the property of a QObject, and we're not quite sure where - the QObject comes from or what it is. So, when reading that property at run-time, - make sure that we capture where we read that property so that if it changes we can - re-evaluate the entire expression. - */ - CaptureAtRuntime - }; - static Reference fromAccumulator(Codegen *cg) { return Reference(cg, Accumulator); } @@ -316,22 +305,6 @@ public: r.isReadonly = true; return r; } - static Reference fromQmlScopeObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) { - Reference r(base.codegen, QmlScopeObject); - r.qmlBase = base.storeOnStack().stackSlot(); - r.qmlCoreIndex = coreIndex; - r.qmlNotifyIndex = notifyIndex; - r.capturePolicy = capturePolicy; - return r; - } - static Reference fromQmlContextObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) { - Reference r(base.codegen, QmlContextObject); - r.qmlBase = base.storeOnStack().stackSlot(); - r.qmlCoreIndex = coreIndex; - r.qmlNotifyIndex = notifyIndex; - r.capturePolicy = capturePolicy; - return r; - } static Reference fromThis(Codegen *cg) { Reference r = fromStackSlot(cg, CallData::This); r.isReadonly = true; @@ -386,25 +359,21 @@ public: Moth::StackSlot elementBase; RValue elementSubscript; }; - struct { // QML scope/context object case - Moth::StackSlot qmlBase; - qint16 qmlCoreIndex; - qint16 qmlNotifyIndex; - PropertyCapturePolicy capturePolicy; - }; Moth::StackSlot property; // super property }; QString name; - mutable bool isArgOrEval = false; - bool isReadonly = false; - bool isReferenceToConst = false; - bool requiresTDZCheck = false; - bool subscriptRequiresTDZCheck = false; - bool stackSlotIsLocalOrArgument = false; - bool isVolatile = false; - bool global = false; Codegen *codegen = nullptr; + quint32 isArgOrEval:1; + quint32 isReadonly:1; + quint32 isReferenceToConst:1; + quint32 requiresTDZCheck:1; + quint32 subscriptRequiresTDZCheck:1; + quint32 stackSlotIsLocalOrArgument:1; + quint32 isVolatile:1; + quint32 global:1; + quint32 qmlGlobal:1; + private: void storeAccumulator() const; Reference doStoreOnStack(int tempIndex) const; @@ -499,6 +468,10 @@ protected: void setResult(const Reference &result) { _result = result; } + + void setResult(Reference &&result) { + _result = std::move(result); + } }; void enterContext(AST::Node *node); @@ -532,6 +505,7 @@ public: int registerGetterLookup(int nameIndex) { return jsUnitGenerator->registerGetterLookup(nameIndex); } int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); } int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); } + int registerQmlContextPropertyGetterLookup(int nameIndex) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex); } // Returns index in _module->functions virtual int defineFunction(const QString &name, AST::Node *ast, @@ -544,9 +518,22 @@ protected: void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition); - Reference expression(AST::ExpressionNode *ast); - void accept(AST::Node *node); + inline Reference expression(AST::ExpressionNode *ast) + { + if (!ast || hasError) + return Reference(); + + pushExpr(); + ast->accept(this); + return popResult(); + } + + inline void accept(AST::Node *node) + { + if (!hasError && node) + node->accept(this); + } void program(AST::Program *ast); void statementList(AST::StatementList *ast); @@ -561,12 +548,6 @@ protected: Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name); - // Hooks provided to implement QML lookup semantics - virtual bool canAccelerateGlobalLookups() const { return true; } - virtual Reference fallbackNameLookup(const QString &name); - - virtual void beginFunctionBodyHook() {} - void emitReturn(const Reference &expr); // nodes @@ -670,6 +651,11 @@ protected: bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc); virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); + void throwRecursionDepthError() override + { + throwSyntaxError(AST::SourceLocation(), + QStringLiteral("Maximum statement or expression depth exceeded")); + } public: QList<DiagnosticMessage> errors() const; @@ -684,6 +670,7 @@ public: void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject); Arguments pushTemplateArgs(AST::TemplateLiteral *args); + bool handleTaggedTemplate(Reference base, AST::TaggedTemplate *ast); void createTemplateObject(AST::TemplateLiteral *t); void setUseFastLookups(bool b) { useFastLookups = b; } @@ -714,13 +701,40 @@ public: m_globalNames = globalNames; } + static const char *s_globalNames[]; protected: friend class ScanFunctions; friend struct ControlFlow; friend struct ControlFlowCatch; friend struct ControlFlowFinally; - Result _expr; + + inline void setExprResult(const Reference &result) { m_expressions.back().setResult(result); } + inline void setExprResult(Reference &&result) { m_expressions.back().setResult(std::move(result)); } + inline Reference exprResult() const { return m_expressions.back().result(); } + + inline bool exprAccept(Format f) { return m_expressions.back().accept(f); } + + inline const Result ¤tExpr() const { return m_expressions.back(); } + + inline void pushExpr(Result &&expr) { m_expressions.push_back(std::move(expr)); } + inline void pushExpr(const Result &expr) { m_expressions.push_back(expr); } + inline void pushExpr() { m_expressions.emplace_back(); } + + inline Result popExpr() + { + const Result result = m_expressions.back(); + m_expressions.pop_back(); + return result; + } + + inline Reference popResult() { + const Reference result = m_expressions.back().result(); + m_expressions.pop_back(); + return result; + } + + std::vector<Result> m_expressions; VolatileMemoryLocations _volatileMemoryLocations; Module *_module; int _returnAddress; @@ -769,33 +783,8 @@ protected: bool _onoff; }; - class RecursionDepthCheck { - public: - RecursionDepthCheck(Codegen *cg, const AST::SourceLocation &loc) - : _cg(cg) - { -#ifdef QT_NO_DEBUG - const int depthLimit = 4000; // limit to ~1000 deep -#else - const int depthLimit = 1000; // limit to ~250 deep -#endif // QT_NO_DEBUG - - ++_cg->_recursionDepth; - if (_cg->_recursionDepth > depthLimit) - _cg->throwSyntaxError(loc, QStringLiteral("Maximum statement or expression depth exceeded")); - } - - ~RecursionDepthCheck() - { --_cg->_recursionDepth; } - - private: - Codegen *_cg; - }; - int _recursionDepth = 0; - friend class RecursionDepthCheck; - private: - VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const; + VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast); void handleConstruct(const Reference &base, AST::ArgumentList *args); }; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 5dd6fca023..e4cc9c9c5a 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -50,6 +50,8 @@ #include <private/qqmlengine_p.h> #include <private/qv4vme_moth_p.h> #include <private/qv4module_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qqmlvaluetypewrapper_p.h> #include "qv4compilationunitmapper_p.h" #include <QQmlPropertyMap> #include <QDateTime> @@ -167,6 +169,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) l->setter = QV4::Lookup::setterGeneric; else if (type == CompiledData::Lookup::Type_GlobalGetter) l->globalGetter = QV4::Lookup::globalGetterGeneric; + else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter) + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; l->nameIndex = compiledLookups[i].nameIndex; } } @@ -269,6 +273,24 @@ void CompilationUnit::unlink() propertyCaches.clear(); + if (runtimeLookups) { + for (uint i = 0; i < data->lookupTableSize; ++i) { + QV4::Lookup &l = runtimeLookups[i]; + if (l.getter == QV4::QObjectWrapper::lookupGetter) { + if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) + pc->release(); + } else if (l.getter == QQmlValueTypeWrapper::lookupGetter) { + if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache) + pc->release(); + } + + if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) { + if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) + pc->release(); + } + } + } + dependentScripts.clear(); typeNameCache = nullptr; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 726b82fea9..fe3e6bad6e 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -79,7 +79,7 @@ QT_BEGIN_NAMESPACE // Also change the comment behind the number to describe the latest change. This has the added // benefit that if another patch changes the version too, it will result in a merge conflict, and // not get removed silently. -#define QV4_DATA_STRUCTURE_VERSION 0x1c // Add trace slot to UPlus +#define QV4_DATA_STRUCTURE_VERSION 0x22 // Add trace slot to UPlus class QIODevice; class QQmlPropertyData; @@ -165,9 +165,10 @@ static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected struct Lookup { enum Type : unsigned int { - Type_Getter = 0x0, - Type_Setter = 0x1, - Type_GlobalGetter = 2 + Type_Getter = 0, + Type_Setter = 1, + Type_GlobalGetter = 2, + Type_QmlContextPropertyGetter = 3 }; union { @@ -292,29 +293,16 @@ struct Function quint16_le nRegisters; Location location; - // Qml Extensions Begin - // Array of resolved ID objects - size_t dependingIdObjectsOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } - quint16_le nDependingIdObjects; - quint16_le nDependingContextProperties; - // Array of int pairs (property index and notify index) - size_t dependingContextPropertiesOffset() const { return dependingIdObjectsOffset() + nDependingIdObjects * sizeof(quint32); } - quint16_le nDependingScopeProperties; - // Array of int pairs (property index and notify index) - size_t dependingScopePropertiesOffset() const { return dependingContextPropertiesOffset() + nDependingContextProperties * sizeof(quint32); } - // Qml Extensions End + quint32_le nLabelInfos; + size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } typedef quint16_le TraceInfoCount; TraceInfoCount nTraceInfos; static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); } - quint32_le nLabelInfos; - size_t labelInfosOffset() const { return dependingScopePropertiesOffset() + nDependingScopeProperties; } - // Keep all unaligned data at the end quint8 flags; quint8 padding1; - quint16 padding2; // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] @@ -322,9 +310,6 @@ struct Function const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); } const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); } - const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset()); } - const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset()); } - const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset()); } // --- QQmlPropertyCacheCreator interface const quint32_le *formalsBegin() const { return formalsTable(); } @@ -335,11 +320,9 @@ struct Function const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; } - inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } - - static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int labelInfoSize, int codeSize) { - int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + labelInfoSize + - 2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine); + static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) { + int trailingData = (nFormals + nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32) + + nLines*sizeof(CodeOffsetToLine); size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); Q_ASSERT(size < INT_MAX); return int(size); @@ -349,7 +332,7 @@ struct Function return (a + 7) & ~size_t(7); } }; -static_assert(sizeof(Function) == 60, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Method { enum Type { diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 0833f552e6..01c033cb2a 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -154,15 +154,19 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex) return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex) { - return registerGlobalGetterLookup(registerString(name)); + CompiledData::Lookup l; + l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter; + l.nameIndex = nameIndex; + lookups << l; + return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex) +int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex) { CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter; + l.type_and_flags = CompiledData::Lookup::Type_QmlContextPropertyGetter; l.nameIndex = nameIndex; lookups << l; return lookups.size() - 1; @@ -423,28 +427,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->nTraceInfos = irFunction->nTraceInfos; function->nRegisters = irFunction->registerCountInFunction; - function->nDependingIdObjects = 0; - function->nDependingContextProperties = 0; - function->nDependingScopeProperties = 0; - - if (!irFunction->idObjectDependencies.isEmpty()) { - function->nDependingIdObjects = irFunction->idObjectDependencies.count(); - Q_ASSERT(function->dependingIdObjectsOffset() == currentOffset); - currentOffset += function->nDependingIdObjects * sizeof(quint32); - } - - if (!irFunction->contextObjectPropertyDependencies.isEmpty()) { - function->nDependingContextProperties = irFunction->contextObjectPropertyDependencies.count(); - Q_ASSERT(function->dependingContextPropertiesOffset() == currentOffset); - currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2; - } - - if (!irFunction->scopeObjectPropertyDependencies.isEmpty()) { - function->nDependingScopeProperties = irFunction->scopeObjectPropertyDependencies.count(); - Q_ASSERT(function->dependingScopePropertiesOffset() == currentOffset); - currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2; - } - if (!irFunction->labelInfo.empty()) { function->nLabelInfos = quint32(irFunction->labelInfo.size()); Q_ASSERT(function->labelInfosOffset() == currentOffset); @@ -470,25 +452,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte // write line numbers memcpy(f + function->lineNumberOffset(), irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine)); - // write QML dependencies - quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset()); - for (int id : irFunction->idObjectDependencies) { - Q_ASSERT(id >= 0); - *writtenDeps++ = static_cast<quint32>(id); - } - - writtenDeps = (quint32_le *)(f + function->dependingContextPropertiesOffset()); - for (auto property : irFunction->contextObjectPropertyDependencies) { - *writtenDeps++ = property.key(); // property index - *writtenDeps++ = property.value(); // notify index - } - - writtenDeps = (quint32_le *)(f + function->dependingScopePropertiesOffset()); - for (auto property : irFunction->scopeObjectPropertyDependencies) { - *writtenDeps++ = property.key(); // property index - *writtenDeps++ = property.value(); // notify index - } - quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset()); for (unsigned u : irFunction->labelInfo) { *labels++ = u; @@ -690,10 +653,8 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp Context *f = module->functions.at(i); blockAndFunctionOffsets[i] = nextOffset; - const int qmlIdDepsCount = f->idObjectDependencies.count(); - const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(), - qmlIdDepsCount, qmlPropertyDepsCount, int(f->labelInfo.size()), f->code.size()); + int(f->labelInfo.size()), f->code.size()); functionSize += size - f->code.size(); nextOffset += size; } diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 2f5889ab53..49e334bb81 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -118,8 +118,8 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { int registerGetterLookup(int nameIndex); int registerSetterLookup(const QString &name); int registerSetterLookup(int nameIndex); - int registerGlobalGetterLookup(const QString &name); int registerGlobalGetterLookup(int nameIndex); + int registerQmlContextPropertyGetterLookup(int nameIndex); int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp); diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 931759fa72..7586611035 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -187,10 +187,13 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS } // ### can we relax the restrictions here? - if (c->contextType == ContextType::Eval || c->contextType == ContextType::Binding) + if (c->contextType == ContextType::Eval) return result; - result.type = ResolvedName::Global; + if (c->contextType == ContextType::Binding) + result.type = ResolvedName::QmlGlobal; + else + result.type = ResolvedName::Global; return result; } diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 5b91b93346..cb20ea9d57 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -278,11 +278,6 @@ struct Context { } }; - // Qml extension: - SmallSet<int> idObjectDependencies; - PropertyDependencyMap contextObjectPropertyDependencies; - PropertyDependencyMap scopeObjectPropertyDependencies; - Context(Context *parent, ContextType type) : parent(parent) , contextType(type) @@ -338,6 +333,7 @@ struct Context { struct ResolvedName { enum Type { Unresolved, + QmlGlobal, Global, Local, Stack, diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index e0eaa8867b..04593f202a 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -57,7 +57,8 @@ using namespace QV4::Compiler; using namespace QQmlJS::AST; ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType) - : _cg(cg) + : QQmlJS::AST::Visitor(cg->recursionDepth()) + , _cg(cg) , _sourceCode(sourceCode) , _context(nullptr) , _allowFuncDecls(true) @@ -96,25 +97,6 @@ void ScanFunctions::leaveEnvironment() _context = _contextStack.isEmpty() ? nullptr : _contextStack.top(); } -bool ScanFunctions::preVisit(Node *ast) -{ - if (_cg->hasError) - return false; - ++_recursionDepth; - - if (_recursionDepth > 1000) { - _cg->throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); - return false; - } - - return true; -} - -void ScanFunctions::postVisit(Node *) -{ - --_recursionDepth; -} - void ScanFunctions::checkDirectivePrologue(StatementList *ast) { for (StatementList *it = ast; it; it = it->next) { @@ -893,3 +875,8 @@ void ScanFunctions::calcEscapingVariables() } } } + +void ScanFunctions::throwRecursionDepthError() +{ + _cg->throwRecursionDepthError(); +} diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index 28ad846bcd..0f7bf1818a 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -96,9 +96,6 @@ protected: using Visitor::visit; using Visitor::endVisit; - bool preVisit(AST::Node *ast) override; - void postVisit(AST::Node *) override; - void checkDirectivePrologue(AST::StatementList *ast); void checkName(const QStringRef &name, const AST::SourceLocation &loc); @@ -160,6 +157,8 @@ protected: bool visit(AST::WithStatement *ast) override; void endVisit(AST::WithStatement *ast) override; + void throwRecursionDepthError() override; + protected: bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName); @@ -173,8 +172,6 @@ protected: bool _allowFuncDecls; ContextType defaultProgramType; - unsigned _recursionDepth = 0; - private: static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr; }; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index f528e1f9d3..b019f191fa 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -287,6 +287,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << index << TRACE_SLOT; MOTH_END_INSTR(LoadGlobalLookup) + MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) + d << index << TRACE_SLOT; + MOTH_END_INSTR(LoadQmlContextPropertyLookup) + MOTH_BEGIN_INSTR(StoreNameSloppy) d << name; MOTH_END_INSTR(StoreNameSloppy) @@ -328,26 +332,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << dumpRegister(property, nFormals); MOTH_END_INSTR(StoreSuperProperty) - MOTH_BEGIN_INSTR(StoreScopeObjectProperty) - d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]"; - MOTH_END_INSTR(StoreScopeObjectProperty) - - MOTH_BEGIN_INSTR(LoadScopeObjectProperty) - d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)"); - MOTH_END_INSTR(LoadScopeObjectProperty) - - MOTH_BEGIN_INSTR(StoreContextObjectProperty) - d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]"; - MOTH_END_INSTR(StoreContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadContextObjectProperty) - d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)"); - MOTH_END_INSTR(LoadContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadIdObject) - d << dumpRegister(base, nFormals) << "[" << index << "]"; - MOTH_END_INSTR(LoadIdObject) - MOTH_BEGIN_INSTR(Yield) MOTH_END_INSTR(Yield) @@ -394,15 +378,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallGlobalLookup) - MOTH_BEGIN_INSTR(CallScopeObjectProperty) - d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; - MOTH_END_INSTR(CallScopeObjectProperty) - - MOTH_BEGIN_INSTR(CallContextObjectProperty) - d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; - MOTH_END_INSTR(CallContextObjectProperty) + MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) + d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) @@ -730,14 +708,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << index; MOTH_END_INSTR(GetTemplateObject) - MOTH_BEGIN_INSTR(LoadQmlContext) - d << dumpRegister(result, nFormals); - MOTH_END_INSTR(LoadQmlContext) - - MOTH_BEGIN_INSTR(LoadQmlImportedScripts) - d << dumpRegister(result, nFormals); - MOTH_END_INSTR(LoadQmlImportedScripts) - MOTH_BEGIN_INSTR(TailCall) d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(TailCall) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index f853c1f1ab..6a8c9a9549 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -86,12 +86,11 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value) #define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot) #define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot) +#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 2, index, traceSlot) #define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) #define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot) #define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot) -#define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired) -#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) #define INSTR_Yield(op) INSTRUCTION(op, Yield, 0) #define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0) @@ -101,8 +100,6 @@ QT_BEGIN_NAMESPACE #define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) #define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property) #define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property) -#define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex) -#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex) #define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot) #define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot) #define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot) @@ -113,8 +110,7 @@ QT_BEGIN_NAMESPACE #define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot) #define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot) #define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot) -#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 5, name, base, argc, argv, traceSlot) -#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 5, name, base, argc, argv, traceSlot) +#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 4, index, argc, argv, traceSlot) #define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot) #define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) #define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv) @@ -194,7 +190,6 @@ QT_BEGIN_NAMESPACE #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) #define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot) #define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot) -#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) #define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0) @@ -228,6 +223,7 @@ QT_BEGIN_NAMESPACE F(LoadClosure) \ F(LoadName) \ F(LoadGlobalLookup) \ + F(LoadQmlContextPropertyLookup) \ F(StoreNameSloppy) \ F(StoreNameStrict) \ F(LoadElement) \ @@ -238,11 +234,6 @@ QT_BEGIN_NAMESPACE F(SetLookup) \ F(LoadSuperProperty) \ F(StoreSuperProperty) \ - F(StoreScopeObjectProperty) \ - F(StoreContextObjectProperty) \ - F(LoadScopeObjectProperty) \ - F(LoadContextObjectProperty) \ - F(LoadIdObject) \ F(ConvertThisToObject) \ F(ToObject) \ F(Jump) \ @@ -296,8 +287,7 @@ QT_BEGIN_NAMESPACE F(CallName) \ F(CallPossiblyDirectEval) \ F(CallGlobalLookup) \ - F(CallScopeObjectProperty) \ - F(CallContextObjectProperty) \ + F(CallQmlContextPropertyLookup) \ F(CallWithSpread) \ F(Construct) \ F(ConstructWithSpread) \ @@ -328,8 +318,6 @@ QT_BEGIN_NAMESPACE F(CreateMappedArgumentsObject) \ F(CreateUnmappedArgumentsObject) \ F(CreateRestParameter) \ - F(LoadQmlContext) \ - F(LoadQmlImportedScripts) \ F(Yield) \ F(YieldStar) \ F(Resume) \ diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index f6f630c749..41bc9fd140 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -142,9 +142,10 @@ types: \li qmlRegisterType() (with no parameters) registers a C++ type that is not instantiable and cannot be referred to from QML. This enables the engine to coerce any inherited types that are instantiable from QML. -\li qmlRegisterInterface() registers a Qt interface type with a specific QML -type name. The type is not instantiable from QML but can be referred to by its -type name. +\li qmlRegisterInterface() registers an existing Qt interface type. The type is +not instantiable from QML, and you cannot declare QML properties with it. Using +C++ properties of this type from QML will do the expected interface casts, +though. \li qmlRegisterUncreatableType() registers a named C++ type that is not instantiable but should be identifiable as a type to the QML type system. This is useful if a type's enums or attached properties should be accessible from QML diff --git a/src/qml/doc/src/javascript/functionlist.qdoc b/src/qml/doc/src/javascript/functionlist.qdoc index 24ff640284..7a6a922480 100644 --- a/src/qml/doc/src/javascript/functionlist.qdoc +++ b/src/qml/doc/src/javascript/functionlist.qdoc @@ -55,6 +55,8 @@ \li decodeURIComponent(encodedURIComponent) \li encodeURI(uri) \li encodeURIComponent(uriComponent) + \li escape(string) + \li unescape(string) \endlist \section2 Constructor Properties @@ -63,11 +65,20 @@ \li Object \li Function \li Array + \li ArrayBuffer \li String \li Boolean \li Number + \li DataView \li Date + \li Promise \li RegExp + \li Map + \li WeakMap + \li Set + \li WeakSet + \li SharedArrayBuffer + \li Symbol \li Error \li EvalError \li RangeError @@ -80,8 +91,11 @@ \section2 Other Properties \list + \li Atomics \li Math \li JSON + \li Reflect + \li Proxy \endlist \section1 The Object Object @@ -92,12 +106,19 @@ \list \li getPrototypeOf(O) + \li setPrototypeOf(O, P) \li getOwnPropertyDescriptor(O, P) + \li getOwnPropertyDescriptors(O) \li getOwnPropertyNames(O) + \li getOwnPropertySymbols(O) + \li assign(O [, Properties]) \li create(O [, Properties]) \li defineProperty(O, P, Attributes) \li defineProperties(O, Properties) + \li entries(O) + \li is(V1, V2) \li keys(O) + \li values(O) \li seal(O) \li isSealed(O) \li freeze(O) @@ -117,6 +138,8 @@ \li hasOwnProperty(V) \li isPrototypeOf(V) \li propertyIsEnumerable(V) + \li __defineGetter__(P, F) + \li __defineSetter__(P, F) \endlist \section1 Function Objects @@ -130,6 +153,7 @@ \li apply(thisArg, argArray) \li call(thisArg [, arg1 [, arg2, ...]]) \li bind((thisArg [, arg1 [, arg2, …]]) + \li [Symbol.hasInstance](O) \endlist \section1 Array Objects @@ -142,9 +166,14 @@ \li toString() \li toLocaleString() \li concat([item1 [, item2 [, ...]]]) + \li copyWithin([item1 [, item2 [, ...]]]) + \li entries() + \li fill(item [, index1 [, index2]]) \li join(separator) \li find(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9 \li findIndex(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9 + \li includes(item) + \li keys() \li pop() \li push([item1 [, item2 [, ...]]]) \li reverse() @@ -162,6 +191,8 @@ \li filter(callbackfn [, thisArg]) \li reduce(callbackfn [, initialValue]) \li reduceRight(callbackfn [, initialValue]) + \li values() + \li [Symbol.iterator]() \endlist \section1 String Objects @@ -175,6 +206,7 @@ \li valueOf() \li charAt(pos) \li charCodeAt(pos) + \li codePointAt(pos) \li concat([string1 [, string2 [, ...]]]) \li endsWith(searchString [, endPosition ]) // ECMAScript 6: Added in Qt 5.8 \li includes(searchString [, position ]) // ECMAScript 6: Added in 5.8 @@ -182,18 +214,23 @@ \li lastIndexOf(searchString, position) \li localeCompare(that) \li match(regexp) + \li normalize() + \li padEnd(length [, string]) + \li padStart(length [, string]) \li repeat(count) // ECMAScript 6: Added in Qt 5.9 \li replace(searchValue, replaceValue) \li search(regexp) \li slice(start, end) \li split(separator, limit) \li startsWith(searchString [, position ]) // ECMAScript 6: Added in Qt 5.8 + \li substr(start, length) \li substring(start, end) \li toLowerCase() \li toLocaleLowerCase() \li toUpperCase() \li toLocaleUpperCase() \li trim() + \li [Symbol.iterator]() \endlist Additionally, the QML engine adds the following functions to the \l String prototype: @@ -222,6 +259,7 @@ \list \li toString(radix) \li toLocaleString() + \li valueOf() \li toFixed(fractionDigits) \li toExponential(fractionDigits) \li toPrecision(precision) @@ -245,12 +283,16 @@ \li MAX_VALUE \li MIN_VALUE \li EPSILON // ECMAScript 6: Added in Qt 5.8 + \li MAX_SAFE_INTEGER + \li MIN_SAFE_INTEGER \endlist \section3 Function Properties \list \li isFinite(x) // ECMAScript 6: Added in Qt 5.8 + \li isInteger(x) + \li isSafeInteger(x) \li isNaN(x) // ECMAScript 6: Added in Qt 5.8 \endlist @@ -274,14 +316,27 @@ \list \li abs(x) \li acos(x) + \li acosh(x) \li asin(x) + \li asinh(x) \li atan(x) + \li atanh(x) \li atan2(y, x) + \li cbrt(x) \li ceil(x) + \li clz32(x) \li cos(x) + \li cosh(x) \li exp(x) + \li expm1(x) \li floor(x) + \li fround(x) + \li hypot(x, y) + \li imul(x, y) \li log(x) + \li log10(x) + \li log1p(x) + \li log2(x) \li max([value1 [, value2 [, ...]]]) \li min([value1 [, value2 [, ...]]]) \li pow(x, y) @@ -289,8 +344,11 @@ \li round(x) \li sign(x) // ECMAScript 6: Added in Qt 5.8 \li sin(x) + \li sinh(x) \li sqrt(x) \li tan(x) + \li tanh(x) + \li trunc(x) \endlist \section1 Date Objects @@ -338,11 +396,14 @@ \li setUTCDate(date) \li setMonth(month [, date]) \li setUTCMonth(month [, date]) + \li setYear(year) \li setFullYear(year [, month [, date]]) \li setUTCFullYear(year [, month [, date]]) \li toUTCString() + \li toGMTString() \li toISOString() \li toJSON() + \li [Symbol.toPrimitive](hint) \endlist Additionally, the QML engine adds the following functions to the \l Date prototype: diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index eb40f10065..c22c392b80 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -40,11 +40,10 @@ not provide a \c window object or \c{DOM API} as commonly found in a browser env Like a browser or server-side JavaScript environment, the QML runtime implements the \l{ECMA-262}{ECMAScript Language Specification} standard. This provides access to all of the built-in types and functions defined by the standard, such as Object, Array, and Math. -The QML runtime implements the 5th edition of the standard, which is the same edition commonly -implemented by browsers. +The QML runtime implements the 7th edition of the standard. The standard ECMAScript built-ins are not explicitly documented in the QML documentation. For more -information on their use, please refer to the ECMA-262 5th edition standard or one of the many online +information on their use, please refer to the ECMA-262 7th edition standard or one of the many online JavaScript reference and tutorial sites, such as the \l{W3Schools JavaScript Reference} (JavaScript Objects Reference section). Many sites focus on JavaScript in the browser, so in some cases you may need to double check the specification to determine whether a given function or object is part of standard ECMAScript or diff --git a/src/qml/doc/src/statemachine.qdoc b/src/qml/doc/src/statemachine.qdoc index 6986f1baa0..231b85af76 100644 --- a/src/qml/doc/src/statemachine.qdoc +++ b/src/qml/doc/src/statemachine.qdoc @@ -27,7 +27,7 @@ /*! \qmlmodule QtQml.StateMachine 1.\QtMinorVersion - \title Declarative State Machine QML Types + \title Qt QML State Machine QML Types \brief Provides QML types to create and execute state graphs. The following is a list of QML types provided by the module: @@ -322,7 +322,7 @@ \section1 Related Information \list - \li \l{Declarative State Machine QML Types} + \li \l{Qt QML State Machine QML Types} \li \l{The State Machine Framework} \endlist */ diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp index 1b60e96f2c..238c11f478 100644 --- a/src/qml/jit/qv4baselineassembler.cpp +++ b/src/qml/jit/qv4baselineassembler.cpp @@ -208,17 +208,20 @@ public: isNumber.link(this); } + // this converts both the lhs and the accumulator to int32 void toInt32LhsAcc(Address lhs, RegisterID lhsTarget) { load64(lhs, lhsTarget); urshift64(lhsTarget, TrustedImm32(Value::QuickType_Shift), ScratchRegister2); auto lhsIsInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2); - pushAligned(AccumulatorRegister); + const Address accumulatorStackAddress(JSStackFrameRegister, + offsetof(CallData, accumulator)); + storeAccumulator(accumulatorStackAddress); move(lhsTarget, registerForArg(0)); callHelper(toInt32Helper); move(ReturnValueRegister, lhsTarget); - popAligned(AccumulatorRegister); + loadAccumulator(accumulatorStackAddress); lhsIsInt.link(this); urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister2); @@ -498,6 +501,7 @@ public: isNumber.link(this); } + // this converts both the lhs and the accumulator to int32 void toInt32LhsAcc(Address lhs, RegisterID lhsTarget) { bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue @@ -510,32 +514,28 @@ public: auto lhsIsInt = jump(); lhsIsNotInt.link(this); - if (accumulatorNeedsSaving) { - push(AccumulatorRegisterTag); - push(AccumulatorRegisterValue); - } + + // Save accumulator from being garbage collected, no matter if we will reuse the register. + const Address accumulatorStackAddress(JSStackFrameRegister, + offsetof(CallData, accumulator)); + storeAccumulator(accumulatorStackAddress); if (ArgInRegCount < 2) { - if (!accumulatorNeedsSaving) - subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); + subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); push(lhsTarget); load32(lhs, lhsTarget); push(lhsTarget); } else { - if (accumulatorNeedsSaving) - subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); move(lhsTarget, registerForArg(1)); load32(lhs, registerForArg(0)); } callHelper(toInt32Helper); move(ReturnValueRegisterValue, lhsTarget); - if (accumulatorNeedsSaving) { - addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); - pop(AccumulatorRegisterValue); - pop(AccumulatorRegisterTag); - } else if (ArgInRegCount < 2) { + if (ArgInRegCount < 2) addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); - } + + if (accumulatorNeedsSaving) // otherwise it's still the same + loadAccumulator(accumulatorStackAddress); lhsIsInt.link(this); diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 6635ee7530..80155d7b20 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -213,6 +213,14 @@ void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(LoadGlobalLookup, CallResultDestination::InAccumulator); } +void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(index, 1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextPropertyLookup, CallResultDestination::InAccumulator); +} + void BaselineJIT::generate_StoreNameSloppy(int name) { STORE_IP(); @@ -329,61 +337,6 @@ void BaselineJIT::generate_StoreSuperProperty(int property) BASELINEJIT_GENERATE_RUNTIME_CALL(StoreSuperProperty, CallResultDestination::Ignore); } - -void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex) -{ - STORE_ACC(); - as->prepareCallWithArgCount(4); - as->passAccumulatorAsArg(3); - as->passInt32AsArg(propertyIndex, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(StoreQmlScopeObjectProperty, CallResultDestination::Ignore); -} - -void BaselineJIT::generate_StoreContextObjectProperty(int base, int propertyIndex) -{ - STORE_ACC(); - as->prepareCallWithArgCount(4); - as->passAccumulatorAsArg(3); - as->passInt32AsArg(propertyIndex, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(StoreQmlContextObjectProperty, CallResultDestination::Ignore); -} - -void BaselineJIT::generate_LoadScopeObjectProperty(int propertyIndex, int base, int captureRequired) -{ - STORE_IP(); - as->prepareCallWithArgCount(4); - as->passInt32AsArg(captureRequired, 3); - as->passInt32AsArg(propertyIndex, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlScopeObjectProperty, CallResultDestination::InAccumulator); -} - -void BaselineJIT::generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired) -{ - STORE_IP(); - as->prepareCallWithArgCount(4); - as->passInt32AsArg(captureRequired, 3); - as->passInt32AsArg(propertyIndex, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextObjectProperty, CallResultDestination::InAccumulator); -} - -void BaselineJIT::generate_LoadIdObject(int index, int base) -{ - STORE_IP(); - as->prepareCallWithArgCount(3); - as->passInt32AsArg(index, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlIdObject, CallResultDestination::InAccumulator); -} - void BaselineJIT::generate_Yield() { // ##### @@ -493,31 +446,18 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int / BASELINEJIT_GENERATE_RUNTIME_CALL(CallGlobalLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/) -{ - STORE_IP(); - as->prepareCallWithArgCount(5); - as->passInt32AsArg(argc, 4); - as->passJSSlotAsArg(argv, 3); - as->passInt32AsArg(propIdx, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlScopeObjectProperty, CallResultDestination::InAccumulator); -} - -void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv, + int /*traceSlot*/) { STORE_IP(); - as->prepareCallWithArgCount(5); - as->passInt32AsArg(argc, 4); - as->passJSSlotAsArg(argv, 3); - as->passInt32AsArg(propIdx, 2); - as->passJSSlotAsArg(base, 1); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passJSSlotAsArg(argv, 2); + as->passInt32AsArg(index, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextObjectProperty, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextPropertyLookup, CallResultDestination::InAccumulator); } - void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/) { STORE_IP(); @@ -937,22 +877,6 @@ void BaselineJIT::generate_Sub(int lhs, int /*traceSlot*/) { as->sub(lhs); } // as->checkException(); //} -void BaselineJIT::generate_LoadQmlContext(int result) -{ - as->prepareCallWithArgCount(1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContext, CallResultDestination::InAccumulator); - as->storeReg(result); -} - -void BaselineJIT::generate_LoadQmlImportedScripts(int result) -{ - as->prepareCallWithArgCount(1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlImportedScripts, CallResultDestination::InAccumulator); - as->storeReg(result); -} - void BaselineJIT::generate_InitializeBlockDeadTemporalZone(int firstReg, int count) { as->loadValue(Value::emptyValue().rawValue()); diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index df263b066b..37ab37eac2 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -97,6 +97,7 @@ public: void generate_LoadClosure(int value) override; void generate_LoadName(int name, int traceSlot) override; void generate_LoadGlobalLookup(int index, int traceSlot) override; + void generate_LoadQmlContextPropertyLookup(int index, int traceSlot) override; void generate_StoreNameSloppy(int name) override; void generate_StoreNameStrict(int name) override; void generate_LoadElement(int base, int traceSlot) override; @@ -107,15 +108,6 @@ public: void generate_SetLookup(int index, int base) override; void generate_LoadSuperProperty(int property) override; void generate_StoreSuperProperty(int property) override; - void generate_StoreScopeObjectProperty(int base, - int propertyIndex) override; - void generate_StoreContextObjectProperty(int base, - int propertyIndex) override; - void generate_LoadScopeObjectProperty(int propertyIndex, int base, - int captureRequired) override; - void generate_LoadContextObjectProperty(int propertyIndex, int base, - int captureRequired) override; - void generate_LoadIdObject(int index, int base) override; void generate_Yield() override; void generate_YieldStar() override; void generate_Resume(int) override; @@ -128,8 +120,7 @@ public: void generate_CallName(int name, int argc, int argv, int traceSlot) override; void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override; void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int traceSlot) override; - void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, int traceSlot) override; + void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override; void generate_CallWithSpread(int func, int thisObject, int argc, int argv, int traceSlot) override; void generate_TailCall(int func, int thisObject, int argc, int argv) override; void generate_Construct(int func, int argc, int argv) override; @@ -211,8 +202,6 @@ public: void generate_Div(int lhs) override; void generate_Mod(int lhs, int traceSlot) override; void generate_Sub(int lhs, int traceSlot) override; - void generate_LoadQmlContext(int result) override; - void generate_LoadQmlImportedScripts(int result) override; void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; void generate_ThrowOnNullOrUndefined() override; void generate_GetTemplateObject(int index) override; diff --git a/src/qml/jit/qv4graphbuilder.cpp b/src/qml/jit/qv4graphbuilder.cpp index 19f83ad0ec..2c073701ee 100644 --- a/src/qml/jit/qv4graphbuilder.cpp +++ b/src/qml/jit/qv4graphbuilder.cpp @@ -800,45 +800,10 @@ void GraphBuilder::generate_StoreSuperProperty(int property) env()->accumulator()); } -void GraphBuilder::generate_StoreScopeObjectProperty(int base, int propertyIndex) +void GraphBuilder::generate_LoadQmlContextPropertyLookup(int propertyIndex, int /*traceSlot*/) { - createNode(opBuilder()->get<Meta::QMLStoreScopeObjectProperty>(), - env()->slot(base), - createConstant(propertyIndex), - env()->accumulator()); -} - -void GraphBuilder::generate_StoreContextObjectProperty(int base, int propertyIndex) -{ - createNode(opBuilder()->get<Meta::QMLStoreContextObjectProperty>(), - env()->slot(base), - createConstant(propertyIndex), - env()->accumulator()); -} - -void GraphBuilder::generate_LoadScopeObjectProperty(int propertyIndex, int base, - int captureRequired) -{ - bindAcc(createNode(opBuilder()->get<Meta::QMLLoadScopeObjectProperty>(), - env()->slot(base), - createConstant(propertyIndex), - createConstant(captureRequired))); -} - -void GraphBuilder::generate_LoadContextObjectProperty(int propertyIndex, int base, - int captureRequired) -{ - bindAcc(createNode(opBuilder()->get<Meta::QMLLoadContextObjectProperty>(), - env()->slot(base), - createConstant(propertyIndex), - createConstant(captureRequired))); -} - -void GraphBuilder::generate_LoadIdObject(int index, int base) -{ - bindAcc(createNode(opBuilder()->get<Meta::QMLLoadIdObject>(), - env()->slot(base), - createConstant(index))); + bindAcc(createNode(opBuilder()->get<Meta::QMLLoadQmlContextPropertyLookup>(), + createConstant(propertyIndex))); } void GraphBuilder::generate_Yield() { Q_UNREACHABLE(); } @@ -913,22 +878,12 @@ void GraphBuilder::generate_CallGlobalLookup(int index, int argc, int argv, int finalizeCall(Meta::JSCallGlobalLookup, args, argc, argv); } -void GraphBuilder::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, - int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(base)); - args.append(createConstant(propIdx)); - finalizeCall(Meta::QMLCallScopeObjectProperty, args, argc, argv); -} - -void GraphBuilder::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, - int /*traceSlot*/) +void GraphBuilder::generate_CallQmlContextPropertyLookup(int index, int argc, int argv, + int /*traceSlot*/) { VarArgNodes args; - args.append(env()->slot(base)); - args.append(createConstant(propIdx)); - finalizeCall(Meta::QMLCallContextObjectProperty, args, argc, argv); + args.append(createConstant(index)); + finalizeCall(Meta::QMLCallQmlContextPropertyLookup, args, argc, argv); } void GraphBuilder::generate_SetUnwindHandler(int offset) @@ -1636,17 +1591,6 @@ void GraphBuilder::generate_Sub(int lhs, int /*traceSlot*/) env()->accumulator())); } -void GraphBuilder::generate_LoadQmlContext(int result) -{ - env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::QMLLoadContext>()), result); -} - -void GraphBuilder::generate_LoadQmlImportedScripts(int result) -{ - env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::QMLLoadImportedScripts>()), - result); -} - void GraphBuilder::generate_InitializeBlockDeadTemporalZone(int firstReg, int count) { for (int reg = firstReg; reg < firstReg + count; ++reg) diff --git a/src/qml/jit/qv4graphbuilder_p.h b/src/qml/jit/qv4graphbuilder_p.h index b6b2931ff0..450d8640b7 100644 --- a/src/qml/jit/qv4graphbuilder_p.h +++ b/src/qml/jit/qv4graphbuilder_p.h @@ -173,15 +173,7 @@ protected: // ByteCodeHandler void generate_SetLookup(int index, int base) override; void generate_LoadSuperProperty(int property) override; void generate_StoreSuperProperty(int property) override; - void generate_StoreScopeObjectProperty(int base, - int propertyIndex) override; - void generate_StoreContextObjectProperty(int base, - int propertyIndex) override; - void generate_LoadScopeObjectProperty(int propertyIndex, int base, - int captureRequired) override; - void generate_LoadContextObjectProperty(int propertyIndex, int base, - int captureRequired) override; - void generate_LoadIdObject(int index, int base) override; + void generate_LoadQmlContextPropertyLookup(int property, int traceSlot) override; void generate_Yield() override; void generate_YieldStar() override; void generate_Resume(int offset) override; @@ -196,10 +188,7 @@ protected: // ByteCodeHandler void generate_CallName(int name, int argc, int argv, int traceSlot) override; void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override; void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, - int traceSlot) override; - void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, - int traceSlot) override; + void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override; void generate_SetUnwindHandler(int offset) override; void generate_UnwindDispatch() override; void generate_UnwindToLabel(int level, int offset) override; @@ -284,8 +273,6 @@ protected: // ByteCodeHandler void generate_Div(int lhs) override; void generate_Mod(int lhs, int traceSlot) override; void generate_Sub(int lhs, int traceSlot) override; - void generate_LoadQmlContext(int result) override; - void generate_LoadQmlImportedScripts(int result) override; void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; void generate_ThrowOnNullOrUndefined() override; void generate_GetTemplateObject(int index) override; diff --git a/src/qml/jit/qv4operation.cpp b/src/qml/jit/qv4operation.cpp index 8356a35098..d10f8f19ac 100644 --- a/src/qml/jit/qv4operation.cpp +++ b/src/qml/jit/qv4operation.cpp @@ -223,11 +223,7 @@ inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *stat case K::JSDeleteName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); case K::JSIn: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); case K::JSInstanceOf: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::QMLLoadScopeObjectProperty: return get(3, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::QMLStoreScopeObjectProperty: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::QMLLoadContextObjectProperty: return get(3, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::QMLStoreContextObjectProperty: return get(3, 1, 1, 1, 1, 2, none, F::CanThrow); - case K::QMLLoadIdObject: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); + case K::QMLLoadQmlContextPropertyLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); case K::JSEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); case K::JSGreaterThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); @@ -263,8 +259,6 @@ inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *stat case K::JSTypeofValue: return get(1, 0, 0, 1, 0, 0, any, F::Pure); case K::JSDeclareVar: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); case K::JSDestructureRestElement: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::QMLLoadContext: return get(0, 0, 0, 1, 0, 0, any, F::NoFlags); - case K::QMLLoadImportedScripts: return get(0, 0, 0, 1, 0, 0, any, F::NoFlags); case K::JSCreateCallContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags); case K::JSCreateCatchContext: return get(2, 1, 1, 1, 1, 1, none, F::NoFlags); @@ -518,18 +512,12 @@ static ReturnValue operateOnRuntimeCall(Operation::Kind kind, bool abortOnMissin case K::JSDeleteName: return M<R::DeleteName>::doIt(); case K::JSIn: return M<R::In>::doIt(); case K::JSInstanceOf: return M<R::Instanceof>::doIt(); - case K::QMLLoadScopeObjectProperty: return M<R::LoadQmlScopeObjectProperty>::doIt(); - case K::QMLStoreScopeObjectProperty: return M<R::StoreQmlScopeObjectProperty>::doIt(); - case K::QMLLoadContextObjectProperty: return M<R::LoadQmlContextObjectProperty>::doIt(); - case K::QMLStoreContextObjectProperty: return M<R::StoreQmlContextObjectProperty>::doIt(); - case K::QMLLoadIdObject: return M<R::LoadQmlIdObject>::doIt(); + case K::QMLLoadQmlContextPropertyLookup: return M<R::LoadQmlContextPropertyLookup>::doIt(); case K::JSTypeofName: return M<R::TypeofName>::doIt(); case K::JSTypeofValue: return M<R::TypeofValue>::doIt(); case K::JSDeclareVar: return M<R::DeclareVar>::doIt(); case K::JSDestructureRestElement: return M<R::DestructureRestElement>::doIt(); - case K::QMLLoadContext: return M<R::LoadQmlContext>::doIt(); - case K::QMLLoadImportedScripts: return M<R::LoadQmlImportedScripts>::doIt(); case K::JSThisToObject: return M<R::ConvertThisToObject>::doIt(); case K::JSCreateMappedArgumentsObject: return M<R::CreateMappedArgumentsObject>::doIt(); case K::JSCreateUnmappedArgumentsObject: return M<R::CreateUnmappedArgumentsObject>::doIt(); diff --git a/src/qml/jit/qv4operation_p.h b/src/qml/jit/qv4operation_p.h index 8b66e58d4b..43214023e8 100644 --- a/src/qml/jit/qv4operation_p.h +++ b/src/qml/jit/qv4operation_p.h @@ -123,12 +123,10 @@ enum OpKind: uint16_t { JSDeleteName, JSIn, JSInstanceOf, - /* ok, these are qml object ops, but we don't care for now and treat them a s JS */ - QMLLoadScopeObjectProperty, - QMLStoreScopeObjectProperty, - QMLLoadContextObjectProperty, - QMLStoreContextObjectProperty, - QMLLoadIdObject, + + /* ok, these are qml object ops, but we don't care for now and treat them as JS */ + QMLLoadQmlContextPropertyLookup, + QMLCallQmlContextPropertyLookup, JSEqual, JSGreaterThan, @@ -168,16 +166,11 @@ enum OpKind: uint16_t { JSCreateClass, JSConstruct, JSConstructWithSpread, - /* ok, these are qml vararg calls, but we don't care for now and treat them as JS */ - QMLCallScopeObjectProperty, - QMLCallContextObjectProperty, JSTypeofName, JSTypeofValue, JSDeclareVar, JSDestructureRestElement, - QMLLoadContext, - QMLLoadImportedScripts, JSThisToObject, JSCreateMappedArgumentsObject, JSCreateUnmappedArgumentsObject, diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index c04617dac0..8a849ddf5f 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1189,6 +1189,14 @@ ReturnedValue ExecutionEngine::throwTypeError(const QString &message) return throwError(error); } +ReturnedValue ExecutionEngine::throwReferenceError(const QString &name) +{ + Scope scope(this); + QString msg = name + QLatin1String(" is not defined"); + ScopedObject error(scope, newReferenceErrorObject(msg)); + return throwError(error); +} + ReturnedValue ExecutionEngine::throwReferenceError(const Value &value) { Scope scope(this); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 3735c24601..6df4545014 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -572,6 +572,7 @@ public: ReturnedValue throwTypeError(); ReturnedValue throwTypeError(const QString &message); ReturnedValue throwReferenceError(const Value &value); + ReturnedValue throwReferenceError(const QString &name); ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int lineNumber, int column); ReturnedValue throwRangeError(const Value &value); ReturnedValue throwRangeError(const QString &message); diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 7702939e23..1bd4329fe8 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -96,7 +96,6 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, , codeData(function->code()) , jittedCode(nullptr) , codeRef(nullptr) - , hasQmlDependencies(function->hasQmlDependencies()) { Scope scope(engine); Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 374e46b929..f8125a58f8 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -94,7 +94,6 @@ public: Heap::InternalClass *internalClass; uint nFormals; int interpreterCallCount = 0; - bool hasQmlDependencies; bool isEval = false; static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index ddb8542e07..9906e2b1a0 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -257,11 +257,15 @@ void InternalClass::init(Heap::InternalClass *other) void InternalClass::destroy() { -#ifndef QT_NO_DEBUG for (const auto &t : transitions) { - Q_ASSERT(!t.lookup || !t.lookup->isMarked()); - } + if (t.lookup) { +#ifndef QT_NO_DEBUG + Q_ASSERT(t.lookup->parent == this); #endif + t.lookup->parent = nullptr; + } + } + if (parent && parent->engine && parent->isMarked()) parent->removeChildEntry(this); @@ -659,8 +663,6 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack) Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b); if (ic->prototype) ic->prototype->mark(stack); - if (ic->parent) - ic->parent->mark(stack); ic->nameMap.mark(stack); } diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 1b6cdcbd14..c2c3fa0474 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -69,37 +69,7 @@ void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto) ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object) { - Heap::Object *obj = object->d(); - PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - if (name.isArrayIndex()) { - indexedLookup.index = name.asArrayIndex(); - getter = getterIndexed; - return getter(this, engine, *object); - } - - auto index = obj->internalClass->findValueOrGetter(name); - if (index.isValid()) { - PropertyAttributes attrs = index.attrs; - uint nInline = obj->vtable()->nInlineProperties; - if (attrs.isData()) { - if (index.index < obj->vtable()->nInlineProperties) { - index.index += obj->vtable()->inlinePropertyOffset; - getter = getter0Inline; - } else { - index.index -= nInline; - getter = getter0MemberData; - } - } else { - getter = getterAccessor; - } - objectLookup.ic = obj->internalClass; - objectLookup.offset = index.index; - return getter(this, engine, *object); - } - - protoLookup.protoId = obj->internalClass->protoId; - resolveProtoGetter(name, obj->prototype()); - return getter(this, engine, *object); + return object->resolveLookupGetter(engine, this); } ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object) @@ -409,7 +379,7 @@ ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Va ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object) { - if (object.type() == l->primitiveLookup.type) { + if (object.type() == l->primitiveLookup.type && !object.isObject()) { Heap::Object *o = l->primitiveLookup.proto; if (l->primitiveLookup.protoId == o->internalClass->protoId) return l->primitiveLookup.data->asReturnedValue(); @@ -420,7 +390,7 @@ ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, c ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object) { - if (object.type() == l->primitiveLookup.type) { + if (object.type() == l->primitiveLookup.type && !object.isObject()) { Heap::Object *o = l->primitiveLookup.proto; if (l->primitiveLookup.protoId == o->internalClass->protoId) { const Value *getter = l->primitiveLookup.data; @@ -473,56 +443,7 @@ ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engi bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value) { - Scope scope(engine); - ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - - Heap::InternalClass *c = object->internalClass(); - PropertyKey key = name->toPropertyKey(); - auto idx = c->findValueOrSetter(key); - if (idx.isValid()) { - if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) { - Q_ASSERT(!idx.attrs.isAccessor()); - setter = arrayLengthSetter; - return setter(this, engine, *object, value); - } else if (idx.attrs.isData() && idx.attrs.isWritable()) { - objectLookup.ic = object->internalClass(); - objectLookup.index = idx.index; - const auto nInline = object->d()->vtable()->nInlineProperties; - if (idx.index < nInline) { - setter = Lookup::setter0Inline; - objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset; - } else { - setter = Lookup::setter0MemberData; - objectLookup.offset = idx.index - nInline; - } - return setter(this, engine, *object, value); - } else { - // ### handle setter - setter = setterFallback; - } - return setter(this, engine, *object, value); - } - - insertionLookup.protoId = c->protoId; - if (!object->put(key, value)) { - setter = Lookup::setterFallback; - return false; - } - - if (object->internalClass() == c) { - // ### setter in the prototype, should handle this - setter = setterFallback; - return true; - } - idx = object->internalClass()->findValueOrSetter(key); - if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen? - setter = setterFallback; - return false; - } - insertionLookup.newClass = object->internalClass(); - insertionLookup.offset = idx.index; - setter = setterInsert; - return true; + return object->resolveLookupSetter(engine, this, value); } bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index bfe2354427..7309749a81 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -68,8 +68,10 @@ struct Lookup { union { ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); + ReturnedValue (*qmlContextPropertyGetter)(Lookup *l, ExecutionEngine *engine, Value *thisObject); bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; + // NOTE: gc assumes the first two entries in the struct are pointers to heap objects or null union { struct { Heap::Base *h1; @@ -119,6 +121,39 @@ struct Lookup { uint index; uint unused; } indexedLookup; + struct { + Heap::InternalClass *ic; + Heap::QObjectWrapper *staticQObject; + QQmlPropertyCache *propertyCache; + QQmlPropertyData *propertyData; + } qobjectLookup; + struct { + Heap::InternalClass *ic; + quintptr unused; + QQmlPropertyCache *propertyCache; + QQmlPropertyData *propertyData; + } qgadgetLookup; + struct { + quintptr unused1; + quintptr unused2; + int scriptIndex; + } qmlContextScriptLookup; + struct { + Heap::Object *singleton; + quintptr unused; + } qmlContextSingletonLookup; + struct { + quintptr unused1; + quintptr unused2; + int objectId; + } qmlContextIdObjectLookup; + struct { + // Same as protoLookup, as used for global lookups + quintptr reserved1; + quintptr reserved2; + quintptr reserved3; + ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine); + } qmlContextGlobalLookup; }; uint nameIndex; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 3d2d54f651..efab9a6454 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -320,8 +320,11 @@ bool Object::virtualDeleteProperty(Managed *m, PropertyKey id) PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) { if (arrayIndex != UINT_MAX && o->arrayData()) { - if (!arrayIndex) - arrayNode = o->sparseBegin(); + SparseArrayNode *arrayNode = nullptr; + if (o->arrayType() == Heap::ArrayData::Sparse) { + SparseArray *sparse = o->arrayData()->sparse; + arrayNode = arrayIndex ? sparse->lowerBound(arrayIndex) : sparse->begin(); + } // sparse arrays if (arrayNode) { @@ -339,7 +342,6 @@ PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, Pr *attrs = a; return PropertyKey::fromArrayIndex(k); } - arrayNode = nullptr; arrayIndex = UINT_MAX; } // dense arrays @@ -734,6 +736,95 @@ ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &v return checkedInstanceOf(engine, function, var); } +ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) +{ + Heap::Object *obj = object->d(); + PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); + if (name.isArrayIndex()) { + lookup->indexedLookup.index = name.asArrayIndex(); + lookup->getter = Lookup::getterIndexed; + return lookup->getter(lookup, engine, *object); + } + + auto index = obj->internalClass->findValueOrGetter(name); + if (index.isValid()) { + PropertyAttributes attrs = index.attrs; + uint nInline = obj->vtable()->nInlineProperties; + if (attrs.isData()) { + if (index.index < obj->vtable()->nInlineProperties) { + index.index += obj->vtable()->inlinePropertyOffset; + lookup->getter = Lookup::getter0Inline; + } else { + index.index -= nInline; + lookup->getter = Lookup::getter0MemberData; + } + } else { + lookup->getter = Lookup::getterAccessor; + } + lookup->objectLookup.ic = obj->internalClass; + lookup->objectLookup.offset = index.index; + return lookup->getter(lookup, engine, *object); + } + + lookup->protoLookup.protoId = obj->internalClass->protoId; + lookup->resolveProtoGetter(name, obj->prototype()); + return lookup->getter(lookup, engine, *object); +} + +bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value) +{ + Scope scope(engine); + ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); + + Heap::InternalClass *c = object->internalClass(); + PropertyKey key = name->toPropertyKey(); + auto idx = c->findValueOrSetter(key); + if (idx.isValid()) { + if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) { + Q_ASSERT(!idx.attrs.isAccessor()); + lookup->setter = Lookup::arrayLengthSetter; + return lookup->setter(lookup, engine, *object, value); + } else if (idx.attrs.isData() && idx.attrs.isWritable()) { + lookup->objectLookup.ic = object->internalClass(); + lookup->objectLookup.index = idx.index; + const auto nInline = object->d()->vtable()->nInlineProperties; + if (idx.index < nInline) { + lookup->setter = Lookup::setter0Inline; + lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset; + } else { + lookup->setter = Lookup::setter0MemberData; + lookup->objectLookup.offset = idx.index - nInline; + } + return lookup->setter(lookup, engine, *object, value); + } else { + // ### handle setter + lookup->setter = Lookup::setterFallback; + } + return lookup->setter(lookup, engine, *object, value); + } + + lookup->insertionLookup.protoId = c->protoId; + if (!object->put(key, value)) { + lookup->setter = Lookup::setterFallback; + return false; + } + + if (object->internalClass() == c) { + // ### setter in the prototype, should handle this + lookup->setter = Lookup::setterFallback; + return true; + } + idx = object->internalClass()->findValueOrSetter(key); + if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen? + lookup->setter = Lookup::setterFallback; + return false; + } + lookup->insertionLookup.newClass = object->internalClass(); + lookup->insertionLookup.offset = idx.index; + lookup->setter = Lookup::setterInsert; + return true; +} + ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var) { Scope scope(engine); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 72b6703554..567382cbc0 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -375,6 +375,11 @@ public: bool setProtoFromNewTarget(const Value *newTarget); + ReturnedValue resolveLookupGetter(ExecutionEngine *engine, Lookup *lookup) const + { return vtable()->resolveLookupGetter(this, engine, lookup); } + ReturnedValue resolveLookupSetter(ExecutionEngine *engine, Lookup *lookup, const Value &value) + { return vtable()->resolveLookupSetter(this, engine, lookup, value); } + protected: static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); @@ -389,6 +394,8 @@ protected: static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); static qint64 virtualGetLength(const Managed *m); static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var); + static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); + static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); public: // qv4runtime uses this directly static ReturnedValue checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *typeObject, const Value &var); @@ -408,7 +415,6 @@ struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator uint arrayIndex = 0; uint memberIndex = 0; bool iterateOverSymbols = false; - SparseArrayNode *arrayNode = nullptr; ~ObjectOwnPropertyKeyIterator() override = default; PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 88b0822f42..97b955632d 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -55,6 +55,8 @@ #include <private/qjsvalue_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qv4module_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4identifiertable_p.h> QT_BEGIN_NAMESPACE @@ -77,14 +79,11 @@ void Heap::QQmlContextWrapper::destroy() Object::destroy(); } -ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) +ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup) { - Q_ASSERT(m->as<QQmlContextWrapper>()); - if (!id.isString()) - return Object::virtualGet(m, id, receiver, hasProperty); + return Object::virtualGet(resource, id, receiver, hasProperty); - const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m); QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); @@ -100,11 +99,11 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c } } - return Object::virtualGet(m, id, receiver, hasProperty); + return Object::virtualGet(resource, id, receiver, hasProperty); } bool hasProp = false; - ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp)); + ScopedValue result(scope, Object::virtualGet(resource, id, receiver, &hasProp)); if (hasProp) { if (hasProperty) *hasProperty = hasProp; @@ -160,6 +159,8 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c // Note: The scope object is only a QADMO for example when somebody registers a QQmlPropertyMap // sub-class as QML type and then instantiates it in .qml. if (scopeObject && QQmlPropertyCache::isDynamicMetaObject(scopeObject->metaObject())) { + // all bets are off, so don't try to optimize any lookups + lookup = nullptr; if (performGobalLookUp()) return result->asReturnedValue(); } @@ -172,11 +173,35 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c if (hasProperty) *hasProperty = true; if (r.scriptIndex != -1) { + if (lookup) { + lookup->qmlContextScriptLookup.scriptIndex = r.scriptIndex; + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScript; + return lookup->qmlContextPropertyGetter(lookup, v4, base); + } QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); if (scripts) return scripts->get(r.scriptIndex); return QV4::Encode::null(); } else if (r.type.isValid()) { + if (lookup) { + if (r.type.isSingleton()) { + QQmlEngine *e = v4->qmlEngine(); + QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo(); + siinfo->init(e); + if (siinfo->qobjectApi(e)) { + lookup->qmlContextSingletonLookup.singleton = + static_cast<Heap::Object*>( + Value::fromReturnedValue( + QQmlTypeWrapper::create(v4, nullptr, r.type) + ).heapObject()); + } else { + QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); + lookup->qmlContextSingletonLookup.singleton = o->d(); + } + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton; + return lookup->qmlContextPropertyGetter(lookup, v4, base); + } + } return QQmlTypeWrapper::create(v4, scopeObject, r.type); } else if (r.importNamespace) { return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); @@ -188,6 +213,15 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c } QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine()); + Lookup * const originalLookup = lookup; + + decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty; + + // minor optimization so we don't potentially try two property lookups on the same object + if (scopeObject == context->contextObject) { + scopeObject = nullptr; + contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty; + } while (context) { // Search context properties @@ -198,11 +232,17 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c if (propertyIdx != -1) { if (propertyIdx < context->idValueCount) { + if (hasProperty) + *hasProperty = true; + + if (lookup) { + lookup->qmlContextIdObjectLookup.objectId = propertyIdx; + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupIdObject; + return lookup->qmlContextPropertyGetter(lookup, v4, base); + } if (ep->propertyCapture) ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings); - if (hasProperty) - *hasProperty = true; return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]); } else { @@ -229,11 +269,30 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c // Search scope object if (scopeObject) { bool hasProp = false; + + QQmlPropertyData *propertyData = nullptr; QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject, - name, QV4::QObjectWrapper::CheckRevision, &hasProp)); + name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData)); if (hasProp) { if (hasProperty) *hasProperty = true; + if (base) + *base = QV4::QObjectWrapper::wrap(v4, scopeObject); + + if (lookup && propertyData) { + QQmlData *ddata = QQmlData::get(scopeObject, false); + if (ddata && ddata->propertyCache) { + ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject))); + const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue()); + lookup->qobjectLookup.ic = That->internalClass(); + lookup->qobjectLookup.staticQObject = nullptr; + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = propertyData; + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty; + } + } + return result->asReturnedValue(); } } @@ -243,27 +302,73 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c // Search context object if (context->contextObject) { bool hasProp = false; - result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, &hasProp); + QQmlPropertyData *propertyData = nullptr; + result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, + name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData); if (hasProp) { if (hasProperty) *hasProperty = true; + if (base) + *base = QV4::QObjectWrapper::wrap(v4, context->contextObject); + + if (lookup && propertyData) { + QQmlData *ddata = QQmlData::get(context->contextObject, false); + if (ddata && ddata->propertyCache) { + ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject))); + const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue()); + lookup->qobjectLookup.ic = That->internalClass(); + lookup->qobjectLookup.staticQObject = nullptr; + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = propertyData; + lookup->qmlContextPropertyGetter = contextGetterFunction; + } + } + return result->asReturnedValue(); } } context = context->parent; + + // As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond + // the immediate QML context (of the .qml file). + lookup = nullptr; } // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming // true if we access properties of the global object. - if (performGobalLookUp()) - return result->asReturnedValue(); + if (originalLookup) { + // Try a lookup in the global object. It's theoretically possible to first find a property + // in the global object and then later a context property with the same name is added, but that + // never really worked as we used to detect access to global properties at type compile time anyway. + lookup = originalLookup; + result = lookup->resolveGlobalGetter(v4); + if (lookup->globalGetter != Lookup::globalGetterGeneric) { + if (hasProperty) + *hasProperty = true; + lookup->qmlContextGlobalLookup.getterTrampoline = lookup->globalGetter; + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject; + return result->asReturnedValue(); + } + lookup->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + } else { + if (performGobalLookUp()) + return result->asReturnedValue(); + } expressionContext->unresolvedNames = true; return Encode::undefined(); } +ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) +{ + Q_ASSERT(m->as<QQmlContextWrapper>()); + const QQmlContextWrapper *This = static_cast<const QQmlContextWrapper *>(m); + return getPropertyAndBase(This, id, receiver, hasProperty, /*base*/nullptr); +} + bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { Q_ASSERT(m->as<QQmlContextWrapper>()); @@ -298,8 +403,16 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val while (context) { const QV4::IdentifierHash &properties = context->propertyNames(); // Search context properties - if (properties.count() && properties.value(name) != -1) - return false; + if (properties.count()) { + const int propertyIndex = properties.value(name); + if (propertyIndex != -1) { + if (propertyIndex < context->idValueCount) { + v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue")); + return false; + } + return false; + } + } // Search scope object if (scopeObject && @@ -323,6 +436,146 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val return false; } +ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Scope scope(engine); + PropertyKey name =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit-> + runtimeStrings[l->nameIndex]); + + // Special hack for bounded signal expressions, where the parameters of signals are injected + // into the handler expression through the locals of the call context. So for onClicked: { ... } + // the parameters of the clicked signal are injected and we must allow for them to be found here + // before any other property from the QML context. + ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); + if (ctx.d()->type == Heap::ExecutionContext::Type_CallContext) { + uint index = ctx.d()->internalClass->indexOfValueOrGetter(name); + if (index < UINT_MAX) + return static_cast<Heap::CallContext*>(ctx.d())->locals[index].asReturnedValue(); + } + + Scoped<QQmlContextWrapper> qmlContext(scope, engine->qmlContext()->qml()); + bool hasProperty = false; + ScopedValue result(scope, QQmlContextWrapper::getPropertyAndBase(qmlContext, name, /*receiver*/nullptr, + &hasProperty, base, l)); + if (!hasProperty) + return engine->throwReferenceError(name.toQString()); + return result->asReturnedValue(); +} + +ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base) + Scope scope(engine); + Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::null(); + + QQmlContextData *context = qmlContext->qmlContext(); + if (!context) + return QV4::Encode::null(); + + QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); + if (!scripts) + return QV4::Encode::null(); + return scripts->get(l->qmlContextScriptLookup.scriptIndex); +} + +ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(engine) + Q_UNUSED(base) + return Value::fromHeapObject(l->qmlContextSingletonLookup.singleton).asReturnedValue(); +} + +ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base) + Scope scope(engine); + Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::null(); + + QQmlContextData *context = qmlContext->qmlContext(); + if (!context) + return QV4::Encode::null(); + + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine->qmlEngine()); + const int objectId = l->qmlContextIdObjectLookup.objectId; + + if (qmlEngine->propertyCapture) + qmlEngine->propertyCapture->captureProperty(&context->idValues[objectId].bindings); + + return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]); +} + +ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base) + Scope scope(engine); + Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::undefined(); + + QObject *scopeObject = qmlContext->qmlScope(); + if (!scopeObject) + return QV4::Encode::undefined(); + + if (QQmlData::wasDeleted(scopeObject)) + return QV4::Encode::undefined(); + + const auto revertLookup = [l, engine, base]() { + l->qobjectLookup.propertyCache->release(); + l->qobjectLookup.propertyCache = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); + }; + + ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, scopeObject)); + return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); +} + +ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base) + Scope scope(engine); + Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::undefined(); + + QQmlContextData *context = qmlContext->qmlContext(); + if (!context) + return QV4::Encode::undefined(); + + QObject *contextObject = context->contextObject; + if (!contextObject) + return QV4::Encode::undefined(); + + if (QQmlData::wasDeleted(contextObject)) + return QV4::Encode::undefined(); + + const auto revertLookup = [l, engine, base]() { + l->qobjectLookup.propertyCache->release(); + l->qobjectLookup.propertyCache = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); + }; + + ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, contextObject)); + return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); +} + +ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base); + ReturnedValue result = l->qmlContextGlobalLookup.getterTrampoline(l, engine); + // In the unlikely event of mutation of the global object, update the trampoline. + if (l->qmlContextPropertyGetter != lookupInGlobalObject) { + l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter; + l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject; + } + return result; +} + void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index dd6de3323d..6375294375 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -99,8 +99,18 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object inline QObject *getScopeObject() const { return d()->scopeObject; } inline QQmlContextData *getContext() const { return *d()->context; } + static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, + bool *hasProperty, Value *base, Lookup *lookup = nullptr); static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + + static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 15f064ba7a..d85e8c94eb 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -56,6 +56,8 @@ #include <private/qv4functionobject_p.h> #include <private/qv4runtime_p.h> #include <private/qv4variantobject_p.h> +#include <private/qv4identifiertable_p.h> +#include <private/qv4lookup_p.h> #if QT_CONFIG(qml_sequence_object) #include <private/qv4sequenceobject_p.h> @@ -231,7 +233,7 @@ QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject return result; } -ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) +ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property) { QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex())); @@ -257,7 +259,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr; - if (captureRequired && ep && ep->propertyCapture && !property->isConstant()) + if (ep && ep->propertyCapture && !property->isConstant()) ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); if (property->isVarProperty()) { @@ -269,9 +271,53 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje } } +static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr) +{ + int index = 0; + if (name->equals(v4->id_destroy())) + index = QV4::QObjectMethod::DestroyMethod; + else if (name->equals(v4->id_toString())) + index = QV4::QObjectMethod::ToStringMethod; + else + return OptionalReturnedValue(); + + if (hasProperty) + *hasProperty = true; + ExecutionContext *global = v4->rootContext(); + return OptionalReturnedValue(QV4::QObjectMethod::create(global, qobj, index)); +} + +static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, QQmlContextData *qmlContext, QObject *qobj, + bool *hasProperty = nullptr) +{ + if (!qmlContext || !qmlContext->imports) + return OptionalReturnedValue(); + + QQmlTypeNameCache::Result r = qmlContext->imports->query(name); + + if (hasProperty) + *hasProperty = true; + + if (!r.isValid()) + return OptionalReturnedValue(); + + if (r.scriptIndex != -1) { + return OptionalReturnedValue(QV4::Encode::undefined()); + } else if (r.type.isValid()) { + return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums)); + } else if (r.importNamespace) { + return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj, qmlContext->imports, r.importNamespace, + Heap::QQmlTypeWrapper::ExcludeEnums)); + } + Q_UNREACHABLE(); + return OptionalReturnedValue(); +} + ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports) const { + // Keep this code in sync with ::virtualResolveLookupGetter + if (QQmlData::wasDeleted(d()->object())) { if (hasProperty) *hasProperty = false; @@ -280,39 +326,17 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String ExecutionEngine *v4 = engine(); - if (name->equals(v4->id_destroy()) || name->equals(v4->id_toString())) { - int index = name->equals(v4->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; - if (hasProperty) - *hasProperty = true; - ExecutionContext *global = v4->rootContext(); - return QV4::QObjectMethod::create(global, d()->object(), index); - } + if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty)) + return *methodValue; QQmlPropertyData local; QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local); if (!result) { + // Check for attached properties if (includeImports && name->startsWithUpper()) { - // Check for attached properties - if (qmlContext && qmlContext->imports) { - QQmlTypeNameCache::Result r = qmlContext->imports->query(name); - - if (hasProperty) - *hasProperty = true; - - if (r.isValid()) { - if (r.scriptIndex != -1) { - return QV4::Encode::undefined(); - } else if (r.type.isValid()) { - return QQmlTypeWrapper::create(v4, d()->object(), - r.type, Heap::QQmlTypeWrapper::ExcludeEnums); - } else if (r.importNamespace) { - return QQmlTypeWrapper::create(v4, d()->object(), - qmlContext->imports, r.importNamespace, Heap::QQmlTypeWrapper::ExcludeEnums); - } - Q_ASSERT(!"Unreachable"); - } - } + if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty)) + return *importProperty; } return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty); } @@ -333,27 +357,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String return getProperty(v4, d()->object(), result); } -ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired) -{ - if (QQmlData::wasDeleted(object)) - return QV4::Encode::null(); - QQmlData *ddata = QQmlData::get(object, /*create*/false); - if (!ddata) - return QV4::Encode::undefined(); - - if (Q_UNLIKELY(!ddata->propertyCache)) { - ddata->propertyCache = QQmlEnginePrivate::get(engine)->cache(object->metaObject()); - ddata->propertyCache->addref(); - } - - 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(engine, object, property, captureRequired); -} - -ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty) +ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property) { if (QQmlData::wasDeleted(object)) { if (hasProperty) @@ -361,13 +365,8 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC return QV4::Encode::null(); } - if (name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) { - int index = name->equals(engine->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; - if (hasProperty) - *hasProperty = true; - ExecutionContext *global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, index); - } + if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty)) + return *methodValue; QQmlData *ddata = QQmlData::get(object, false); QQmlPropertyData local; @@ -385,6 +384,9 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC if (hasProperty) *hasProperty = true; + if (property) + *property = result; + return getProperty(engine, object, result); } else { // Check if this object is already wrapped. @@ -829,6 +831,71 @@ OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m, return new QObjectWrapperOwnPropertyKeyIterator; } +ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) +{ + // Keep this code in sync with ::getQmlProperty + PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit-> + runtimeStrings[lookup->nameIndex]); + if (!id.isString()) + return Object::virtualResolveLookupGetter(object, engine, lookup); + Scope scope(engine); + + const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object); + ScopedString name(scope, id.asStringOrSymbol()); + QQmlContextData *qmlContext = engine->callingQmlContext(); + + QObject * const qobj = This->d()->object(); + + if (QQmlData::wasDeleted(qobj)) + return QV4::Encode::undefined(); + + if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) + return *methodValue; + + QQmlData *ddata = QQmlData::get(qobj, false); + if (!ddata || !ddata->propertyCache) { + QQmlPropertyData local; + QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local); + return getProperty(engine, qobj, property); + } + QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext); + + if (!property) { + // Check for attached properties + if (name->startsWithUpper()) { + if (auto importProperty = getPropertyFromImports(engine, name, qmlContext, qobj)) + return *importProperty; + } + return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); + } + + lookup->qobjectLookup.ic = This->internalClass(); + lookup->qobjectLookup.staticQObject = nullptr; + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = property; + lookup->getter = QV4::QObjectWrapper::lookupGetter; + return lookup->getter(lookup, engine, *object); +} + +ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [lookup, engine, &object]() { + lookup->qobjectLookup.propertyCache->release(); + lookup->qobjectLookup.propertyCache = nullptr; + lookup->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(lookup, engine, object); + }; + + return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup); +} + +bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, + const Value &value) +{ + return Object::virtualResolveLookupSetter(object, engine, lookup, value); +} + namespace QV4 { struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase @@ -1923,13 +1990,13 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in return method.asReturnedValue(); } -ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index) +ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index) { Scope valueScope(scope); Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope)); - method->d()->setPropertyCache(valueType->d()->propertyCache()); + method->d()->setPropertyCache(valueType->propertyCache()); method->d()->index = index; - method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d()); + method->d()->valueTypeWrapper.set(valueScope.engine, valueType); return method.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 43a53ac673..795bf241f2 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -60,6 +60,7 @@ #include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> +#include <private/qv4lookup_p.h> QT_BEGIN_NAMESPACE @@ -165,7 +166,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object QObject *object() const { return d()->object(); } ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, bool includeImports = false) const; - static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr); + static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr); static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); @@ -174,13 +175,18 @@ struct Q_QML_EXPORT QObjectWrapper : public Object using Object::get; - static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired); static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value); void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value); void destroyObject(bool lastCall); - static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true); + static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property); + + static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); + static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); + template <typename ReversalFunctor> static ReturnedValue lookupGetterImpl(Lookup *l, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revert); + static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); + protected: static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value); @@ -216,6 +222,47 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje return wrap_slowPath(engine, object); } +template <typename ReversalFunctor> +inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup) +{ + // we can safely cast to a QV4::Object here. If object is something else, + // the internal class won't match + Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); + if (!o || o->internalClass != lookup->qobjectLookup.ic) + return revertLookup(); + + const Heap::QObjectWrapper *This = lookup->qobjectLookup.staticQObject ? lookup->qobjectLookup.staticQObject : + static_cast<const Heap::QObjectWrapper *>(o); + QObject *qobj = This->object(); + if (QQmlData::wasDeleted(qobj)) + return QV4::Encode::undefined(); + + QQmlData *ddata = QQmlData::get(qobj, /*create*/false); + if (!ddata) + return revertLookup(); + + QQmlPropertyData *property = lookup->qobjectLookup.propertyData; + if (ddata->propertyCache != lookup->qobjectLookup.propertyCache) { + if (property->isOverridden() && (!useOriginalProperty || property->isFunction() || property->isSignalHandler())) + return revertLookup(); + + QQmlPropertyCache *fromMo = ddata->propertyCache; + QQmlPropertyCache *toMo = lookup->qobjectLookup.propertyCache; + bool canConvert = false; + while (fromMo) { + if (fromMo == toMo) { + canConvert = true; + break; + } + fromMo = fromMo->parent(); + } + if (!canConvert) + return revertLookup(); + } + + return getProperty(engine, qobj, property); +} + struct QQmlValueTypeWrapper; struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject @@ -226,7 +273,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject enum { DestroyMethod = -1, ToStringMethod = -2 }; static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index); - static ReturnedValue create(QV4::ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index); + static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index); int methodIndex() const { return d()->index; } QObject *object() const { return d()->object(); } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 9753ee4b1d..de46d046c9 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1130,6 +1130,12 @@ ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function return l->globalGetter(l, engine); } +ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index) +{ + Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + return l->qmlContextPropertyGetter(l, engine, nullptr); +} + ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index) { Lookup *l = f->compilationUnit->runtimeLookups + index; @@ -1390,18 +1396,43 @@ uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const return v->booleanValue(); } +static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName) +{ + QString objectAsString = QStringLiteral("[null]"); + if (!thisObject->isUndefined()) + objectAsString = thisObject->toQStringNoThrow(); + QString msg = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(propertyName, objectAsString); + return engine->throwTypeError(msg); +} ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc) { + Scope scope(engine); Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; Value function = Value::fromReturnedValue(l->globalGetter(l, engine)); + Value thisObject = Value::undefinedValue(); if (!function.isFunctionObject()) - return engine->throwTypeError(); + return throwPropertyIsNotAFunctionTypeError(engine, &thisObject, + engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); - Value thisObject = Value::undefinedValue(); return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc); } +ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index, + Value *argv, int argc) +{ + Scope scope(engine); + ScopedValue thisObject(scope); + Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject)); + if (!function.isFunctionObject()) + return throwPropertyIsNotAFunctionTypeError(engine, thisObject, + engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); + + return static_cast<FunctionObject &>(function).call(thisObject, argv, argc); +} + ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc) { Scope scope(engine); @@ -1412,13 +1443,8 @@ ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Val if (engine->hasException) return Encode::undefined(); - if (!function) { - QString objectAsString = QStringLiteral("[null]"); - if (!thisObject->isUndefined()) - objectAsString = thisObject->toQStringNoThrow(); - QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString); - return engine->throwTypeError(msg); - } + if (!function) + return throwPropertyIsNotAFunctionTypeError(engine, thisObject, QLatin1String("eval")); if (function->d() == engine->evalFunction()->d()) return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true); @@ -1437,15 +1463,9 @@ ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Va if (engine->hasException) return Encode::undefined(); - if (!f) { - QString objectAsString = QStringLiteral("[null]"); - if (!thisObject->isUndefined()) - objectAsString = thisObject->toQStringNoThrow(); - QString msg = QStringLiteral("Property '%1' of object %2 is not a function") - .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), - objectAsString); - return engine->throwTypeError(msg); - } + if (!f) + return throwPropertyIsNotAFunctionTypeError(engine, thisObject, + engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString()); return f->call(thisObject, argv, argc); } @@ -1536,41 +1556,6 @@ ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Val return static_cast<const FunctionObject &>(func).call(&thisObject, argv, argc); } -ReturnedValue Runtime::CallQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &base, - int propertyIndex, Value argv[], int argc) -{ - Scope scope(engine); - ScopedFunctionObject fo(scope, LoadQmlScopeObjectProperty::call(engine, base, propertyIndex, - /*captureRequired*/true)); - if (!fo) { - QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex); - return engine->throwTypeError(error); - } - - QObject *qmlScopeObj = static_cast<const QmlContext *>(&base)->d()->qml()->scopeObject; - ScopedValue qmlScopeValue(scope, QObjectWrapper::wrap(engine, qmlScopeObj)); - return fo->call(qmlScopeValue, argv, argc); -} - -ReturnedValue Runtime::CallQmlContextObjectProperty::call(ExecutionEngine *engine, - const Value &base, - int propertyIndex, - Value argv[], - int argc) -{ - Scope scope(engine); - ScopedFunctionObject fo(scope, LoadQmlContextObjectProperty::call(engine, base, propertyIndex, - /*captureRequired*/true)); - if (!fo) { - QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex); - return engine->throwTypeError(error); - } - - QObject *qmlContextObj = static_cast<const QmlContext *>(&base)->d()->qml()->context->contextData()->contextObject; - ScopedValue qmlContextValue(scope, QObjectWrapper::wrap(engine, qmlContextObj)); - return fo->call(qmlContextValue, argv, argc); -} - struct CallArgs { Value *argv; int argc; @@ -2013,66 +1998,12 @@ QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, i return engine->newArrayObject(values, nValues)->asReturnedValue(); } - -ReturnedValue Runtime::LoadQmlContext::call(ExecutionEngine *engine) -{ - Heap::QmlContext *ctx = engine->qmlContext(); - Q_ASSERT(ctx); - return ctx->asReturnedValue(); -} - ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id) { Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>()); return ro->asReturnedValue(); } -ReturnedValue Runtime::LoadQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired) -{ - const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::getProperty(engine, c.d()->qml()->scopeObject, propertyIndex, captureRequired); -} - -ReturnedValue Runtime::LoadQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired) -{ - const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, captureRequired); -} - -ReturnedValue Runtime::LoadQmlIdObject::call(ExecutionEngine *engine, const Value &c, uint index) -{ - const QmlContext &qmlContext = static_cast<const QmlContext &>(c); - QQmlContextData *context = *qmlContext.d()->qml()->context; - if (!context || index >= (uint)context->idValueCount) - return Encode::undefined(); - - QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr; - if (ep && ep->propertyCapture) - ep->propertyCapture->captureProperty(&context->idValues[index].bindings); - - return QObjectWrapper::wrap(engine, context->idValues[index].data()); -} - -void Runtime::StoreQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) -{ - const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::setProperty(engine, c.d()->qml()->scopeObject, propertyIndex, value); -} - -void Runtime::StoreQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) -{ - const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, value); -} - -ReturnedValue Runtime::LoadQmlImportedScripts::call(ExecutionEngine *engine) -{ - QQmlContextData *context = engine->callingQmlContext(); - if (!context) - return Encode::undefined(); - return context->importedScripts.value(); -} - ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj) { if (obj.isObject()) diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 0312522d90..86cbccde23 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -58,7 +58,6 @@ namespace QV4 { typedef uint Bool; - struct Q_QML_PRIVATE_EXPORT Runtime { typedef ReturnedValue (*UnaryOperation)(const Value &value); typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right); @@ -87,6 +86,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime { { static ReturnedValue call(ExecutionEngine *, uint, Value[], int); }; + struct Q_QML_PRIVATE_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, uint, Value[], int); + }; struct Q_QML_PRIVATE_EXPORT CallName : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, int, Value[], int); @@ -187,6 +190,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime { { static ReturnedValue call(ExecutionEngine *, Function *, int); }; + struct Q_QML_PRIVATE_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, uint); + }; struct Q_QML_PRIVATE_EXPORT GetLookup : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, Function *, const Value &, int); @@ -495,45 +502,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime { static ReturnedValue call(Function *, int); }; - /* qml */ - struct Q_QML_PRIVATE_EXPORT LoadQmlContext : Method<Throws::No> - { - static ReturnedValue call(ExecutionEngine *); - }; - struct Q_QML_PRIVATE_EXPORT LoadQmlImportedScripts : Method<Throws::No> - { - static ReturnedValue call(ExecutionEngine *); - }; - struct Q_QML_PRIVATE_EXPORT LoadQmlScopeObjectProperty : Method<Throws::Yes> - { - static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool); - }; - struct Q_QML_PRIVATE_EXPORT LoadQmlContextObjectProperty : Method<Throws::Yes> - { - static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool); - }; - struct Q_QML_PRIVATE_EXPORT LoadQmlIdObject : Method<Throws::Yes> - { - static ReturnedValue call(ExecutionEngine *, const Value &, uint); - }; - struct Q_QML_PRIVATE_EXPORT CallQmlScopeObjectProperty : Method<Throws::Yes> - { - static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int); - }; - struct Q_QML_PRIVATE_EXPORT CallQmlContextObjectProperty : Method<Throws::Yes> - { - static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int); - }; - - struct Q_QML_PRIVATE_EXPORT StoreQmlScopeObjectProperty : Method<Throws::Yes> - { - static void call(ExecutionEngine *, const Value &, int, const Value &); - }; - struct Q_QML_PRIVATE_EXPORT StoreQmlContextObjectProperty : Method<Throws::Yes> - { - static void call(ExecutionEngine *, const Value &, int, const Value &); - }; - struct StackOffsets { static const int tailCall_function = -1; static const int tailCall_thisObject = -2; diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h index be66dc57ca..006a6a3cde 100644 --- a/src/qml/jsruntime/qv4runtimecodegen_p.h +++ b/src/qml/jsruntime/qv4runtimecodegen_p.h @@ -71,6 +71,7 @@ public: void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override; void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override; + private: ExecutionEngine *engine; }; diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 8186153ba4..dee6a67792 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -130,7 +130,7 @@ PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Prope return PropertyKey::fromArrayIndex(index); } else if (arrayIndex == slen) { if (s->arrayData()) { - arrayNode = s->sparseBegin(); + SparseArrayNode *arrayNode = s->sparseBegin(); // iterate until we're past the end of the string while (arrayNode && arrayNode->key() < slen) arrayNode = arrayNode->nextNode(); diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index b1cae8796f..da08841026 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -877,6 +877,22 @@ struct ValueArray { // have wrong offsets between host and target. Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8); +class OptionalReturnedValue { + ReturnedValue value; +public: + + OptionalReturnedValue() : value(Value::emptyValue().asReturnedValue()) {} + explicit OptionalReturnedValue(ReturnedValue v) + : value(v) + { + Q_ASSERT(!Value::fromReturnedValue(v).isEmpty()); + } + + ReturnedValue operator->() const { return value; } + ReturnedValue operator*() const { return value; } + explicit operator bool() const { return !Value::fromReturnedValue(value).isEmpty(); } +}; + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index e3cdc4552a..98e4f4f7b9 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -669,6 +669,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadGlobalLookup) + MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) + STORE_IP(); + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + acc = l->qmlContextPropertyGetter(l, engine, nullptr); + CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); + MOTH_END_INSTR(LoadQmlContextPropertyLookup) + MOTH_BEGIN_INSTR(StoreNameStrict) STORE_IP(); STORE_ACC(); @@ -719,7 +727,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(GetLookup) STORE_IP(); STORE_ACC(); + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + + if (accumulator.isNullOrUndefined()) { + QString message = QStringLiteral("Cannot read property '%1' of %2") + .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()) + .arg(accumulator.toQStringNoThrow()); + acc = engine->throwTypeError(message); + goto handleUnwind; + } + acc = l->getter(l, engine, accumulator); CHECK_EXCEPTION; traceValue(acc, function, traceSlot); @@ -755,37 +773,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, CHECK_EXCEPTION; MOTH_END_INSTR(StoreSuperProperty) - MOTH_BEGIN_INSTR(StoreScopeObjectProperty) - STORE_ACC(); - Runtime::StoreQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator); - CHECK_EXCEPTION; - MOTH_END_INSTR(StoreScopeObjectProperty) - - MOTH_BEGIN_INSTR(LoadScopeObjectProperty) - STORE_IP(); - acc = Runtime::LoadQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadScopeObjectProperty) - - MOTH_BEGIN_INSTR(StoreContextObjectProperty) - STORE_IP(); - STORE_ACC(); - Runtime::StoreQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator); - CHECK_EXCEPTION; - MOTH_END_INSTR(StoreContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadContextObjectProperty) - STORE_IP(); - acc = Runtime::LoadQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadIdObject) - STORE_IP(); - acc = Runtime::LoadQmlIdObject::call(engine, STACK_VALUE(base), index); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadIdObject) - MOTH_BEGIN_INSTR(Yield) frame->yield = code; frame->yieldIsIterator = false; @@ -852,11 +839,23 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(CallPropertyLookup) STORE_IP(); Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex; + + if (stack[base].isNullOrUndefined()) { + QString message = QStringLiteral("Cannot call method '%1' of %2") + .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()) + .arg(stack[base].toQStringNoThrow()); + acc = engine->throwTypeError(message); + goto handleUnwind; + } + // ok to have the value on the stack here Value f = Value::fromReturnedValue(l->getter(l, engine, stack[base])); if (Q_UNLIKELY(!f.isFunctionObject())) { - acc = engine->throwTypeError(); + QString message = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()) + .arg(stack[base].toQStringNoThrow()); + acc = engine->throwTypeError(message); goto handleUnwind; } @@ -893,19 +892,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallGlobalLookup) - MOTH_BEGIN_INSTR(CallScopeObjectProperty) + MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) STORE_IP(); - acc = Runtime::CallQmlScopeObjectProperty::call(engine, stack[base], name, stack + argv, argc); + acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc); CHECK_EXCEPTION; traceValue(acc, function, traceSlot); - MOTH_END_INSTR(CallScopeObjectProperty) - - MOTH_BEGIN_INSTR(CallContextObjectProperty) - STORE_IP(); - acc = Runtime::CallQmlContextObjectProperty::call(engine, stack[base], name, stack + argv, argc); - CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); - MOTH_END_INSTR(CallContextObjectProperty) + MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) STORE_IP(); @@ -1520,14 +1512,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, #endif // QT_CONFIG(qml_debug) MOTH_END_INSTR(Debug) - MOTH_BEGIN_INSTR(LoadQmlContext) - STACK_VALUE(result) = Runtime::LoadQmlContext::call(engine); - MOTH_END_INSTR(LoadQmlContext) - - MOTH_BEGIN_INSTR(LoadQmlImportedScripts) - STACK_VALUE(result) = Runtime::LoadQmlImportedScripts::call(engine); - MOTH_END_INSTR(LoadQmlImportedScripts) - handleUnwind: Q_ASSERT(engine->hasException || frame->unwindLevel); if (!frame->unwindHandler) { diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h index 00dcb962d3..a4d91640c5 100644 --- a/src/qml/jsruntime/qv4vtable_p.h +++ b/src/qml/jsruntime/qv4vtable_p.h @@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct Lookup; + struct OwnPropertyKeyIterator { virtual ~OwnPropertyKeyIterator() = 0; virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0; @@ -84,6 +86,9 @@ struct VTable typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget); + typedef ReturnedValue (*ResolveLookupGetter)(const Object *, ExecutionEngine *, Lookup *); + typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &); + const VTable * const parent; quint16 inlinePropertyOffset; quint16 nInlineProperties; @@ -118,6 +123,9 @@ struct VTable Call call; CallAsConstructor callAsConstructor; + + ResolveLookupGetter resolveLookupGetter; + ResolveLookupSetter resolveLookupSetter; }; @@ -142,6 +150,9 @@ protected: static constexpr VTable::Call virtualCall = nullptr; static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr; + + static constexpr VTable::ResolveLookupGetter virtualResolveLookupGetter = nullptr; + static constexpr VTable::ResolveLookupSetter virtualResolveLookupSetter = nullptr; }; #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ @@ -181,6 +192,9 @@ protected: \ classname::virtualCall, \ classname::virtualCallAsConstructor, \ + \ + classname::virtualResolveLookupGetter, \ + classname::virtualResolveLookupSetter \ } #define DEFINE_MANAGED_VTABLE(classname) \ diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index b86dba6daa..8ae51a795f 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -614,16 +614,8 @@ bool Parser::parse(int startToken) program = 0; do { - if (++tos == stack_size) { + if (++tos == stack_size) reallocateStack(); - if (stack_size > 10000) { - // We're now in some serious right-recursive stuff, which will probably result in - // an AST that's so deep that recursively visiting it will run out of stack space. - const QString msg = QCoreApplication::translate("QQmlParser", "Maximum statement or expression depth exceeded"); - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); - return false; - } - } state_stack[tos] = action; diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 4ebb2d3b5c..54a1200493 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -65,21 +65,6 @@ ClassExpression *asAnonymousClassDefinition(Node *n) return c; } - -void Node::accept(Visitor *visitor) -{ - if (visitor->preVisit(this)) { - accept0(visitor); - } - visitor->postVisit(this); -} - -void Node::accept(Node *node, Visitor *visitor) -{ - if (node) - node->accept(visitor); -} - ExpressionNode *Node::expressionCast() { return nullptr; diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 43aeec6525..e84c62af2f 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -271,11 +271,29 @@ public: virtual FunctionExpression *asFunctionDefinition(); virtual ClassExpression *asClassDefinition(); - void accept(Visitor *visitor); - static void accept(Node *node, Visitor *visitor); + inline void accept(Visitor *visitor) + { + Visitor::RecursionDepthCheck recursionCheck(visitor); + if (recursionCheck()) { + if (visitor->preVisit(this)) + accept0(visitor); + visitor->postVisit(this); + } else { + visitor->throwRecursionDepthError(); + } + } + inline static void accept(Node *node, Visitor *visitor) + { + if (node) + node->accept(visitor); + } + + // ### Remove when we can. This is part of the qmldevtools library, though. inline static void acceptChild(Node *node, Visitor *visitor) - { return accept(node, visitor); } // ### remove + { + return accept(node, visitor); + } virtual void accept0(Visitor *visitor) = 0; virtual SourceLocation firstSourceLocation() const = 0; diff --git a/src/qml/parser/qqmljsastvisitor.cpp b/src/qml/parser/qqmljsastvisitor.cpp index eec151298e..666623eecc 100644 --- a/src/qml/parser/qqmljsastvisitor.cpp +++ b/src/qml/parser/qqmljsastvisitor.cpp @@ -43,7 +43,7 @@ QT_QML_BEGIN_NAMESPACE namespace QQmlJS { namespace AST { -Visitor::Visitor() +Visitor::Visitor(quint16 parentRecursionDepth) : m_recursionDepth(parentRecursionDepth) { } diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h index c925096de6..9c69f88e0c 100644 --- a/src/qml/parser/qqmljsastvisitor_p.h +++ b/src/qml/parser/qqmljsastvisitor_p.h @@ -61,7 +61,33 @@ namespace QQmlJS { namespace AST { class QML_PARSER_EXPORT Visitor { public: - Visitor(); + class RecursionDepthCheck + { + Q_DISABLE_COPY(RecursionDepthCheck) + public: + RecursionDepthCheck(RecursionDepthCheck &&) = delete; + RecursionDepthCheck &operator=(RecursionDepthCheck &&) = delete; + + RecursionDepthCheck(Visitor *visitor) : m_visitor(visitor) + { + ++(m_visitor->m_recursionDepth); + } + + ~RecursionDepthCheck() + { + --(m_visitor->m_recursionDepth); + } + + bool operator()() const { + return m_visitor->m_recursionDepth < s_recursionLimit; + } + + private: + static const quint16 s_recursionLimit = 4096; + Visitor *m_visitor; + }; + + Visitor(quint16 parentRecursionDepth = 0); virtual ~Visitor(); virtual bool preVisit(Node *) { return true; } @@ -374,6 +400,14 @@ public: virtual bool visit(DebuggerStatement *) { return true; } virtual void endVisit(DebuggerStatement *) {} + + virtual void throwRecursionDepthError() = 0; + + quint16 recursionDepth() const { return m_recursionDepth; } + +protected: + quint16 m_recursionDepth = 0; + friend class RecursionDepthCheck; }; } } // namespace AST diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 05a9f70247..bf9330856d 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -584,7 +584,6 @@ namespace QtQml { const QMetaObject *, bool create); #ifndef Q_QDOC } -#endif QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wheader-hygiene") @@ -594,6 +593,8 @@ using namespace QtQml; QT_WARNING_POP +#endif // Q_QDOC + //The C++ version of protected namespaces in qmldir Q_QML_EXPORT bool qmlProtectModule(const char* uri, int majVersion); Q_QML_EXPORT void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor); diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index a949df4968..b164517011 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -260,8 +260,6 @@ protected: } else { clearError(); } - - cancelPermanentGuards(); } ep->dereferenceScarceResources(); @@ -643,24 +641,22 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const if (!m_target.data()) return dependencies; - for (const auto &guardList : { permanentGuards, activeGuards }) { - for (QQmlJavaScriptExpressionGuard *guard = guardList.first(); guard; guard = guardList.next(guard)) { - if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*. - continue; + for (QQmlJavaScriptExpressionGuard *guard = activeGuards.first(); guard; guard = activeGuards.next(guard)) { + if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*. + continue; - QObject *senderObject = guard->senderAsObject(); - if (!senderObject) - continue; + QObject *senderObject = guard->senderAsObject(); + if (!senderObject) + continue; - const QMetaObject *senderMeta = senderObject->metaObject(); - if (!senderMeta) - continue; + const QMetaObject *senderMeta = senderObject->metaObject(); + if (!senderMeta) + continue; - for (int i = 0; i < senderMeta->propertyCount(); i++) { - QMetaProperty property = senderMeta->property(i); - if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) { - dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name()))); - } + for (int i = 0; i < senderMeta->propertyCount(); i++) { + QMetaProperty property = senderMeta->property(i); + if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) { + dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name()))); } } } @@ -670,7 +666,7 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const bool QQmlBinding::hasDependencies() const { - return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured(); + return !activeGuards.isEmpty() || translationsCaptured(); } class QObjectPointerBinding: public QQmlNonbindingBinding diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 90c2c1fb96..454bd3abfb 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1471,7 +1471,7 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl); Q_ASSERT(!localFileOrQrc.isEmpty()); - QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, importUri)); + const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1); if (!typeLoader->directoryExists(dir)) { if (!isImplicitImport) { QQmlError error; diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 380163202a..9a3a5218e0 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -109,7 +109,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() } clearActiveGuards(); - clearPermanentGuards(); clearError(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = nullptr; @@ -118,12 +117,8 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) { activeGuards.setFlagValue(v); - permanentGuards.setFlagValue(v); - if (!v) { + if (!v) clearActiveGuards(); - clearPermanentGuards(); - m_permanentDependenciesRegistered = false; - } } void QQmlJavaScriptExpression::resetNotifyOnValueChanged() @@ -216,10 +211,6 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())); QV4::Scope scope(v4); QV4::ScopedValue result(scope, res); - if (v4Function->hasQmlDependencies) { - QV4::Heap::QmlContext *qc = m_qmlScope.as<QV4::QmlContext>()->d(); - QQmlPropertyCapture::registerQmlDependencies(qc, v4, v4Function->compiledFunction); - } if (scope.hasException()) { if (watcher.wasDeleted()) @@ -254,7 +245,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b return result->asReturnedValue(); } -void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) +void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) { if (watcher->wasDeleted()) return; @@ -274,17 +265,14 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) g->connect(n); } - if (duration == Permanently) - expression->permanentGuards.prepend(g); - else - expression->activeGuards.prepend(g); + expression->activeGuards.prepend(g); } /*! \internal \a n is in the signal index range (see QObjectPrivate::signalIndex()). */ -void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration, bool doNotify) +void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, bool doNotify) { if (watcher->wasDeleted()) return; @@ -323,61 +311,8 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration dur g->connect(o, n, engine, doNotify); } - if (duration == Permanently) - expression->permanentGuards.prepend(g); - else - expression->activeGuards.prepend(g); - } -} - -void QQmlPropertyCapture::registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction) -{ - // Let the caller check and avoid the function call :) - Q_ASSERT(compiledFunction->hasQmlDependencies()); - - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine()); - if (!ep) - return; - QQmlPropertyCapture *capture = ep->propertyCapture; - if (!capture || capture->watcher->wasDeleted()) - return; - - if (capture->expression->m_permanentDependenciesRegistered) - return; - - capture->expression->m_permanentDependenciesRegistered = true; - - QV4::Heap::QQmlContextWrapper *wrapper = context->qml(); - QQmlContextData *qmlContext = wrapper->context->contextData(); - - const quint32_le *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); - const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; - for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) { - Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount); - capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings, - QQmlPropertyCapture::Permanently); - } - - Q_ASSERT(qmlContext->contextObject); - const quint32_le *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, - QQmlPropertyCapture::Permanently); - } - - QObject *scopeObject = wrapper->scopeObject; - const quint32_le *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, - QQmlPropertyCapture::Permanently); + expression->activeGuards.prepend(g); } - } QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const @@ -471,13 +406,6 @@ void QQmlJavaScriptExpression::clearActiveGuards() g->Delete(); } -void QQmlJavaScriptExpression::clearPermanentGuards() -{ - m_permanentDependenciesRegistered = false; - while (QQmlJavaScriptExpressionGuard *g = permanentGuards.takeFirst()) - g->Delete(); -} - void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **) { QQmlJavaScriptExpression *expression = diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index de3fba0774..453c8ab8a8 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -144,7 +144,6 @@ public: QQmlError error(QQmlEngine *) const; void clearError(); void clearActiveGuards(); - void clearPermanentGuards(); QQmlDelayedError *delayedError(); static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope, @@ -153,14 +152,6 @@ public: protected: void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line); - void cancelPermanentGuards() const - { - if (m_permanentDependenciesRegistered) { - for (QQmlJavaScriptExpressionGuard *it = permanentGuards.first(); it; it = permanentGuards.next(it)) - it->cancelNotify(); - } - } - void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f); void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit); @@ -169,7 +160,6 @@ protected: // activeGuards:flag2 - useSharedContext QBiPointer<QObject, DeleteWatcher> m_scopeObject; QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards; - QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> permanentGuards; void setTranslationsCaptured(bool captured) { m_error.setFlagValue(captured); } bool translationsCaptured() const { return m_error.flag(); } @@ -186,7 +176,6 @@ private: QQmlContextData *m_context; QQmlJavaScriptExpression **m_prevExpression; QQmlJavaScriptExpression *m_nextExpression; - bool m_permanentDependenciesRegistered = false; QV4::PersistentValue m_qmlScope; QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; @@ -204,14 +193,8 @@ public: Q_ASSERT(errorString == nullptr); } - enum Duration { - OnlyOnce, - Permanently - }; - - static void registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction); - void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce); - void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true); + void captureProperty(QQmlNotifier *); + void captureProperty(QObject *, int, int, bool doNotify = true); void captureTranslation() { translationCaptured = true; } QQmlEngine *engine; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 3ec828ea2d..6b977df453 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -48,6 +48,8 @@ #include <private/qv4functionobject_p.h> #include <private/qv4objectproto_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4identifiertable_p.h> +#include <private/qv4lookup_p.h> QT_BEGIN_NAMESPACE @@ -170,6 +172,7 @@ static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *n ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { + // Keep this code in sync with ::virtualResolveLookupGetter Q_ASSERT(m->as<QQmlTypeWrapper>()); if (!id.isString()) @@ -426,6 +429,64 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType)); } +ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) +{ + // Keep this code in sync with ::virtualGet + PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); + if (!id.isString()) + return Object::virtualResolveLookupGetter(object, engine, lookup); + Scope scope(engine); + + const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object); + ScopedString name(scope, id.asStringOrSymbol()); + QQmlContextData *qmlContext = engine->callingQmlContext(); + + Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This)); + QQmlType type = w->d()->type(); + + if (type.isValid()) { + + if (type.isSingleton()) { + QQmlEngine *e = engine->qmlEngine(); + QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); + siinfo->init(e); + + QObject *qobjectSingleton = siinfo->qobjectApi(e); + if (qobjectSingleton) { + + const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + if (!includeEnums || !name->startsWithUpper()) { + QQmlData *ddata = QQmlData::get(qobjectSingleton, false); + if (ddata && ddata->propertyCache) { + ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); + QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); + if (property) { + lookup->qobjectLookup.ic = This->internalClass(); + lookup->qobjectLookup.staticQObject = static_cast<Heap::QObjectWrapper *>(val->heapObject()); + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = property; + lookup->getter = QV4::QObjectWrapper::lookupGetter; + return lookup->getter(lookup, engine, *This); + } + // Fall through to base implementation + } + // Fall through to base implementation + } + // Fall through to base implementation + } + // Fall through to base implementation + } + // Fall through to base implementation + } + return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); +} + +bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value) +{ + return Object::virtualResolveLookupSetter(object, engine, lookup, value); +} + void Heap::QQmlScopedEnumWrapper::destroy() { QQmlType::derefHandle(typePrivate); diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index bc615e0f6c..c797a4ac10 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -111,6 +111,9 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlRefPointer<QQmlTypeNameCache> &, const QQmlImportRef *, Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums); + static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); + static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); + protected: static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 9ce1c82f09..7df5757b95 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -51,6 +51,8 @@ #include <private/qv4stackframe_p.h> #include <private/qv4objectiterator_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4identifiertable_p.h> +#include <private/qv4lookup_p.h> #include <QtCore/qloggingcategory.h> QT_BEGIN_NAMESPACE @@ -372,6 +374,117 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con return Encode(b->engine()->newString(result)); } +Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine, + Heap::QQmlValueTypeWrapper *valueTypeWrapper, + QQmlPropertyData *property) +{ + if (property->isFunction()) { + // calling a Q_INVOKABLE function of a value type + return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, property->coreIndex()); + } + +#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ + if (property->propType() == metatype) { \ + cpptype v; \ + void *args[] = { &v, nullptr }; \ + metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), \ + QMetaObject::ReadProperty, index, args); \ + return QV4::Encode(constructor(v)); \ + } + + const QMetaObject *metaObject = valueTypeWrapper->propertyCache()->metaObject(); + + int index = property->coreIndex(); + QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index); + + // These four types are the most common used by the value type wrappers + VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal); + VALUE_TYPE_LOAD(QMetaType::Int || property->isEnum(), int, int); + VALUE_TYPE_LOAD(QMetaType::Int, int, int); + VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString); + VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool); + + QVariant v; + void *args[] = { nullptr, nullptr }; + if (property->propType() == QMetaType::QVariant) { + args[0] = &v; + } else { + v = QVariant(property->propType(), static_cast<void *>(nullptr)); + args[0] = v.data(); + } + metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), QMetaObject::ReadProperty, + index, args); + return engine->fromVariant(v); +#undef VALUE_TYPE_LOAD +} + +ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, + Lookup *lookup) +{ + PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit-> + runtimeStrings[lookup->nameIndex]); + if (!id.isString()) + return Object::virtualResolveLookupGetter(object, engine, lookup); + + const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object); + QV4::ExecutionEngine *v4 = r->engine(); + Scope scope(v4); + ScopedString name(scope, id.asStringOrSymbol()); + + // Note: readReferenceValue() can change the reference->type. + if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { + if (!reference->readReferenceValue()) + return Value::undefinedValue().asReturnedValue(); + } + + QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr); + if (!result) + return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); + + lookup->qgadgetLookup.ic = r->internalClass(); + lookup->qgadgetLookup.propertyCache = r->d()->propertyCache(); + lookup->qgadgetLookup.propertyCache->addref(); + lookup->qgadgetLookup.propertyData = result; + lookup->getter = QQmlValueTypeWrapper::lookupGetter; + return lookup->getter(lookup, engine, *object); +} + +ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [lookup, engine, &object]() { + lookup->qgadgetLookup.propertyCache->release(); + lookup->qgadgetLookup.propertyCache = nullptr; + lookup->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(lookup, engine, object); + }; + + // we can safely cast to a QV4::Object here. If object is something else, + // the internal class won't match + Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); + if (!o || o->internalClass != lookup->qgadgetLookup.ic) + return revertLookup(); + + Heap::QQmlValueTypeWrapper *valueTypeWrapper = + const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o)); + if (valueTypeWrapper->propertyCache() != lookup->qgadgetLookup.propertyCache) + return revertLookup(); + + if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) { + Scope scope(engine); + Scoped<QQmlValueTypeReference> referenceWrapper(scope, valueTypeWrapper); + referenceWrapper->readReferenceValue(); + } + + QQmlPropertyData *property = lookup->qgadgetLookup.propertyData; + return getGadgetProperty(engine, valueTypeWrapper, property); +} + +bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, + const Value &value) +{ + return Object::virtualResolveLookupSetter(object, engine, lookup, value); +} + ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as<QQmlValueTypeWrapper>()); @@ -397,43 +510,7 @@ ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, if (hasProperty) *hasProperty = true; - if (result->isFunction()) - // calling a Q_INVOKABLE function of a value type - return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex()); - -#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ - if (result->propType() == metatype) { \ - cpptype v; \ - void *args[] = { &v, 0 }; \ - metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args); \ - return QV4::Encode(constructor(v)); \ - } - - const QMetaObject *metaObject = r->d()->propertyCache()->metaObject(); - - int index = result->coreIndex(); - QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index); - - void *gadget = r->d()->gadgetPtr; - - // These four types are the most common used by the value type wrappers - VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal); - VALUE_TYPE_LOAD(QMetaType::Int || result->isEnum(), int, int); - VALUE_TYPE_LOAD(QMetaType::Int, int, int); - VALUE_TYPE_LOAD(QMetaType::QString, QString, v4->newString); - VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool); - - QVariant v; - void *args[] = { nullptr, nullptr }; - if (result->propType() == QMetaType::QVariant) { - args[0] = &v; - } else { - v = QVariant(result->propType(), static_cast<void *>(nullptr)); - args[0] = v.data(); - } - metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args); - return v4->fromVariant(v); -#undef VALUE_TYPE_ACCESSOR + return getGadgetProperty(v4, r->d(), result); } bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index 8db9474132..baac129afa 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -112,6 +112,9 @@ public: static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p); static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); + static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); + static ReturnedValue lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object); static void initProto(ExecutionEngine *v4); }; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 27171b9bd4..565e60b3c1 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -52,6 +52,7 @@ #include <private/qv4dateobject_p.h> #include <private/qv4objectiterator_p.h> #include <private/qv4alloca_p.h> +#include <private/qv4lookup_p.h> #include <qqmlcontext.h> #include <qqmlinfo.h> @@ -1604,8 +1605,7 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine); if (ep && ep->propertyCapture) - ep->propertyCapture->captureProperty(that->object(), -1, role->index, - QQmlPropertyCapture::OnlyOnce, false); + ep->propertyCapture->captureProperty(that->object(), -1, role->index, /*doNotify=*/ false); } const int elementIndex = that->d()->elementIndex(); @@ -1613,6 +1613,12 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va return that->engine()->fromVariant(value); } +ReturnedValue ModelObject::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) +{ + lookup->getter = Lookup::getterFallback; + return lookup->getter(lookup, engine, *object); +} + struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator { int roleNameIndex = 0; diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index ff52ee049f..2876c71de6 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -181,6 +181,8 @@ struct ModelObject : public QObjectWrapper protected: static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); + static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); }; diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index d9cb6506b8..a9a38c5381 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -525,7 +525,7 @@ public: metaObject.reset(builder.toMetaObject()); *static_cast<QMetaObject *>(this) = *metaObject; - propertyCache = new QQmlPropertyCache(metaObject.data(), model.modelItemRevision); + propertyCache.adopt(new QQmlPropertyCache(metaObject.data(), model.modelItemRevision)); } }; @@ -659,8 +659,8 @@ public: { VDMListDelegateDataType *dataType = const_cast<VDMListDelegateDataType *>(this); if (!propertyCache) { - dataType->propertyCache = new QQmlPropertyCache( - &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision); + dataType->propertyCache.adopt(new QQmlPropertyCache( + &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision)); } return new QQmlDMListAccessorData( diff --git a/src/quick/doc/src/concepts/statesanimations/states.qdoc b/src/quick/doc/src/concepts/statesanimations/states.qdoc index b695713091..5592ccd25d 100644 --- a/src/quick/doc/src/concepts/statesanimations/states.qdoc +++ b/src/quick/doc/src/concepts/statesanimations/states.qdoc @@ -120,7 +120,7 @@ interpolation behaviors are definable. The {Animation and Transitions} article has more information about creating state animations. -The \l {animation/states}{States and Transitions example} +The \l {Qt Quick Examples - Animation}{Animation} example demonstrates how to declare a basic set of states and apply animated transitions between them. diff --git a/src/quick/handlers/qquickdragaxis.cpp b/src/quick/handlers/qquickdragaxis.cpp index 5efe19b2fe..88470c8a7d 100644 --- a/src/quick/handlers/qquickdragaxis.cpp +++ b/src/quick/handlers/qquickdragaxis.cpp @@ -39,6 +39,8 @@ #include "qquickdragaxis_p.h" #include <limits> +QT_BEGIN_NAMESPACE + QQuickDragAxis::QQuickDragAxis() : m_minimum(-std::numeric_limits<qreal>::max()) , m_maximum(std::numeric_limits<qreal>::max()) @@ -73,3 +75,4 @@ void QQuickDragAxis::setEnabled(bool enabled) emit enabledChanged(); } +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickdragaxis_p.h b/src/quick/handlers/qquickdragaxis_p.h index 2c2e0a426d..a4649d5eb9 100644 --- a/src/quick/handlers/qquickdragaxis_p.h +++ b/src/quick/handlers/qquickdragaxis_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -51,10 +51,11 @@ // We mean it. // -#include <QtCore/qobject.h> -#include <QtCore/qglobal.h> +#include <private/qtquickglobal_p.h> -class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QQuickDragAxis : public QObject { Q_OBJECT Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged) @@ -84,4 +85,6 @@ private: bool m_enabled; }; +QT_END_NAMESPACE + #endif // QQUICKDRAGAXIS_P_H diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index 387a81eb43..748026488a 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE -class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickMultiPointHandler +class Q_QUICK_PRIVATE_EXPORT QQuickDragHandler : public QQuickMultiPointHandler { Q_OBJECT Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT) diff --git a/src/quick/handlers/qquickhoverhandler_p.h b/src/quick/handlers/qquickhoverhandler_p.h index 1ee2aeb7e6..d8e5fc00a1 100644 --- a/src/quick/handlers/qquickhoverhandler_p.h +++ b/src/quick/handlers/qquickhoverhandler_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -class Q_AUTOTEST_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler +class Q_QUICK_PRIVATE_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler { Q_OBJECT Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged) diff --git a/src/quick/handlers/qquickmultipointhandler_p.h b/src/quick/handlers/qquickmultipointhandler_p.h index 94142013cc..06f170154b 100644 --- a/src/quick/handlers/qquickmultipointhandler_p.h +++ b/src/quick/handlers/qquickmultipointhandler_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -class Q_AUTOTEST_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler +class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler { Q_OBJECT Q_PROPERTY(int minimumPointCount READ minimumPointCount WRITE setMinimumPointCount NOTIFY minimumPointCountChanged) diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h index 1afc028758..766d57f892 100644 --- a/src/quick/handlers/qquickpinchhandler_p.h +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE -class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler +class Q_QUICK_PRIVATE_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler { Q_OBJECT Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged) diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h index 82b24369d3..bb15142824 100644 --- a/src/quick/handlers/qquickpointerdevicehandler_p.h +++ b/src/quick/handlers/qquickpointerdevicehandler_p.h @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE class QQuickPointerDeviceHandlerPrivate; -class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler +class Q_QUICK_PRIVATE_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler { Q_OBJECT Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged) diff --git a/src/quick/handlers/qquickpointerdevicehandler_p_p.h b/src/quick/handlers/qquickpointerdevicehandler_p_p.h index 6a950590f3..03272d9f69 100644 --- a/src/quick/handlers/qquickpointerdevicehandler_p_p.h +++ b/src/quick/handlers/qquickpointerdevicehandler_p_p.h @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE -class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate +class Q_QUICK_PRIVATE_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate { Q_DECLARE_PUBLIC(QQuickPointerDeviceHandler) diff --git a/src/quick/handlers/qquickpointhandler_p.h b/src/quick/handlers/qquickpointhandler_p.h index 380ce1f90f..c197cb4f20 100644 --- a/src/quick/handlers/qquickpointhandler_p.h +++ b/src/quick/handlers/qquickpointhandler_p.h @@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE -class Q_AUTOTEST_EXPORT QQuickPointHandler : public QQuickSinglePointHandler +class Q_QUICK_PRIVATE_EXPORT QQuickPointHandler : public QQuickSinglePointHandler { Q_OBJECT Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged) diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h index 6ec5d55227..56e08590b2 100644 --- a/src/quick/handlers/qquicktaphandler_p.h +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickSinglePointHandler +class Q_QUICK_PRIVATE_EXPORT QQuickTapHandler : public QQuickSinglePointHandler { Q_OBJECT Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged) diff --git a/src/quick/items/qquickclipnode.cpp b/src/quick/items/qquickclipnode.cpp index 747e844172..d90b1f1540 100644 --- a/src/quick/items/qquickclipnode.cpp +++ b/src/quick/items/qquickclipnode.cpp @@ -43,6 +43,8 @@ #include <QtGui/qvector2d.h> #include <QtCore/qmath.h> +QT_BEGIN_NAMESPACE + QQuickDefaultClipNode::QQuickDefaultClipNode(const QRectF &rect) : m_rect(rect) , m_radius(0) @@ -117,3 +119,4 @@ void QQuickDefaultClipNode::updateGeometry() markDirty(DirtyGeometry); } +QT_END_NAMESPACE diff --git a/src/quick/items/qquickclipnode_p.h b/src/quick/items/qquickclipnode_p.h index ed9e1ae4d8..00914deae8 100644 --- a/src/quick/items/qquickclipnode_p.h +++ b/src/quick/items/qquickclipnode_p.h @@ -54,6 +54,8 @@ #include <private/qtquickglobal_p.h> #include <QtQuick/qsgnode.h> +QT_BEGIN_NAMESPACE + class Q_QUICK_PRIVATE_EXPORT QQuickDefaultClipNode : public QSGClipNode { public: @@ -78,4 +80,6 @@ private: QSGGeometry m_geometry; }; +QT_END_NAMESPACE + #endif // QQUICKCLIPNODE_P_H diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h index 854e284c9e..cb677de030 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.h +++ b/src/quick/scenegraph/coreapi/qsgnode.h @@ -96,7 +96,7 @@ public: // Uppermost 8 bits are reserved for internal use. IsVisitableNode = 0x01000000 #ifdef Q_CLANG_QDOC - InternalReserved = 0x01000000 + , InternalReserved = 0x01000000 #endif }; Q_DECLARE_FLAGS(Flags, Flag) |