aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
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;
};