diff options
Diffstat (limited to 'src/qml')
-rw-r--r-- | src/qml/common/qv4compileddata_p.h | 21 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 25 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 25 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 32 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler_p.h | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit.cpp | 3 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4lookup.cpp | 24 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4lookup_p.h | 47 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qmlcontext.cpp | 156 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qmlcontext_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 253 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper_p.h | 174 | ||||
-rw-r--r-- | src/qml/memory/qv4heap_p.h | 8 | ||||
-rw-r--r-- | src/qml/qml/qqml.cpp | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectorgadget_p.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmltypewrapper.cpp | 74 | ||||
-rw-r--r-- | src/qml/qml/qqmltypewrapper_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper.cpp | 15 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper_p.h | 2 |
19 files changed, 700 insertions, 177 deletions
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index f71001c63c..73f4060d0f 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -43,7 +43,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 0x36 // reordered runtime functions when compiling at run time +#define QV4_DATA_STRUCTURE_VERSION 0x37 // Added flag for call-type method lookups class QIODevice; class QQmlTypeNameCache; @@ -171,20 +171,29 @@ struct Lookup Type_QmlContextPropertyGetter = 3 }; - quint32 typeAndFlags() const { return m_data.get<TypeAndFlagsField>(); } + enum Mode : unsigned int { + Mode_ForStorage = 0, + Mode_ForCall = 1 + }; + + quint32 type() const { return m_data.get<TypeField>(); } quint32 nameIndex() const { return m_data.get<NameIndexField>(); } + quint32 mode() const { return m_data.get<ModeField>(); } Lookup() : m_data(QSpecialIntegerBitfieldZero) {} - Lookup(Type type, quint32 nameIndex) : Lookup() + Lookup(Type type, Mode mode, quint32 nameIndex) : Lookup() { - m_data.set<TypeAndFlagsField>(type); + m_data.set<TypeField>(type); + m_data.set<ModeField>(mode); m_data.set<NameIndexField>(nameIndex); } private: - using TypeAndFlagsField = quint32_le_bitfield_member<0, 4>; + using TypeField = quint32_le_bitfield_member<0, 2>; + using ModeField = quint32_le_bitfield_member<2, 1>; + // 1 bit left using NameIndexField = quint32_le_bitfield_member<4, 28>; - quint32_le_bitfield_union<TypeAndFlagsField, NameIndexField> m_data; + quint32_le_bitfield_union<TypeField, ModeField, NameIndexField> m_data; }; static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 5faaf34962..9014ad3a98 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2045,7 +2045,8 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio if (!disable_lookups && useFastLookups) { Instruction::CallPropertyLookup call; call.base = base.propertyBase.stackSlot(); - call.lookupIndex = registerGetterLookup(base.propertyNameIndex); + call.lookupIndex = registerGetterLookup( + base.propertyNameIndex, JSUnitGenerator::LookupForCall); call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); @@ -2073,13 +2074,15 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio } else if (!disable_lookups && useFastLookups && base.global) { if (base.qmlGlobal) { Instruction::CallQmlContextPropertyLookup call; - call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex()); + call.index = registerQmlContextPropertyGetterLookup( + base.nameAsIndex(), JSUnitGenerator::LookupForCall); call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } else { Instruction::CallGlobalLookup call; - call.index = registerGlobalGetterLookup(base.nameAsIndex()); + call.index = registerGlobalGetterLookup( + base.nameAsIndex(), JSUnitGenerator::LookupForCall); call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); @@ -4647,11 +4650,13 @@ QT_WARNING_POP if (!disable_lookups && global) { if (qmlGlobal) { Instruction::LoadQmlContextPropertyLookup load; - load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex()); + load.index = codegen->registerQmlContextPropertyGetterLookup( + nameAsIndex(), JSUnitGenerator::LookupForStorage); codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadGlobalLookup load; - load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); + load.index = codegen->registerGlobalGetterLookup( + nameAsIndex(), JSUnitGenerator::LookupForStorage); codegen->bytecodeGenerator->addInstruction(load); } } else { @@ -4668,12 +4673,16 @@ QT_WARNING_POP codegen->bytecodeGenerator->setLocation(sourceLocation); if (!disable_lookups && codegen->useFastLookups) { - if (optionalChainJumpLabel->isValid()) { // If we got a valid jump label, this means it's an optional lookup - auto jump = codegen->bytecodeGenerator->jumpOptionalLookup(codegen->registerGetterLookup(propertyNameIndex)); + if (optionalChainJumpLabel->isValid()) { + // If we got a valid jump label, this means it's an optional lookup + auto jump = codegen->bytecodeGenerator->jumpOptionalLookup( + codegen->registerGetterLookup( + propertyNameIndex, JSUnitGenerator::LookupForStorage)); jump.link(*optionalChainJumpLabel.get()); } else { Instruction::GetLookup load; - load.index = codegen->registerGetterLookup(propertyNameIndex); + load.index = codegen->registerGetterLookup( + propertyNameIndex, JSUnitGenerator::LookupForStorage); codegen->bytecodeGenerator->addInstruction(load); } } else { diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 95d39fa1eb..61af4b1139 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -506,11 +506,26 @@ public: int registerString(const QString &name) { return jsUnitGenerator->registerString(name); } - int registerConstant(QV4::ReturnedValue v) { return jsUnitGenerator->registerConstant(v); } - 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); } + int registerConstant(QV4::ReturnedValue v) + { + return jsUnitGenerator->registerConstant(v); + } + int registerGetterLookup(int nameIndex, JSUnitGenerator::LookupMode mode) + { + return jsUnitGenerator->registerGetterLookup(nameIndex, mode); + } + int registerSetterLookup(int nameIndex) + { + return jsUnitGenerator->registerSetterLookup(nameIndex); + } + int registerGlobalGetterLookup(int nameIndex, JSUnitGenerator::LookupMode mode) + { + return jsUnitGenerator->registerGlobalGetterLookup(nameIndex, mode); + } + int registerQmlContextPropertyGetterLookup(int nameIndex, JSUnitGenerator::LookupMode mode) + { + return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex, mode); + } // Returns index in _module->functions virtual int defineFunction(const QString &name, QQmlJS::AST::Node *ast, diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index c0026aba1f..2dad207087 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -111,14 +111,22 @@ QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module) registerString(QString()); } -int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name, LookupMode mode) { - return registerGetterLookup(registerString(name)); + return registerGetterLookup(registerString(name), mode); } -int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex) +static QV4::CompiledData::Lookup::Mode lookupMode(QV4::Compiler::JSUnitGenerator::LookupMode mode) { - lookups << CompiledData::Lookup(CompiledData::Lookup::Type_Getter, nameIndex); + return mode == QV4::Compiler::JSUnitGenerator::LookupForCall + ? QV4::CompiledData::Lookup::Mode_ForCall + : QV4::CompiledData::Lookup::Mode_ForStorage; +} + +int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex, LookupMode mode) +{ + lookups << CompiledData::Lookup( + CompiledData::Lookup::Type_Getter, lookupMode(mode), nameIndex); return lookups.size() - 1; } @@ -129,19 +137,25 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name) int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex) { - lookups << CompiledData::Lookup(CompiledData::Lookup::Type_Setter, nameIndex); + lookups << CompiledData::Lookup( + CompiledData::Lookup::Type_Setter, + CompiledData::Lookup::Mode_ForStorage, nameIndex); return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex) +int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex, LookupMode mode) { - lookups << CompiledData::Lookup(CompiledData::Lookup::Type_GlobalGetter, nameIndex); + lookups << CompiledData::Lookup( + CompiledData::Lookup::Type_GlobalGetter, lookupMode(mode), nameIndex); return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex) +int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup( + int nameIndex, LookupMode mode) { - lookups << CompiledData::Lookup(CompiledData::Lookup::Type_QmlContextPropertyGetter, nameIndex); + lookups << CompiledData::Lookup( + CompiledData::Lookup::Type_QmlContextPropertyGetter, lookupMode(mode), + nameIndex); return lookups.size() - 1; } diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 61432aa028..82afb7cf3a 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -70,6 +70,8 @@ private: }; struct Q_QML_COMPILER_PRIVATE_EXPORT JSUnitGenerator { + enum LookupMode { LookupForStorage, LookupForCall }; + static void generateUnitChecksum(CompiledData::Unit *unit); struct MemberInfo { @@ -83,12 +85,12 @@ struct Q_QML_COMPILER_PRIVATE_EXPORT JSUnitGenerator { int getStringId(const QString &string) const { return stringTable.getStringId(string); } QString stringForIndex(int index) const { return stringTable.stringForIndex(index); } - int registerGetterLookup(const QString &name); - int registerGetterLookup(int nameIndex); + int registerGetterLookup(const QString &name, LookupMode mode); + int registerGetterLookup(int nameIndex, LookupMode mode); int registerSetterLookup(const QString &name); int registerSetterLookup(int nameIndex); - int registerGlobalGetterLookup(int nameIndex); - int registerQmlContextPropertyGetterLookup(int nameIndex); + int registerGlobalGetterLookup(int nameIndex, LookupMode mode); + int registerQmlContextPropertyGetterLookup(int nameIndex, LookupMode mode); int lookupNameIndex(int index) const { return lookups[index].nameIndex(); } QString lookupName(int index) const { return stringForIndex(lookupNameIndex(index)); } diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index ee7b7656cc..81716d2f0f 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -139,7 +139,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) QV4::Lookup *l = runtimeLookups + i; CompiledData::Lookup::Type type - = CompiledData::Lookup::Type(uint(compiledLookups[i].typeAndFlags())); + = CompiledData::Lookup::Type(uint(compiledLookups[i].type())); if (type == CompiledData::Lookup::Type_Getter) l->getter = QV4::Lookup::getterGeneric; else if (type == CompiledData::Lookup::Type_Setter) @@ -148,6 +148,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) l->globalGetter = QV4::Lookup::globalGetterGeneric; else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter) l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + l->forCall = compiledLookups[i].mode() == CompiledData::Lookup::Mode_ForCall; l->nameIndex = compiledLookups[i].nameIndex(); } } diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 3e5e961245..abf88f4283 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -148,6 +148,7 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Lookup second; memset(&second, 0, sizeof(Lookup)); second.nameIndex = l->nameIndex; + second.forCall = l->forCall; second.getter = getterGeneric; const ReturnedValue result = second.resolveGetter(engine, o); @@ -393,8 +394,27 @@ ReturnedValue Lookup::getterQObject(Lookup *lookup, ExecutionEngine *engine, con return Lookup::getterGeneric(lookup, engine, object); }; - return QObjectWrapper::lookupGetterImpl( - lookup, engine, object, /*useOriginalProperty*/ false, revertLookup); + const QObjectWrapper::Flags flags = lookup->forCall + ? QObjectWrapper::AllowOverride + : (QObjectWrapper::AllowOverride | QObjectWrapper::AttachMethods); + + return QObjectWrapper::lookupPropertyGetterImpl(lookup, engine, object, flags, revertLookup); +} + +ReturnedValue Lookup::getterQObjectMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [lookup, engine, &object]() { + lookup->qobjectMethodLookup.propertyCache->release(); + lookup->qobjectMethodLookup.propertyCache = nullptr; + lookup->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(lookup, engine, object); + }; + + const QObjectWrapper::Flags flags = lookup->forCall + ? QObjectWrapper::AllowOverride + : (QObjectWrapper::AllowOverride | QObjectWrapper::AttachMethods); + + return QObjectWrapper::lookupMethodGetterImpl(lookup, engine, object, flags, revertLookup); } ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object) diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 03ab132ec9..bd481d366e 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -24,6 +24,10 @@ QT_BEGIN_NAMESPACE namespace QV4 { +namespace Heap { + struct QObjectMethod; +} + // Note: We cannot hide the copy ctor and assignment operator of this class because it needs to // be trivially copyable. But you should never ever copy it. There are refcounted members // in there. @@ -92,6 +96,12 @@ struct Q_QML_PRIVATE_EXPORT Lookup { const QQmlPropertyData *propertyData; } qobjectLookup; struct { + Heap::InternalClass *ic; + Heap::QObjectMethod *method; + const QQmlPropertyCache *propertyCache; + const QQmlPropertyData *propertyData; + } qobjectMethodLookup; + struct { quintptr isConstant; // This is a bool, encoded as 0 or 1. Both values are ignored by gc quintptr metaObject; // a (const QMetaObject* & 1) or nullptr int coreIndex; @@ -141,7 +151,10 @@ struct Q_QML_PRIVATE_EXPORT Lookup { Heap::Object *qmlScopedEnumWrapper; } qmlScopedEnumWrapperLookup; }; - uint nameIndex; + + uint nameIndex: 28; // Same number of bits we store in the compilation unit for name indices + uint forCall: 1; // Whether we are looking up a value in order to call it right away + uint reserved: 3; ReturnedValue resolveGetter(ExecutionEngine *engine, const Object *object); ReturnedValue resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object); @@ -164,6 +177,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup { static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterQObject(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getterQObjectMethod(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -204,6 +218,12 @@ struct Q_QML_PRIVATE_EXPORT Lookup { || qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) { if (const QQmlPropertyCache *pc = qobjectLookup.propertyCache) pc->release(); + } else if (getter == getterQObjectMethod + || getter == QQmlTypeWrapper::lookupSingletonMethod + || qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectMethod + || qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectMethod) { + if (const QQmlPropertyCache *pc = qobjectMethodLookup.propertyCache) + pc->release(); } } }; @@ -227,8 +247,8 @@ inline void setupQObjectLookup( Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData, const Object *self) { - lookup->qobjectLookup.ic = self->internalClass(); setupQObjectLookup(lookup, ddata, propertyData); + lookup->qobjectLookup.ic = self->internalClass(); } @@ -236,8 +256,29 @@ inline void setupQObjectLookup( Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData, const Object *self, const Object *qmlType) { - lookup->qobjectLookup.qmlTypeIc = qmlType->internalClass(); setupQObjectLookup(lookup, ddata, propertyData, self); + lookup->qobjectLookup.qmlTypeIc = qmlType->internalClass(); +} + +inline void setupQObjectMethodLookup( + Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData, + const Object *self, Heap::QObjectMethod *method) +{ + lookup->releasePropertyCache(); + Q_ASSERT(!ddata->propertyCache.isNull()); + lookup->qobjectMethodLookup.method = method; + lookup->qobjectMethodLookup.ic = self->internalClass(); + lookup->qobjectMethodLookup.propertyCache = ddata->propertyCache.data(); + lookup->qobjectMethodLookup.propertyCache->addref(); + lookup->qobjectMethodLookup.propertyData = propertyData; +} + +inline bool qualifiesForMethodLookup(const QQmlPropertyData *propertyData) +{ + return propertyData->isFunction() + && !propertyData->isSignalHandler() // TODO: Optimize SignalHandler, too + && !propertyData->isVMEFunction() // Handled by QObjectLookup + && !propertyData->isVarProperty(); } } diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index c069bd854c..1a1636a988 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -103,6 +103,13 @@ bool performLookup(ScopedValue *result, bool *hasProperty, const Lookup &lookup) return false; } +static QV4::QObjectWrapper::Flags getQmlPropertyFlags(const Lookup *l) +{ + return (l && l->forCall) + ? QV4::QObjectWrapper::CheckRevision + : (QV4::QObjectWrapper::CheckRevision | QV4::QObjectWrapper::AttachMethods); +} + ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup) { if (!id.isString()) @@ -270,8 +277,9 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r bool hasProp = false; const QQmlPropertyData *propertyData = nullptr; - QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject, - name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData)); + QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty( + v4, context, scopeObject, name, getQmlPropertyFlags(lookup), + &hasProp, &propertyData)); if (hasProp) { if (hasProperty) *hasProperty = true; @@ -281,9 +289,22 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r 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))); - QV4::setupQObjectLookup(lookup, ddata, propertyData, val->objectValue()); - lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty; + ScopedValue val( + scope, + base ? *base : Value::fromReturnedValue( + QV4::QObjectWrapper::wrap(v4, scopeObject))); + if (QObjectMethod *method = result->as<QObjectMethod>()) { + QV4::setupQObjectMethodLookup( + lookup, ddata, propertyData, val->objectValue(), + method->d()); + lookup->qmlContextPropertyGetter + = QQmlContextWrapper::lookupScopeObjectMethod; + } else { + QV4::setupQObjectLookup( + lookup, ddata, propertyData, val->objectValue()); + lookup->qmlContextPropertyGetter + = QQmlContextWrapper::lookupScopeObjectProperty; + } } } @@ -297,9 +318,9 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r if (QObject *contextObject = context->contextObject()) { bool hasProp = false; const QQmlPropertyData *propertyData = nullptr; - result = QV4::QObjectWrapper::getQmlProperty(v4, context, contextObject, - name, QV4::QObjectWrapper::CheckRevision, - &hasProp, &propertyData); + result = QV4::QObjectWrapper::getQmlProperty( + v4, context, contextObject, name, getQmlPropertyFlags(lookup), + &hasProp, &propertyData); if (hasProp) { if (hasProperty) *hasProperty = true; @@ -311,11 +332,23 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r QQmlData *ddata = QQmlData::get(contextObject, false); if (ddata && ddata->propertyCache && lookup->qmlContextPropertyGetter != contextGetterFunction) { - ScopedValue val(scope, base ? *base - : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, contextObject))); - QV4::setupQObjectLookup(lookup, ddata, propertyData, - val->objectValue()); - lookup->qmlContextPropertyGetter = contextGetterFunction; + ScopedValue val( + scope, + base ? *base : Value::fromReturnedValue( + QV4::QObjectWrapper::wrap(v4, contextObject))); + if (QObjectMethod *method = result->as<QObjectMethod>()) { + setupQObjectMethodLookup( + lookup, ddata, propertyData, val->objectValue(), + method->d()); + if (contextGetterFunction == lookupScopeObjectProperty) + lookup->qmlContextPropertyGetter = lookupScopeObjectMethod; + else + lookup->qmlContextPropertyGetter = lookupContextObjectMethod; + } else { + setupQObjectLookup( + lookup, ddata, propertyData, val->objectValue()); + lookup->qmlContextPropertyGetter = contextGetterFunction; + } } } else if (originalLookup) { originalLookup->qmlContextPropertyGetter = lookupInParentContextHierarchy; @@ -478,8 +511,8 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup * Scoped<QmlContext> callingQmlContext(scope, engine->qmlContext()); if (callingQmlContext) { Scoped<QQmlContextWrapper> qmlContextWrapper(scope, callingQmlContext->d()->qml()); - result = QQmlContextWrapper::getPropertyAndBase(qmlContextWrapper, name, /*receiver*/nullptr, &hasProperty, - base, l); + result = QQmlContextWrapper::getPropertyAndBase( + qmlContextWrapper, name, /*receiver*/nullptr, &hasProperty, base, l); } else { // Code path typical to worker scripts, compiled with lookups but no qml context. result = l->resolveGlobalGetter(engine); @@ -556,7 +589,24 @@ ReturnedValue QQmlContextWrapper::lookupIdObjectInParentContext( return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); } -ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +static ReturnedValue revertObjectPropertyLookup(Lookup *l, ExecutionEngine *engine, Value *base) +{ + l->qobjectLookup.propertyCache->release(); + l->qobjectLookup.propertyCache = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); +} + +static ReturnedValue revertObjectMethodLookup(Lookup *l, ExecutionEngine *engine, Value *base) +{ + l->qobjectMethodLookup.propertyCache->release(); + l->qobjectMethodLookup.propertyCache = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); +} + +template<typename Call> +ReturnedValue callWithScopeObject(ExecutionEngine *engine, Value *base, Call c) { Scope scope(engine); Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); @@ -570,22 +620,40 @@ ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, Execution 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)); if (base) *base = obj; - return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); + return c(obj); +} + +ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +{ + return callWithScopeObject(engine, base, [l, engine, base](const Value &obj) { + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::NoFlag + : QObjectWrapper::AttachMethods; + return QObjectWrapper::lookupPropertyGetterImpl(l, engine, obj, flags, [l, engine, base]() { + return revertObjectPropertyLookup(l, engine, base); + }); + }); +} + +ReturnedValue QQmlContextWrapper::lookupScopeObjectMethod(Lookup *l, ExecutionEngine *engine, Value *base) +{ + return callWithScopeObject(engine, base, [l, engine, base](const Value &obj) { + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::NoFlag + : QObjectWrapper::AttachMethods; + return QObjectWrapper::lookupMethodGetterImpl(l, engine, obj, flags, [l, engine, base]() { + return revertObjectMethodLookup(l, engine, base); + }); + }); } -ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +template<typename Call> +ReturnedValue callWithContextObject(ExecutionEngine *engine, Value *base, Call c) { Scope scope(engine); Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); @@ -603,19 +671,38 @@ ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, Executi 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)); if (base) *base = obj; - return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); + return c(obj); +} + +ReturnedValue QQmlContextWrapper::lookupContextObjectProperty( + Lookup *l, ExecutionEngine *engine, Value *base) +{ + return callWithContextObject(engine, base, [l, engine, base](const Value &obj) { + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::NoFlag + : QObjectWrapper::AttachMethods; + return QObjectWrapper::lookupPropertyGetterImpl(l, engine, obj, flags, [l, engine, base]() { + return revertObjectPropertyLookup(l, engine, base); + }); + }); +} + +ReturnedValue QQmlContextWrapper::lookupContextObjectMethod( + Lookup *l, ExecutionEngine *engine, Value *base) +{ + return callWithContextObject(engine, base, [l, engine, base](const Value &obj) { + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::NoFlag + : QObjectWrapper::AttachMethods; + return QObjectWrapper::lookupMethodGetterImpl(l, engine, obj, flags, [l, engine, base]() { + return revertObjectMethodLookup(l, engine, base); + }); + }); } ReturnedValue QQmlContextWrapper::lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base) @@ -664,8 +751,7 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec if (QObject *contextObject = context->contextObject()) { bool hasProp = false; result = QV4::QObjectWrapper::getQmlProperty( - engine, context, contextObject, name, - QV4::QObjectWrapper::CheckRevision, &hasProp); + engine, context, contextObject, name, getQmlPropertyFlags(l), &hasProp); if (hasProp) { if (base) *base = QV4::QObjectWrapper::wrap(engine, contextObject); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index a89e7f6f2f..3aed9af8fd 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -76,8 +76,10 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupIdObjectInParentContext(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupScopeObjectMethod(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupContextObjectMethod(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupType(Lookup *l, ExecutionEngine *engine, Value *base); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 54ef2c75b7..376332179c 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -195,16 +195,16 @@ void QObjectWrapper::initializeBindings(ExecutionEngine *engine) const QQmlPropertyData *QObjectWrapper::findProperty( const QQmlRefPointer<QQmlContextData> &qmlContext, String *name, - RevisionMode revisionMode, QQmlPropertyData *local) const + Flags flags, QQmlPropertyData *local) const { - return findProperty(d()->object(), qmlContext, name, revisionMode, local); + return findProperty(d()->object(), qmlContext, name, flags, local); } const QQmlPropertyData *QObjectWrapper::findProperty( QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext, - String *name, RevisionMode revisionMode, QQmlPropertyData *local) + String *name, Flags flags, QQmlPropertyData *local) { - Q_UNUSED(revisionMode); + Q_UNUSED(flags); QQmlData *ddata = QQmlData::get(o, false); const QQmlPropertyData *result = nullptr; @@ -216,7 +216,8 @@ const QQmlPropertyData *QObjectWrapper::findProperty( } ReturnedValue QObjectWrapper::getProperty( - ExecutionEngine *engine, QObject *object, const QQmlPropertyData *property) + ExecutionEngine *engine, QObject *object, + const QQmlPropertyData *property, Flags flags) { QQmlData::flushPendingBinding(object, property->coreIndex()); @@ -230,13 +231,16 @@ ReturnedValue QObjectWrapper::getProperty( ScopedContext global(scope, engine->qmlContext()); if (!global) global = engine->rootContext(); - return QObjectMethod::create(global, object, property->coreIndex()); + return QObjectMethod::create( + global, (flags & AttachMethods) ? object : nullptr, property->coreIndex()); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); - return engine->memoryManager->allocate<QmlSignalHandler>(object, property->coreIndex())->asReturnedValue(); + return engine->memoryManager->allocate<QmlSignalHandler>( + object, property->coreIndex())->asReturnedValue(); } else { ExecutionContext *global = engine->rootContext(); - return QObjectMethod::create(global, object, property->coreIndex()); + return QObjectMethod::create( + global, (flags & AttachMethods) ? object : nullptr, property->coreIndex()); } } @@ -301,7 +305,7 @@ static OptionalReturnedValue getPropertyFromImports( ReturnedValue QObjectWrapper::getQmlProperty( const QQmlRefPointer<QQmlContextData> &qmlContext, String *name, - QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports) const + QObjectWrapper::Flags flags, bool *hasProperty) const { // Keep this code in sync with ::virtualResolveLookupGetter @@ -317,12 +321,13 @@ ReturnedValue QObjectWrapper::getQmlProperty( return *methodValue; QQmlPropertyData local; - const QQmlPropertyData *result = findProperty(qmlContext, name, revisionMode, &local); + const QQmlPropertyData *result = findProperty(qmlContext, name, flags, &local); if (!result) { // Check for attached properties - if (includeImports && name->startsWithUpper()) { - if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty)) + if ((flags & IncludeImports) && name->startsWithUpper()) { + if (auto importProperty = getPropertyFromImports( + v4, name, qmlContext, d()->object(), hasProperty)) return *importProperty; } return Object::virtualGet(this, name->propertyKey(), this, hasProperty); @@ -330,7 +335,7 @@ ReturnedValue QObjectWrapper::getQmlProperty( QQmlData *ddata = QQmlData::get(d()->object(), false); - if (revisionMode == QObjectWrapper::CheckRevision && result->hasRevision()) { + if ((flags & CheckRevision) && result->hasRevision()) { if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) { if (hasProperty) *hasProperty = false; @@ -341,12 +346,12 @@ ReturnedValue QObjectWrapper::getQmlProperty( if (hasProperty) *hasProperty = true; - return getProperty(v4, d()->object(), result); + return getProperty(v4, d()->object(), result, flags); } ReturnedValue QObjectWrapper::getQmlProperty( ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, - QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, + QObject *object, String *name, QObjectWrapper::Flags flags, bool *hasProperty, const QQmlPropertyData **property) { if (QQmlData::wasDeleted(object)) { @@ -360,11 +365,12 @@ ReturnedValue QObjectWrapper::getQmlProperty( QQmlData *ddata = QQmlData::get(object, false); QQmlPropertyData local; - const QQmlPropertyData *result = findProperty(object, qmlContext, name, revisionMode, &local); + const QQmlPropertyData *result = findProperty(object, qmlContext, name, flags, &local); if (result) { - if (revisionMode == QObjectWrapper::CheckRevision && result->hasRevision()) { - if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) { + if ((flags & QObjectWrapper::CheckRevision) && result->hasRevision()) { + if (ddata && ddata->propertyCache + && !ddata->propertyCache->isAllowedInRevision(result)) { if (hasProperty) *hasProperty = false; return Encode::undefined(); @@ -377,7 +383,7 @@ ReturnedValue QObjectWrapper::getQmlProperty( if (property && result != &local) *property = result; - return getProperty(engine, object, result); + return getProperty(engine, object, result, flags); } else { // Check if this object is already wrapped. if (!ddata || (ddata->jsWrapper.isUndefined() && @@ -403,13 +409,13 @@ ReturnedValue QObjectWrapper::getQmlProperty( *hasProperty = false; return Encode::null(); } - return wrapper->getQmlProperty(qmlContext, name, revisionMode, hasProperty); + return wrapper->getQmlProperty(qmlContext, name, flags, hasProperty); } bool QObjectWrapper::setQmlProperty( ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, QObject *object, - String *name, QObjectWrapper::RevisionMode revisionMode, const Value &value) + String *name, QObjectWrapper::Flags flags, const Value &value) { if (QQmlData::wasDeleted(object)) return false; @@ -419,7 +425,7 @@ bool QObjectWrapper::setQmlProperty( if (!result) return false; - if (revisionMode == QObjectWrapper::CheckRevision && result->hasRevision()) { + if ((flags & QObjectWrapper::CheckRevision) && result->hasRevision()) { QQmlData *ddata = QQmlData::get(object); if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) return false; @@ -688,6 +694,13 @@ ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObjec return constWrapper.asReturnedValue(); } +Heap::QObjectMethod *QObjectWrapper::cloneMethod(ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, QObject *object) +{ + Scope scope(engine); + Scoped<QObjectMethod> method(scope, QObjectMethod::create(engine, cloneFrom, object)); + return method ? method->d() : nullptr; +} + void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) { if (QQmlData::wasDeleted(object)) @@ -760,7 +773,7 @@ ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Scope scope(that); ScopedString n(scope, id.asStringOrSymbol()); QQmlRefPointer<QQmlContextData> qmlContext = that->engine()->callingQmlContext(); - return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true); + return that->getQmlProperty(qmlContext, n, IncludeImports | AttachMethods, hasProperty); } bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) @@ -783,7 +796,7 @@ bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, return false; QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext(); - if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, QObjectWrapper::IgnoreRevision, value)) { + if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, NoFlag, value)) { QQmlData *ddata = QQmlData::get(that->d()->object()); // Types created by QML are not extensible at run-time, but for other QObjects we can store them // as regular JavaScript properties, like on JavaScript objects. @@ -810,12 +823,13 @@ PropertyAttributes QObjectWrapper::virtualGetOwnProperty(const Managed *m, Prope ScopedString n(scope, id.asStringOrSymbol()); QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext(); QQmlPropertyData local; - if (that->findProperty(qmlContext, n, IgnoreRevision, &local) + if (that->findProperty(qmlContext, n, NoFlag, &local) || n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) { if (p) { // ### probably not the fastest implementation bool hasProperty; - p->value = that->getQmlProperty(qmlContext, n, IgnoreRevision, &hasProperty, /*includeImports*/ true); + p->value = that->getQmlProperty( + qmlContext, n, IncludeImports | AttachMethods, &hasProperty); } return Attr_Data; } @@ -861,7 +875,8 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property if (pd) { QQmlPropertyData local; local.load(property); - pd->value = that->getProperty(thatEngine, thatObject, &local); + pd->value = that->getProperty( + thatEngine, thatObject, &local, QObjectWrapper::AttachMethods); } return propName->toPropertyKey(); } @@ -886,7 +901,8 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property if (pd) { QQmlPropertyData local; local.load(method); - pd->value = that->getProperty(thatEngine, thatObject, &local); + pd->value = that->getProperty( + thatEngine, thatObject, &local, QObjectWrapper::AttachMethods); } return methodName->toPropertyKey(); } @@ -919,15 +935,22 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E if (QQmlData::wasDeleted(qobj)) return Encode::undefined(); - if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) - return *methodValue; - QQmlData *ddata = QQmlData::get(qobj, false); + if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) { + Scoped<QObjectMethod> method(scope, *methodValue); + setupQObjectMethodLookup( + lookup, ddata ? ddata : QQmlData::get(qobj, true), nullptr, This, method->d()); + lookup->getter = Lookup::getterQObjectMethod; + return method.asReturnedValue(); + } + if (!ddata || !ddata->propertyCache) { QQmlPropertyData local; const QQmlPropertyData *property = QQmlPropertyCache::property( qobj, name, qmlContext, &local); - return property ? getProperty(engine, qobj, property) : Encode::undefined(); + return property + ? getProperty(engine, qobj, property, lookup->forCall ? NoFlag : AttachMethods) + : Encode::undefined(); } const QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext); @@ -940,6 +963,15 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E return Object::virtualResolveLookupGetter(object, engine, lookup); } + if (property->isFunction() + && !property->isVarProperty() + && !property->isVMEFunction() // Handled by QObjectLookup + && !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too + setupQObjectMethodLookup(lookup, ddata, property, This, nullptr); + lookup->getter = Lookup::getterQObjectMethod; + return lookup->getter(lookup, engine, *object); + } + setupQObjectLookup(lookup, ddata, property, This); lookup->getter = Lookup::getterQObject; return lookup->getter(lookup, engine, *object); @@ -2178,6 +2210,47 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueType return method.asReturnedValue(); } +ReturnedValue QObjectMethod::create( + ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, QObject *object) +{ + Scope valueScope(engine); + + Scoped<QQmlValueTypeWrapper> valueTypeWrapper(valueScope); + if (cloneFrom->valueTypeWrapper) { + Scoped<QQmlValueTypeReference> ref(valueScope, cloneFrom->valueTypeWrapper); + if (ref) { + valueTypeWrapper = QQmlValueTypeReference::create(engine, ref->d(), object); + } else { + // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what + // value we should operate on. Without knowledge of the property the value + // was read from, we cannot load the value from the given object. + return Encode::undefined(); + } + } + + Scoped<ExecutionContext> context(valueScope, cloneFrom->scope.get()); + Scoped<QObjectMethod> method( + valueScope, engine->memoryManager->allocate<QV4::QObjectMethod>(context)); + + method->d()->index = cloneFrom->index; + method->d()->methodCount = cloneFrom->methodCount; + + if (valueTypeWrapper) + method->d()->valueTypeWrapper.set(engine, valueTypeWrapper->d()); + else + method->d()->setObject(object); + + if (cloneFrom->methodCount == 1) { + method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod); + *method->d()->methods = *cloneFrom->methods; + } else { + method->d()->methods = new QQmlPropertyData[cloneFrom->methodCount]; + memcpy(method->d()->methods, cloneFrom->methods, + cloneFrom->methodCount * sizeof(QQmlPropertyData)); + } + return method.asReturnedValue(); +} + void Heap::QObjectMethod::init(QV4::ExecutionContext *scope) { Heap::FunctionObject::init(scope); @@ -2187,14 +2260,62 @@ const QMetaObject *Heap::QObjectMethod::metaObject() { if (valueTypeWrapper) return valueTypeWrapper->metaObject(); - return object()->metaObject(); + if (QObject *self = object()) + return self->metaObject(); + return nullptr; +} + +bool Heap::QObjectMethod::isDetached() const +{ + if (qObj.isValid()) + return false; + + if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) { + const VTable *vt = wrapper->internalClass->vtable; + while (vt && vt != QV4::QQmlValueTypeWrapper::staticVTable()) { + if (vt == QV4::QQmlValueTypeReference::staticVTable()) + return static_cast<Heap::QQmlValueTypeReference *>(wrapper)->object.isNull(); + vt = vt->parent; + } + } + + return true; } -void Heap::QObjectMethod::ensureMethodsCache() +bool Heap::QObjectMethod::isAttachedTo(QObject *o) const +{ + if (qObj.isValid() && qObj != o) + return false; + + if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) { + const VTable *vt = wrapper->internalClass->vtable; + while (vt && vt != QV4::QQmlValueTypeWrapper::staticVTable()) { + if (vt == QV4::QQmlValueTypeReference::staticVTable()) { + if (static_cast<Heap::QQmlValueTypeReference *>(wrapper)->object != o) + return false; + break; + } + vt = vt->parent; + } + } + + return true; +} + +void Heap::QObjectMethod::ensureMethodsCache(QObject *o) { if (methods) return; + const QMetaObject *mo = metaObject(); + + if (!mo) { + Q_ASSERT(o); + mo = o->metaObject(); + } + + Q_ASSERT(mo); + int methodOffset = mo->methodOffset(); while (methodOffset > index) { mo = mo->superClass(); @@ -2225,14 +2346,30 @@ void Heap::QObjectMethod::ensureMethodsCache() } } -ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine) const +static QObject *qObject(const Value *v) +{ + if (const QObjectWrapper *o = v->as<QObjectWrapper>()) + return o->object(); + if (const QQmlTypeWrapper *t = v->as<QQmlTypeWrapper>()) + return t->object(); + return nullptr; +} + +ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, const QV4::Value *thisObject) const { const auto encode = [engine](const QString &result) { return engine->newString(result)->asReturnedValue(); }; - if (const QMetaObject *metaObject = d()->metaObject()) { - if (QObject *qobject = d()->object()) { + QObject *o = qObject(thisObject); + + d()->assertIntegrity(o); + + const QMetaObject *metaObject = d()->metaObject(); + if (!metaObject && o) + metaObject = o->metaObject(); + if (metaObject) { + if (QObject *qobject = o ? o : d()->object()) { const int id = metaObject->indexOfMethod("toString()"); if (id >= 0) { const QMetaMethod method = metaObject->method(id); @@ -2258,11 +2395,14 @@ ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine) const return encode(QLatin1String("null")); } -ReturnedValue QObjectMethod::method_destroy(ExecutionEngine *engine, const Value *args, int argc) const +ReturnedValue QObjectMethod::method_destroy(ExecutionEngine *engine, const QV4::Value *thisObject, const Value *args, int argc) const { - if (!d()->object()) + QObject *o = qObject(thisObject); + QObject *object = o ? o : d()->object(); + if (!object) return Encode::undefined(); - if (QQmlData::keepAliveDuringGarbageCollection(d()->object())) + + if (QQmlData::keepAliveDuringGarbageCollection(object)) return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); int delay = 0; @@ -2270,9 +2410,9 @@ ReturnedValue QObjectMethod::method_destroy(ExecutionEngine *engine, const Value delay = args[0].toUInt32(); if (delay > 0) - QTimer::singleShot(delay, d()->object(), SLOT(deleteLater())); + QTimer::singleShot(delay, object, SLOT(deleteLater())); else - d()->object()->deleteLater(); + object->deleteLater(); return Encode::undefined(); } @@ -2286,23 +2426,25 @@ ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *t ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const { ExecutionEngine *v4 = engine(); + if (d()->index == DestroyMethod) - return method_destroy(v4, argv, argc); + return method_destroy(v4, thisObject, argv, argc); else if (d()->index == ToStringMethod) - return method_toString(v4); + return method_toString(v4, thisObject); - d()->ensureMethodsCache(); + QObject *o = qObject(thisObject); - Scope scope(v4); - QQmlObjectOrGadget object(d()->object()); + d()->assertIntegrity(o); + d()->ensureMethodsCache(o); - if (!d()->object()) { - if (!d()->valueTypeWrapper) - return Encode::undefined(); + QQmlObjectOrGadget object = d()->valueTypeWrapper + ? QQmlObjectOrGadget(d()->metaObject(), d()->valueTypeWrapper->gadgetPtr()) + : QQmlObjectOrGadget(o ? o : d()->object()); - object = QQmlObjectOrGadget(d()->metaObject(), d()->valueTypeWrapper->gadgetPtr()); - } + if (object.isNull()) + return Encode::undefined(); + Scope scope(v4); JSCallData cData(thisObject, argv, argc); CallData *callData = cData.callData(scope); @@ -2312,11 +2454,10 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * // The method might change the value. const auto doCall = [&](const auto &call) { if (!method->isConstant()) { - Scoped<QQmlValueTypeReference> valueTypeReference( - scope, d()->valueTypeWrapper.get()); - if (valueTypeReference) { + Scoped<QQmlValueTypeReference> ref(scope, d()->valueTypeWrapper); + if (ref) { ScopedValue rv(scope, call()); - valueTypeReference->d()->writeBack(); + ref->d()->writeBack(); return rv->asReturnedValue(); } } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index ff569ce60d..eb1b295c5d 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -60,16 +60,16 @@ private: }; #define QObjectMethodMembers(class, Member) \ - Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) \ - Member(class, NoMark, QV4QPointer<QObject>, qObj) \ - Member(class, NoMark, int, index) + Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { DECLARE_MARKOBJECTS(QObjectMethod); + QV4QPointer<QObject> qObj; QQmlPropertyData *methods; - int methodCount; alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)]; + int methodCount; + int index; void init(QV4::ExecutionContext *scope); void destroy() @@ -80,12 +80,26 @@ DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { FunctionObject::destroy(); } - void ensureMethodsCache(); + void ensureMethodsCache(QObject *o); const QMetaObject *metaObject(); QObject *object() const { return qObj.data(); } void setObject(QObject *o) { qObj = o; } + bool isDetached() const; + bool isAttachedTo(QObject *o) const; + + void assertIntegrity(QObject *thisObject) const + { + // This implies that the metaobject matches. + // If the QObjectMethod is detached, we can only have gotten here via a lookup. + // The lookup checks that the QQmlPropertyCache matches. + // We can also call methods on standalone value type wrappers, without an object. + if (thisObject) + Q_ASSERT(isDetached() || isAttachedTo(thisObject)); + else + Q_ASSERT(valueTypeWrapper || !isDetached()); + } }; struct QMetaObjectWrapper : FunctionObject { @@ -120,7 +134,15 @@ struct Q_QML_EXPORT QObjectWrapper : public Object V4_OBJECT2(QObjectWrapper, Object) V4_NEEDS_DESTROY - enum RevisionMode { IgnoreRevision, CheckRevision }; + enum Flag { + NoFlag = 0x0, + CheckRevision = 0x1, + AttachMethods = 0x2, + AllowOverride = 0x4, + IncludeImports = 0x8, + }; + + Q_DECLARE_FLAGS(Flags, Flag); static void initializeBindings(ExecutionEngine *engine); @@ -128,16 +150,16 @@ struct Q_QML_EXPORT QObjectWrapper : public Object ReturnedValue getQmlProperty( const QQmlRefPointer<QQmlContextData> &qmlContext, String *name, - RevisionMode revisionMode, bool *hasProperty = nullptr, - bool includeImports = false) const; - \ - static ReturnedValue getQmlProperty(ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, - QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, + Flags flags, bool *hasProperty = nullptr) const; + + static ReturnedValue getQmlProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, + QObject *object, String *name, Flags flags, bool *hasProperty = nullptr, const QQmlPropertyData **property = nullptr); static bool setQmlProperty( ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, - QObject *object, String *name, RevisionMode revisionMode, const Value &value); + QObject *object, String *name, Flags flags, const Value &value); static ReturnedValue wrap(ExecutionEngine *engine, QObject *object); static ReturnedValue wrapConst(ExecutionEngine *engine, QObject *object); @@ -154,37 +176,55 @@ struct Q_QML_EXPORT QObjectWrapper : public Object void destroyObject(bool lastCall); static ReturnedValue getProperty( - ExecutionEngine *engine, QObject *object, const QQmlPropertyData *property); + ExecutionEngine *engine, QObject *object, const QQmlPropertyData *property, + Flags flags); static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); static ReturnedValue lookupAttached(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); + template <typename ReversalFunctor> static ReturnedValue lookupPropertyGetterImpl( + Lookup *l, ExecutionEngine *engine, const Value &object, + Flags flags, ReversalFunctor revert); + template <typename ReversalFunctor> static ReturnedValue lookupMethodGetterImpl( + Lookup *l, ExecutionEngine *engine, const Value &object, + Flags flags, ReversalFunctor revert); + static bool virtualResolveLookupSetter( + Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); + protected: static bool virtualIsEqualTo(Managed *that, Managed *o); static ReturnedValue create(ExecutionEngine *engine, QObject *object); - static const QQmlPropertyData *findProperty(QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext, - String *name, RevisionMode revisionMode, QQmlPropertyData *local); + static const QQmlPropertyData *findProperty( + QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext, + String *name, Flags flags, QQmlPropertyData *local); - const QQmlPropertyData *findProperty(const QQmlRefPointer<QQmlContextData> &qmlContext, - String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; + const QQmlPropertyData *findProperty( + const QQmlRefPointer<QQmlContextData> &qmlContext, + String *name, Flags flags, QQmlPropertyData *local) const; - static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + 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 PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p); - static ReturnedValue method_connect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_disconnect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_connect( + const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_disconnect( + const FunctionObject *, const Value *thisObject, const Value *argv, int argc); private: Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object); Q_NEVER_INLINE static ReturnedValue wrapConst_slowPath(ExecutionEngine *engine, QObject *object); + + static Heap::QObjectMethod *cloneMethod( + ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, QObject *object); }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QObjectWrapper::Flags) + inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) { if (Q_UNLIKELY(QQmlData::wasDeleted(object))) @@ -208,8 +248,20 @@ inline ReturnedValue QObjectWrapper::wrapConst(ExecutionEngine *engine, QObject return wrapConst_slowPath(engine, object); } +inline bool canConvert(const QQmlPropertyCache *fromMo, const QQmlPropertyCache *toMo) +{ + while (fromMo) { + if (fromMo == toMo) + return true; + fromMo = fromMo->parent().data(); + } + return false; +} + template <typename ReversalFunctor> -inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup) +inline ReturnedValue QObjectWrapper::lookupPropertyGetterImpl( + Lookup *lookup, ExecutionEngine *engine, const Value &object, + QObjectWrapper::Flags flags, ReversalFunctor revertLookup) { // we can safely cast to a QV4::Object here. If object is something else, // the internal class won't match @@ -228,24 +280,69 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE const QQmlPropertyData *property = lookup->qobjectLookup.propertyData; if (ddata->propertyCache.data() != lookup->qobjectLookup.propertyCache) { - if (property->isOverridden() && (!useOriginalProperty || property->isFunction() || property->isSignalHandler())) + // If the property is overridden and the lookup allows overrides to be considered, + // we have to revert here and redo the lookup from scratch. + if (property->isOverridden() + && ((flags & AllowOverride) + || property->isFunction() + || property->isSignalHandler())) { return revertLookup(); - - const QQmlPropertyCache *fromMo = ddata->propertyCache.data(); - const QQmlPropertyCache *toMo = lookup->qobjectLookup.propertyCache; - bool canConvert = false; - while (fromMo) { - if (fromMo == toMo) { - canConvert = true; - break; - } - fromMo = fromMo->parent().data(); } - if (!canConvert) + + if (!canConvert(ddata->propertyCache.data(), lookup->qobjectLookup.propertyCache)) return revertLookup(); } - return getProperty(engine, qobj, property); + return getProperty(engine, qobj, property, flags); +} + +template <typename ReversalFunctor> +inline ReturnedValue QObjectWrapper::lookupMethodGetterImpl( + Lookup *lookup, ExecutionEngine *engine, const Value &object, + QObjectWrapper::Flags flags, 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->qobjectMethodLookup.ic) + return revertLookup(); + + const Heap::QObjectWrapper *This = 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(); + + const QQmlPropertyData *property = lookup->qobjectMethodLookup.propertyData; + if (ddata->propertyCache.data() != lookup->qobjectMethodLookup.propertyCache) { + if (property && property->isOverridden()) + return revertLookup(); + + if (!canConvert(ddata->propertyCache.data(), lookup->qobjectMethodLookup.propertyCache)) + return revertLookup(); + } + + if (Heap::QObjectMethod *method = lookup->qobjectMethodLookup.method) { + if (lookup->forCall && !method->isDetached()) + method = lookup->qobjectMethodLookup.method = cloneMethod(engine, method, nullptr); + else if (!lookup->forCall && !method->isAttachedTo(qobj)) + method = lookup->qobjectMethodLookup.method = cloneMethod(engine, method, qobj); + return method ? method->asReturnedValue() : revertLookup(); + } + + if (!property) // was toString() or destroy() + return revertLookup(); + + QV4::Scope scope(engine); + QV4::ScopedValue v(scope, getProperty(engine, qobj, property, flags)); + if (!v->as<QObjectMethod>()) + return revertLookup(); + + lookup->qobjectMethodLookup.method = static_cast<Heap::QObjectMethod *>(v->heapObject()); + return v->asReturnedValue(); } struct QQmlValueTypeWrapper; @@ -259,12 +356,13 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index); static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index); + static ReturnedValue create(QV4::ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, QObject *object); int methodIndex() const { return d()->index; } QObject *object() const { return d()->object(); } - QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine) const; - QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const Value *args, int argc) const; + QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine, const QV4::Value *thisObject) const; + QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const QV4::Value *thisObject, const Value *args, int argc) const; static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 9b7a0013f8..b4bd3c4cd6 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -208,9 +208,15 @@ struct QV4QPointer { init(o); return *this; } + bool isNull() const noexcept { - return d == nullptr || qObject == nullptr || d->strongref.loadRelaxed() == 0; + return !isValid() || d->strongref.loadRelaxed() == 0; + } + + bool isValid() const noexcept + { + return d != nullptr && qObject != nullptr; } private: diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index 7cd61e3f8d..81416b6a0d 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -1168,6 +1168,7 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType QV4::Lookup l; l.clear(); l.nameIndex = nameIndex; + l.forCall = false; ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit; switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) { case ObjectLookupResult::Object: { @@ -1242,7 +1243,7 @@ bool AOTCompiledContext::callQmlContextPropertyLookup( return false; } - function->call(nullptr, args, types, argc); + function->call(qmlScopeObject, args, types, argc); return !scope.hasException(); } diff --git a/src/qml/qml/qqmlobjectorgadget_p.h b/src/qml/qml/qqmlobjectorgadget_p.h index c9090db18b..ee7c8de65f 100644 --- a/src/qml/qml/qqmlobjectorgadget_p.h +++ b/src/qml/qml/qqmlobjectorgadget_p.h @@ -37,6 +37,8 @@ public: void metacall(QMetaObject::Call type, int index, void **argv) const; + bool isNull() const { return ptr.isNull(); } + private: QBiPointer<QObject, void> ptr; }; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index ab0c7e32ef..a7af7efb3c 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -190,7 +190,9 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons // check for property. bool ok; - const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); + const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty( + v4, context, qobjectSingleton, name, + QV4::QObjectWrapper::AttachMethods, &ok); if (hasProperty) *hasProperty = ok; @@ -232,7 +234,9 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons object, type.attachedPropertiesFunction(QQmlEnginePrivate::get(v4->qmlEngine()))); if (ao) - return QV4::QObjectWrapper::getQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty); + return QV4::QObjectWrapper::getQmlProperty( + v4, context, ao, name, QV4::QObjectWrapper::AttachMethods, + hasProperty); // Fall through to base implementation } @@ -297,13 +301,16 @@ bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, QObject *ao = qmlAttachedPropertiesObject( object, type.attachedPropertiesFunction(QQmlEnginePrivate::get(e))); if (ao) - return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty( + scope.engine, context, ao, name, QV4::QObjectWrapper::NoFlag, value); return false; } else if (type.isSingleton()) { QQmlEnginePrivate *e = QQmlEnginePrivate::get(scope.engine->qmlEngine()); if (type.isQObjectSingleton() || type.isCompositeSingleton()) { if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) - return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty( + scope.engine, context, qobjectSingleton, name, + QV4::QObjectWrapper::NoFlag, value); } else { QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); @@ -419,9 +426,15 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, const QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); if (property) { ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); - setupQObjectLookup(lookup, ddata, property, - val->objectValue(), This); - lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; + if (qualifiesForMethodLookup(property)) { + setupQObjectMethodLookup( + lookup, ddata, property, val->objectValue(), nullptr); + lookup->getter = QQmlTypeWrapper::lookupSingletonMethod; + } else { + setupQObjectLookup( + lookup, ddata, property, val->objectValue(), This); + lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; + } return lookup->getter(lookup, engine, *object); } // Fall through to base implementation @@ -497,6 +510,16 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin // 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()); + + // The qmlTypeIc check is not strictly necessary. + // If we have different ways to get to the same QObject type + // we can use the same lookup to get its properties, no matter + // how we've found the object. Most of the few times this check + // fails, we will, of course have different object types. So + // this check provides an early exit for the error case. + // + // So, if we ever need more bits in qobjectLookup, qmlTypeIc is the + // member to be replaced. if (!o || o->internalClass != l->qobjectLookup.qmlTypeIc) return revertLookup(); @@ -515,7 +538,42 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin Scope scope(engine); ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton)); - return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::AllowOverride + : (QObjectWrapper::AttachMethods | QObjectWrapper::AllowOverride); + return QObjectWrapper::lookupPropertyGetterImpl(l, engine, obj, flags, revertLookup); +} + +ReturnedValue QQmlTypeWrapper::lookupSingletonMethod(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [l, engine, &object]() { + l->qobjectMethodLookup.propertyCache->release(); + l->qobjectMethodLookup.propertyCache = nullptr; + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, object); + }; + + // We cannot safely cast here as we don't explicitly check the IC. Therefore as(). + const QQmlTypeWrapper *This = object.as<QQmlTypeWrapper>(); + if (!This) + return revertLookup(); + + QQmlType type = This->d()->type(); + if (!type.isValid()) + return revertLookup(); + + if (!type.isQObjectSingleton() && !type.isCompositeSingleton()) + return revertLookup(); + + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine()); + QObject *qobjectSingleton = e->singletonInstance<QObject *>(type); + Q_ASSERT(qobjectSingleton); + + Scope scope(engine); + ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton)); + return QObjectWrapper::lookupMethodGetterImpl( + l, engine, obj, l->forCall ? QObjectWrapper::NoFlag : QObjectWrapper::AttachMethods, + revertLookup); } ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base) diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 35d5a23947..98ab664fe5 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -81,6 +81,7 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); static ReturnedValue lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &base); + static ReturnedValue lookupSingletonMethod(Lookup *l, ExecutionEngine *engine, const Value &base); static ReturnedValue lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base); static ReturnedValue lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 9e08882b59..1d91ff6bd8 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -63,6 +63,21 @@ QVariant Heap::QQmlValueTypeWrapper::toVariant() const } +ReturnedValue QQmlValueTypeReference::create( + ExecutionEngine *engine, Heap::QQmlValueTypeReference *cloneFrom, QObject *object) +{ + Scope scope(engine); + initProto(engine); + + Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocate<QQmlValueTypeReference>()); + r->d()->object = object; + r->d()->property = cloneFrom->property; + r->d()->setMetaObject(cloneFrom->metaObject()); + r->d()->setValueType(cloneFrom->valueType()); + r->d()->setGadgetPtr(nullptr); + return r->asReturnedValue(); +} + bool QQmlValueTypeReference::readReferenceValue() const { if (!d()->object) diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index eaf1b2cfd6..9d96d68c50 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -151,6 +151,8 @@ struct QQmlValueTypeReference : public QQmlValueTypeWrapper V4_OBJECT2(QQmlValueTypeReference, QQmlValueTypeWrapper) V4_NEEDS_DESTROY + static ReturnedValue create(ExecutionEngine *engine, Heap::QQmlValueTypeReference *cloneFrom, QObject *object); + bool readReferenceValue() const; }; |