aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-09-16 12:08:06 +0200
committerUlf Hermann <ulf.hermann@qt.io>2022-09-20 10:03:56 +0200
commit17bd07cbc5b6cf54716e991765ab3088a710d7b3 (patch)
tree64f0840f9a4da790c37f9a78992a39cfba19c1ae /src/qml
parenta2db40e6c070017960b9f815c66cab354e3466dc (diff)
QML: Optimize QObject method calls
So far, for each method call we had to allocate a new QObjectMethod as we didn't have any lookup to cache the methods. Introduce a new lookup for that and use it for all QObject methods. Since QObjectMethod contains a pointer to the concrete QObject the method was retrieved from, some more care has to be taken: If we are going to call the method right away, we don't need the object since we always have a thisObject and any further retrieval of the same method will result in a call again. This enables us to cache the method for any instance of the same class. When storing the method elsewhere, though, we need to hold on to the object since you can defer the call or connect a handler to a signal or similar. For such operations we do need the object. We can still optimize a bit by re-using the method cache we build the first time around. Fixes: QTBUG-95628 Change-Id: I5991180c5e0234cdc179c2b78a43dafc9083e525 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/common/qv4compileddata_p.h21
-rw-r--r--src/qml/compiler/qv4codegen.cpp25
-rw-r--r--src/qml/compiler/qv4codegen_p.h25
-rw-r--r--src/qml/compiler/qv4compiler.cpp32
-rw-r--r--src/qml/compiler/qv4compiler_p.h10
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp3
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp24
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h47
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp156
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h2
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp253
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h174
-rw-r--r--src/qml/memory/qv4heap_p.h8
-rw-r--r--src/qml/qml/qqml.cpp3
-rw-r--r--src/qml/qml/qqmlobjectorgadget_p.h2
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp74
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h1
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp15
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h2
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;
};