aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2019-03-22 14:47:51 +0100
committerUlf Hermann <ulf.hermann@qt.io>2019-03-22 14:57:04 +0100
commita768780f36a9913d4371c4a61706fc90bbba18c5 (patch)
tree141606d8b87fcb932172444369fa0df5cf1acf9a /src/qml/jsruntime
parentb7eebec9597b38fed52710bc1dcc166d456a415d (diff)
parent56b3232a7d35fe2b856d1d87a7e1c59906b46681 (diff)
Merge remote-tracking branch 'origin/5.13' into HEAD
Conflicts: src/qml/compiler/qv4compileddata_p.h src/qml/jit/qv4baselinejit.cpp src/qml/jit/qv4jithelpers.cpp src/qml/jsruntime/qv4lookup.cpp src/qml/jsruntime/qv4runtime.cpp src/qml/jsruntime/qv4runtimeapi_p.h src/qml/jsruntime/qv4vme_moth.cpp src/qml/qml/qqmltypemodule_p.h Change-Id: If28793e9e08418457a11fc2c5832f03cab2fcc76
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r--src/qml/jsruntime/qv4engine.cpp8
-rw-r--r--src/qml/jsruntime/qv4engine_p.h1
-rw-r--r--src/qml/jsruntime/qv4function.cpp1
-rw-r--r--src/qml/jsruntime/qv4function_p.h1
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp12
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp87
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h35
-rw-r--r--src/qml/jsruntime/qv4object.cpp97
-rw-r--r--src/qml/jsruntime/qv4object_p.h8
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp283
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h10
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp187
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h55
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp145
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h48
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen_p.h1
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4value_p.h16
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp84
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h14
20 files changed, 724 insertions, 371 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index c04617dac0..8a849ddf5f 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1189,6 +1189,14 @@ ReturnedValue ExecutionEngine::throwTypeError(const QString &message)
return throwError(error);
}
+ReturnedValue ExecutionEngine::throwReferenceError(const QString &name)
+{
+ Scope scope(this);
+ QString msg = name + QLatin1String(" is not defined");
+ ScopedObject error(scope, newReferenceErrorObject(msg));
+ return throwError(error);
+}
+
ReturnedValue ExecutionEngine::throwReferenceError(const Value &value)
{
Scope scope(this);
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 3735c24601..6df4545014 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -572,6 +572,7 @@ public:
ReturnedValue throwTypeError();
ReturnedValue throwTypeError(const QString &message);
ReturnedValue throwReferenceError(const Value &value);
+ ReturnedValue throwReferenceError(const QString &name);
ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int lineNumber, int column);
ReturnedValue throwRangeError(const Value &value);
ReturnedValue throwRangeError(const QString &message);
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 7702939e23..1bd4329fe8 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -96,7 +96,6 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
, codeData(function->code())
, jittedCode(nullptr)
, codeRef(nullptr)
- , hasQmlDependencies(function->hasQmlDependencies())
{
Scope scope(engine);
Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 374e46b929..f8125a58f8 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -94,7 +94,6 @@ public:
Heap::InternalClass *internalClass;
uint nFormals;
int interpreterCallCount = 0;
- bool hasQmlDependencies;
bool isEval = false;
static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index ddb8542e07..9906e2b1a0 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -257,11 +257,15 @@ void InternalClass::init(Heap::InternalClass *other)
void InternalClass::destroy()
{
-#ifndef QT_NO_DEBUG
for (const auto &t : transitions) {
- Q_ASSERT(!t.lookup || !t.lookup->isMarked());
- }
+ if (t.lookup) {
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(t.lookup->parent == this);
#endif
+ t.lookup->parent = nullptr;
+ }
+ }
+
if (parent && parent->engine && parent->isMarked())
parent->removeChildEntry(this);
@@ -659,8 +663,6 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b);
if (ic->prototype)
ic->prototype->mark(stack);
- if (ic->parent)
- ic->parent->mark(stack);
ic->nameMap.mark(stack);
}
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 1b6cdcbd14..c2c3fa0474 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -69,37 +69,7 @@ void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto)
ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
{
- Heap::Object *obj = object->d();
- PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- if (name.isArrayIndex()) {
- indexedLookup.index = name.asArrayIndex();
- getter = getterIndexed;
- return getter(this, engine, *object);
- }
-
- auto index = obj->internalClass->findValueOrGetter(name);
- if (index.isValid()) {
- PropertyAttributes attrs = index.attrs;
- uint nInline = obj->vtable()->nInlineProperties;
- if (attrs.isData()) {
- if (index.index < obj->vtable()->nInlineProperties) {
- index.index += obj->vtable()->inlinePropertyOffset;
- getter = getter0Inline;
- } else {
- index.index -= nInline;
- getter = getter0MemberData;
- }
- } else {
- getter = getterAccessor;
- }
- objectLookup.ic = obj->internalClass;
- objectLookup.offset = index.index;
- return getter(this, engine, *object);
- }
-
- protoLookup.protoId = obj->internalClass->protoId;
- resolveProtoGetter(name, obj->prototype());
- return getter(this, engine, *object);
+ return object->resolveLookupGetter(engine, this);
}
ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object)
@@ -409,7 +379,7 @@ ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Va
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
{
- if (object.type() == l->primitiveLookup.type) {
+ if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId)
return l->primitiveLookup.data->asReturnedValue();
@@ -420,7 +390,7 @@ ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, c
ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object)
{
- if (object.type() == l->primitiveLookup.type) {
+ if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->primitiveLookup.data;
@@ -473,56 +443,7 @@ ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engi
bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value)
{
- Scope scope(engine);
- ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
-
- Heap::InternalClass *c = object->internalClass();
- PropertyKey key = name->toPropertyKey();
- auto idx = c->findValueOrSetter(key);
- if (idx.isValid()) {
- if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
- Q_ASSERT(!idx.attrs.isAccessor());
- setter = arrayLengthSetter;
- return setter(this, engine, *object, value);
- } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
- objectLookup.ic = object->internalClass();
- objectLookup.index = idx.index;
- const auto nInline = object->d()->vtable()->nInlineProperties;
- if (idx.index < nInline) {
- setter = Lookup::setter0Inline;
- objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
- } else {
- setter = Lookup::setter0MemberData;
- objectLookup.offset = idx.index - nInline;
- }
- return setter(this, engine, *object, value);
- } else {
- // ### handle setter
- setter = setterFallback;
- }
- return setter(this, engine, *object, value);
- }
-
- insertionLookup.protoId = c->protoId;
- if (!object->put(key, value)) {
- setter = Lookup::setterFallback;
- return false;
- }
-
- if (object->internalClass() == c) {
- // ### setter in the prototype, should handle this
- setter = setterFallback;
- return true;
- }
- idx = object->internalClass()->findValueOrSetter(key);
- if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
- setter = setterFallback;
- return false;
- }
- insertionLookup.newClass = object->internalClass();
- insertionLookup.offset = idx.index;
- setter = setterInsert;
- return true;
+ return object->resolveLookupSetter(engine, this, value);
}
bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index bfe2354427..7309749a81 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -68,8 +68,10 @@ struct Lookup {
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
+ ReturnedValue (*qmlContextPropertyGetter)(Lookup *l, ExecutionEngine *engine, Value *thisObject);
bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v);
};
+ // NOTE: gc assumes the first two entries in the struct are pointers to heap objects or null
union {
struct {
Heap::Base *h1;
@@ -119,6 +121,39 @@ struct Lookup {
uint index;
uint unused;
} indexedLookup;
+ struct {
+ Heap::InternalClass *ic;
+ Heap::QObjectWrapper *staticQObject;
+ QQmlPropertyCache *propertyCache;
+ QQmlPropertyData *propertyData;
+ } qobjectLookup;
+ struct {
+ Heap::InternalClass *ic;
+ quintptr unused;
+ QQmlPropertyCache *propertyCache;
+ QQmlPropertyData *propertyData;
+ } qgadgetLookup;
+ struct {
+ quintptr unused1;
+ quintptr unused2;
+ int scriptIndex;
+ } qmlContextScriptLookup;
+ struct {
+ Heap::Object *singleton;
+ quintptr unused;
+ } qmlContextSingletonLookup;
+ struct {
+ quintptr unused1;
+ quintptr unused2;
+ int objectId;
+ } qmlContextIdObjectLookup;
+ struct {
+ // Same as protoLookup, as used for global lookups
+ quintptr reserved1;
+ quintptr reserved2;
+ quintptr reserved3;
+ ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine);
+ } qmlContextGlobalLookup;
};
uint nameIndex;
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 3d2d54f651..efab9a6454 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -320,8 +320,11 @@ bool Object::virtualDeleteProperty(Managed *m, PropertyKey id)
PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
{
if (arrayIndex != UINT_MAX && o->arrayData()) {
- if (!arrayIndex)
- arrayNode = o->sparseBegin();
+ SparseArrayNode *arrayNode = nullptr;
+ if (o->arrayType() == Heap::ArrayData::Sparse) {
+ SparseArray *sparse = o->arrayData()->sparse;
+ arrayNode = arrayIndex ? sparse->lowerBound(arrayIndex) : sparse->begin();
+ }
// sparse arrays
if (arrayNode) {
@@ -339,7 +342,6 @@ PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, Pr
*attrs = a;
return PropertyKey::fromArrayIndex(k);
}
- arrayNode = nullptr;
arrayIndex = UINT_MAX;
}
// dense arrays
@@ -734,6 +736,95 @@ ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &v
return checkedInstanceOf(engine, function, var);
}
+ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ Heap::Object *obj = object->d();
+ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+ if (name.isArrayIndex()) {
+ lookup->indexedLookup.index = name.asArrayIndex();
+ lookup->getter = Lookup::getterIndexed;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ auto index = obj->internalClass->findValueOrGetter(name);
+ if (index.isValid()) {
+ PropertyAttributes attrs = index.attrs;
+ uint nInline = obj->vtable()->nInlineProperties;
+ if (attrs.isData()) {
+ if (index.index < obj->vtable()->nInlineProperties) {
+ index.index += obj->vtable()->inlinePropertyOffset;
+ lookup->getter = Lookup::getter0Inline;
+ } else {
+ index.index -= nInline;
+ lookup->getter = Lookup::getter0MemberData;
+ }
+ } else {
+ lookup->getter = Lookup::getterAccessor;
+ }
+ lookup->objectLookup.ic = obj->internalClass;
+ lookup->objectLookup.offset = index.index;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ lookup->protoLookup.protoId = obj->internalClass->protoId;
+ lookup->resolveProtoGetter(name, obj->prototype());
+ return lookup->getter(lookup, engine, *object);
+}
+
+bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
+{
+ Scope scope(engine);
+ ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+
+ Heap::InternalClass *c = object->internalClass();
+ PropertyKey key = name->toPropertyKey();
+ auto idx = c->findValueOrSetter(key);
+ if (idx.isValid()) {
+ if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
+ Q_ASSERT(!idx.attrs.isAccessor());
+ lookup->setter = Lookup::arrayLengthSetter;
+ return lookup->setter(lookup, engine, *object, value);
+ } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
+ lookup->objectLookup.ic = object->internalClass();
+ lookup->objectLookup.index = idx.index;
+ const auto nInline = object->d()->vtable()->nInlineProperties;
+ if (idx.index < nInline) {
+ lookup->setter = Lookup::setter0Inline;
+ lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
+ } else {
+ lookup->setter = Lookup::setter0MemberData;
+ lookup->objectLookup.offset = idx.index - nInline;
+ }
+ return lookup->setter(lookup, engine, *object, value);
+ } else {
+ // ### handle setter
+ lookup->setter = Lookup::setterFallback;
+ }
+ return lookup->setter(lookup, engine, *object, value);
+ }
+
+ lookup->insertionLookup.protoId = c->protoId;
+ if (!object->put(key, value)) {
+ lookup->setter = Lookup::setterFallback;
+ return false;
+ }
+
+ if (object->internalClass() == c) {
+ // ### setter in the prototype, should handle this
+ lookup->setter = Lookup::setterFallback;
+ return true;
+ }
+ idx = object->internalClass()->findValueOrSetter(key);
+ if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
+ lookup->setter = Lookup::setterFallback;
+ return false;
+ }
+ lookup->insertionLookup.newClass = object->internalClass();
+ lookup->insertionLookup.offset = idx.index;
+ lookup->setter = Lookup::setterInsert;
+ return true;
+}
+
ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var)
{
Scope scope(engine);
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index 72b6703554..567382cbc0 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -375,6 +375,11 @@ public:
bool setProtoFromNewTarget(const Value *newTarget);
+ ReturnedValue resolveLookupGetter(ExecutionEngine *engine, Lookup *lookup) const
+ { return vtable()->resolveLookupGetter(this, engine, lookup); }
+ ReturnedValue resolveLookupSetter(ExecutionEngine *engine, Lookup *lookup, const Value &value)
+ { return vtable()->resolveLookupSetter(this, engine, lookup, value); }
+
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
@@ -389,6 +394,8 @@ protected:
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static qint64 virtualGetLength(const Managed *m);
static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
public:
// qv4runtime uses this directly
static ReturnedValue checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *typeObject, const Value &var);
@@ -408,7 +415,6 @@ struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
uint arrayIndex = 0;
uint memberIndex = 0;
bool iterateOverSymbols = false;
- SparseArrayNode *arrayNode = nullptr;
~ObjectOwnPropertyKeyIterator() override = default;
PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 88b0822f42..97b955632d 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -55,6 +55,8 @@
#include <private/qjsvalue_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4module_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4identifiertable_p.h>
QT_BEGIN_NAMESPACE
@@ -77,14 +79,11 @@ void Heap::QQmlContextWrapper::destroy()
Object::destroy();
}
-ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup)
{
- Q_ASSERT(m->as<QQmlContextWrapper>());
-
if (!id.isString())
- return Object::virtualGet(m, id, receiver, hasProperty);
+ return Object::virtualGet(resource, id, receiver, hasProperty);
- const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
@@ -100,11 +99,11 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
}
}
- return Object::virtualGet(m, id, receiver, hasProperty);
+ return Object::virtualGet(resource, id, receiver, hasProperty);
}
bool hasProp = false;
- ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp));
+ ScopedValue result(scope, Object::virtualGet(resource, id, receiver, &hasProp));
if (hasProp) {
if (hasProperty)
*hasProperty = hasProp;
@@ -160,6 +159,8 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Note: The scope object is only a QADMO for example when somebody registers a QQmlPropertyMap
// sub-class as QML type and then instantiates it in .qml.
if (scopeObject && QQmlPropertyCache::isDynamicMetaObject(scopeObject->metaObject())) {
+ // all bets are off, so don't try to optimize any lookups
+ lookup = nullptr;
if (performGobalLookUp())
return result->asReturnedValue();
}
@@ -172,11 +173,35 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
if (hasProperty)
*hasProperty = true;
if (r.scriptIndex != -1) {
+ if (lookup) {
+ lookup->qmlContextScriptLookup.scriptIndex = r.scriptIndex;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScript;
+ return lookup->qmlContextPropertyGetter(lookup, v4, base);
+ }
QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
if (scripts)
return scripts->get(r.scriptIndex);
return QV4::Encode::null();
} else if (r.type.isValid()) {
+ if (lookup) {
+ if (r.type.isSingleton()) {
+ QQmlEngine *e = v4->qmlEngine();
+ QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo();
+ siinfo->init(e);
+ if (siinfo->qobjectApi(e)) {
+ lookup->qmlContextSingletonLookup.singleton =
+ static_cast<Heap::Object*>(
+ Value::fromReturnedValue(
+ QQmlTypeWrapper::create(v4, nullptr, r.type)
+ ).heapObject());
+ } else {
+ QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e)));
+ lookup->qmlContextSingletonLookup.singleton = o->d();
+ }
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton;
+ return lookup->qmlContextPropertyGetter(lookup, v4, base);
+ }
+ }
return QQmlTypeWrapper::create(v4, scopeObject, r.type);
} else if (r.importNamespace) {
return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace);
@@ -188,6 +213,15 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
}
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine());
+ Lookup * const originalLookup = lookup;
+
+ decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty;
+
+ // minor optimization so we don't potentially try two property lookups on the same object
+ if (scopeObject == context->contextObject) {
+ scopeObject = nullptr;
+ contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty;
+ }
while (context) {
// Search context properties
@@ -198,11 +232,17 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
if (propertyIdx != -1) {
if (propertyIdx < context->idValueCount) {
+ if (hasProperty)
+ *hasProperty = true;
+
+ if (lookup) {
+ lookup->qmlContextIdObjectLookup.objectId = propertyIdx;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupIdObject;
+ return lookup->qmlContextPropertyGetter(lookup, v4, base);
+ }
if (ep->propertyCapture)
ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings);
- if (hasProperty)
- *hasProperty = true;
return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]);
} else {
@@ -229,11 +269,30 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Search scope object
if (scopeObject) {
bool hasProp = false;
+
+ QQmlPropertyData *propertyData = nullptr;
QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject,
- name, QV4::QObjectWrapper::CheckRevision, &hasProp));
+ name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData));
if (hasProp) {
if (hasProperty)
*hasProperty = true;
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(v4, scopeObject);
+
+ if (lookup && propertyData) {
+ QQmlData *ddata = QQmlData::get(scopeObject, false);
+ if (ddata && ddata->propertyCache) {
+ ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject)));
+ const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
+ lookup->qobjectLookup.ic = That->internalClass();
+ lookup->qobjectLookup.staticQObject = nullptr;
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = propertyData;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty;
+ }
+ }
+
return result->asReturnedValue();
}
}
@@ -243,27 +302,73 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Search context object
if (context->contextObject) {
bool hasProp = false;
- result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, &hasProp);
+ QQmlPropertyData *propertyData = nullptr;
+ result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject,
+ name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData);
if (hasProp) {
if (hasProperty)
*hasProperty = true;
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(v4, context->contextObject);
+
+ if (lookup && propertyData) {
+ QQmlData *ddata = QQmlData::get(context->contextObject, false);
+ if (ddata && ddata->propertyCache) {
+ ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject)));
+ const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
+ lookup->qobjectLookup.ic = That->internalClass();
+ lookup->qobjectLookup.staticQObject = nullptr;
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = propertyData;
+ lookup->qmlContextPropertyGetter = contextGetterFunction;
+ }
+ }
+
return result->asReturnedValue();
}
}
context = context->parent;
+
+ // As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond
+ // the immediate QML context (of the .qml file).
+ lookup = nullptr;
}
// Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming
// true if we access properties of the global object.
- if (performGobalLookUp())
- return result->asReturnedValue();
+ if (originalLookup) {
+ // Try a lookup in the global object. It's theoretically possible to first find a property
+ // in the global object and then later a context property with the same name is added, but that
+ // never really worked as we used to detect access to global properties at type compile time anyway.
+ lookup = originalLookup;
+ result = lookup->resolveGlobalGetter(v4);
+ if (lookup->globalGetter != Lookup::globalGetterGeneric) {
+ if (hasProperty)
+ *hasProperty = true;
+ lookup->qmlContextGlobalLookup.getterTrampoline = lookup->globalGetter;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
+ return result->asReturnedValue();
+ }
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ } else {
+ if (performGobalLookUp())
+ return result->asReturnedValue();
+ }
expressionContext->unresolvedNames = true;
return Encode::undefined();
}
+ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+{
+ Q_ASSERT(m->as<QQmlContextWrapper>());
+ const QQmlContextWrapper *This = static_cast<const QQmlContextWrapper *>(m);
+ return getPropertyAndBase(This, id, receiver, hasProperty, /*base*/nullptr);
+}
+
bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
@@ -298,8 +403,16 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
while (context) {
const QV4::IdentifierHash &properties = context->propertyNames();
// Search context properties
- if (properties.count() && properties.value(name) != -1)
- return false;
+ if (properties.count()) {
+ const int propertyIndex = properties.value(name);
+ if (propertyIndex != -1) {
+ if (propertyIndex < context->idValueCount) {
+ v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue"));
+ return false;
+ }
+ return false;
+ }
+ }
// Search scope object
if (scopeObject &&
@@ -323,6 +436,146 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
return false;
}
+ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Scope scope(engine);
+ PropertyKey name =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[l->nameIndex]);
+
+ // Special hack for bounded signal expressions, where the parameters of signals are injected
+ // into the handler expression through the locals of the call context. So for onClicked: { ... }
+ // the parameters of the clicked signal are injected and we must allow for them to be found here
+ // before any other property from the QML context.
+ ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context);
+ if (ctx.d()->type == Heap::ExecutionContext::Type_CallContext) {
+ uint index = ctx.d()->internalClass->indexOfValueOrGetter(name);
+ if (index < UINT_MAX)
+ return static_cast<Heap::CallContext*>(ctx.d())->locals[index].asReturnedValue();
+ }
+
+ Scoped<QQmlContextWrapper> qmlContext(scope, engine->qmlContext()->qml());
+ bool hasProperty = false;
+ ScopedValue result(scope, QQmlContextWrapper::getPropertyAndBase(qmlContext, name, /*receiver*/nullptr,
+ &hasProperty, base, l));
+ if (!hasProperty)
+ return engine->throwReferenceError(name.toQString());
+ return result->asReturnedValue();
+}
+
+ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::null();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::null();
+
+ QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
+ if (!scripts)
+ return QV4::Encode::null();
+ return scripts->get(l->qmlContextScriptLookup.scriptIndex);
+}
+
+ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(base)
+ return Value::fromHeapObject(l->qmlContextSingletonLookup.singleton).asReturnedValue();
+}
+
+ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::null();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::null();
+
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine->qmlEngine());
+ const int objectId = l->qmlContextIdObjectLookup.objectId;
+
+ if (qmlEngine->propertyCapture)
+ qmlEngine->propertyCapture->captureProperty(&context->idValues[objectId].bindings);
+
+ return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]);
+}
+
+ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QObject *scopeObject = qmlContext->qmlScope();
+ if (!scopeObject)
+ return QV4::Encode::undefined();
+
+ if (QQmlData::wasDeleted(scopeObject))
+ return QV4::Encode::undefined();
+
+ const auto revertLookup = [l, engine, base]() {
+ l->qobjectLookup.propertyCache->release();
+ l->qobjectLookup.propertyCache = nullptr;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
+ };
+
+ ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, scopeObject));
+ return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
+}
+
+ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::undefined();
+
+ QObject *contextObject = context->contextObject;
+ if (!contextObject)
+ return QV4::Encode::undefined();
+
+ if (QQmlData::wasDeleted(contextObject))
+ return QV4::Encode::undefined();
+
+ const auto revertLookup = [l, engine, base]() {
+ l->qobjectLookup.propertyCache->release();
+ l->qobjectLookup.propertyCache = nullptr;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
+ };
+
+ ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, contextObject));
+ return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
+}
+
+ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base);
+ ReturnedValue result = l->qmlContextGlobalLookup.getterTrampoline(l, engine);
+ // In the unlikely event of mutation of the global object, update the trampoline.
+ if (l->qmlContextPropertyGetter != lookupInGlobalObject) {
+ l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
+ }
+ return result;
+}
+
void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
{
Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext);
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index dd6de3323d..6375294375 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -99,8 +99,18 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
inline QObject *getScopeObject() const { return d()->scopeObject; }
inline QQmlContextData *getContext() const { return *d()->context; }
+ static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver,
+ bool *hasProperty, Value *base, Lookup *lookup = nullptr);
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
+
+ static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 15f064ba7a..d85e8c94eb 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -56,6 +56,8 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4variantobject_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4lookup_p.h>
#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
@@ -231,7 +233,7 @@ QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject
return result;
}
-ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired)
+ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property)
{
QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex()));
@@ -257,7 +259,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
- if (captureRequired && ep && ep->propertyCapture && !property->isConstant())
+ if (ep && ep->propertyCapture && !property->isConstant())
ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
if (property->isVarProperty()) {
@@ -269,9 +271,53 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
}
}
+static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr)
+{
+ int index = 0;
+ if (name->equals(v4->id_destroy()))
+ index = QV4::QObjectMethod::DestroyMethod;
+ else if (name->equals(v4->id_toString()))
+ index = QV4::QObjectMethod::ToStringMethod;
+ else
+ return OptionalReturnedValue();
+
+ if (hasProperty)
+ *hasProperty = true;
+ ExecutionContext *global = v4->rootContext();
+ return OptionalReturnedValue(QV4::QObjectMethod::create(global, qobj, index));
+}
+
+static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, QQmlContextData *qmlContext, QObject *qobj,
+ bool *hasProperty = nullptr)
+{
+ if (!qmlContext || !qmlContext->imports)
+ return OptionalReturnedValue();
+
+ QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
+
+ if (hasProperty)
+ *hasProperty = true;
+
+ if (!r.isValid())
+ return OptionalReturnedValue();
+
+ if (r.scriptIndex != -1) {
+ return OptionalReturnedValue(QV4::Encode::undefined());
+ } else if (r.type.isValid()) {
+ return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
+ } else if (r.importNamespace) {
+ return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj, qmlContext->imports, r.importNamespace,
+ Heap::QQmlTypeWrapper::ExcludeEnums));
+ }
+ Q_UNREACHABLE();
+ return OptionalReturnedValue();
+}
+
ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode,
bool *hasProperty, bool includeImports) const
{
+ // Keep this code in sync with ::virtualResolveLookupGetter
+
if (QQmlData::wasDeleted(d()->object())) {
if (hasProperty)
*hasProperty = false;
@@ -280,39 +326,17 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
ExecutionEngine *v4 = engine();
- if (name->equals(v4->id_destroy()) || name->equals(v4->id_toString())) {
- int index = name->equals(v4->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
- if (hasProperty)
- *hasProperty = true;
- ExecutionContext *global = v4->rootContext();
- return QV4::QObjectMethod::create(global, d()->object(), index);
- }
+ if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty))
+ return *methodValue;
QQmlPropertyData local;
QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local);
if (!result) {
+ // Check for attached properties
if (includeImports && name->startsWithUpper()) {
- // Check for attached properties
- if (qmlContext && qmlContext->imports) {
- QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
-
- if (hasProperty)
- *hasProperty = true;
-
- if (r.isValid()) {
- if (r.scriptIndex != -1) {
- return QV4::Encode::undefined();
- } else if (r.type.isValid()) {
- return QQmlTypeWrapper::create(v4, d()->object(),
- r.type, Heap::QQmlTypeWrapper::ExcludeEnums);
- } else if (r.importNamespace) {
- return QQmlTypeWrapper::create(v4, d()->object(),
- qmlContext->imports, r.importNamespace, Heap::QQmlTypeWrapper::ExcludeEnums);
- }
- Q_ASSERT(!"Unreachable");
- }
- }
+ if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty))
+ return *importProperty;
}
return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty);
}
@@ -333,27 +357,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
return getProperty(v4, d()->object(), result);
}
-ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired)
-{
- if (QQmlData::wasDeleted(object))
- return QV4::Encode::null();
- QQmlData *ddata = QQmlData::get(object, /*create*/false);
- if (!ddata)
- return QV4::Encode::undefined();
-
- if (Q_UNLIKELY(!ddata->propertyCache)) {
- ddata->propertyCache = QQmlEnginePrivate::get(engine)->cache(object->metaObject());
- ddata->propertyCache->addref();
- }
-
- QQmlPropertyCache *cache = ddata->propertyCache;
- Q_ASSERT(cache);
- QQmlPropertyData *property = cache->property(propertyIndex);
- Q_ASSERT(property); // We resolved this property earlier, so it better exist!
- return getProperty(engine, object, property, captureRequired);
-}
-
-ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty)
+ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property)
{
if (QQmlData::wasDeleted(object)) {
if (hasProperty)
@@ -361,13 +365,8 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC
return QV4::Encode::null();
}
- if (name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) {
- int index = name->equals(engine->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
- if (hasProperty)
- *hasProperty = true;
- ExecutionContext *global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, index);
- }
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty))
+ return *methodValue;
QQmlData *ddata = QQmlData::get(object, false);
QQmlPropertyData local;
@@ -385,6 +384,9 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC
if (hasProperty)
*hasProperty = true;
+ if (property)
+ *property = result;
+
return getProperty(engine, object, result);
} else {
// Check if this object is already wrapped.
@@ -829,6 +831,71 @@ OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m,
return new QObjectWrapperOwnPropertyKeyIterator;
}
+ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ // Keep this code in sync with ::getQmlProperty
+ PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[lookup->nameIndex]);
+ if (!id.isString())
+ return Object::virtualResolveLookupGetter(object, engine, lookup);
+ Scope scope(engine);
+
+ const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
+ ScopedString name(scope, id.asStringOrSymbol());
+ QQmlContextData *qmlContext = engine->callingQmlContext();
+
+ QObject * const qobj = This->d()->object();
+
+ if (QQmlData::wasDeleted(qobj))
+ return QV4::Encode::undefined();
+
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj))
+ return *methodValue;
+
+ QQmlData *ddata = QQmlData::get(qobj, false);
+ if (!ddata || !ddata->propertyCache) {
+ QQmlPropertyData local;
+ QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local);
+ return getProperty(engine, qobj, property);
+ }
+ QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
+
+ if (!property) {
+ // Check for attached properties
+ if (name->startsWithUpper()) {
+ if (auto importProperty = getPropertyFromImports(engine, name, qmlContext, qobj))
+ return *importProperty;
+ }
+ return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
+ }
+
+ lookup->qobjectLookup.ic = This->internalClass();
+ lookup->qobjectLookup.staticQObject = nullptr;
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = property;
+ lookup->getter = QV4::QObjectWrapper::lookupGetter;
+ return lookup->getter(lookup, engine, *object);
+}
+
+ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
+{
+ const auto revertLookup = [lookup, engine, &object]() {
+ lookup->qobjectLookup.propertyCache->release();
+ lookup->qobjectLookup.propertyCache = nullptr;
+ lookup->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(lookup, engine, object);
+ };
+
+ return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
+}
+
+bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
+ const Value &value)
+{
+ return Object::virtualResolveLookupSetter(object, engine, lookup, value);
+}
+
namespace QV4 {
struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
@@ -1923,13 +1990,13 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in
return method.asReturnedValue();
}
-ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index)
+ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->setPropertyCache(valueType->d()->propertyCache());
+ method->d()->setPropertyCache(valueType->propertyCache());
method->d()->index = index;
- method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d());
+ method->d()->valueTypeWrapper.set(valueScope.engine, valueType);
return method.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 43a53ac673..795bf241f2 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -60,6 +60,7 @@
#include <private/qv4value_p.h>
#include <private/qv4functionobject_p.h>
+#include <private/qv4lookup_p.h>
QT_BEGIN_NAMESPACE
@@ -165,7 +166,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
QObject *object() const { return d()->object(); }
ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, bool includeImports = false) const;
- static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr);
+ static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr);
static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value);
@@ -174,13 +175,18 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
using Object::get;
- static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired);
static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value);
void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value);
void destroyObject(bool lastCall);
- static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true);
+ static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property);
+
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
+ template <typename ReversalFunctor> static ReturnedValue lookupGetterImpl(Lookup *l, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revert);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
+
protected:
static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value);
@@ -216,6 +222,47 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje
return wrap_slowPath(engine, object);
}
+template <typename ReversalFunctor>
+inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup)
+{
+ // we can safely cast to a QV4::Object here. If object is something else,
+ // the internal class won't match
+ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
+ if (!o || o->internalClass != lookup->qobjectLookup.ic)
+ return revertLookup();
+
+ const Heap::QObjectWrapper *This = lookup->qobjectLookup.staticQObject ? lookup->qobjectLookup.staticQObject :
+ static_cast<const Heap::QObjectWrapper *>(o);
+ QObject *qobj = This->object();
+ if (QQmlData::wasDeleted(qobj))
+ return QV4::Encode::undefined();
+
+ QQmlData *ddata = QQmlData::get(qobj, /*create*/false);
+ if (!ddata)
+ return revertLookup();
+
+ QQmlPropertyData *property = lookup->qobjectLookup.propertyData;
+ if (ddata->propertyCache != lookup->qobjectLookup.propertyCache) {
+ if (property->isOverridden() && (!useOriginalProperty || property->isFunction() || property->isSignalHandler()))
+ return revertLookup();
+
+ QQmlPropertyCache *fromMo = ddata->propertyCache;
+ QQmlPropertyCache *toMo = lookup->qobjectLookup.propertyCache;
+ bool canConvert = false;
+ while (fromMo) {
+ if (fromMo == toMo) {
+ canConvert = true;
+ break;
+ }
+ fromMo = fromMo->parent();
+ }
+ if (!canConvert)
+ return revertLookup();
+ }
+
+ return getProperty(engine, qobj, property);
+}
+
struct QQmlValueTypeWrapper;
struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
@@ -226,7 +273,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
enum { DestroyMethod = -1, ToStringMethod = -2 };
static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index);
- static ReturnedValue create(QV4::ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index);
+ static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index);
int methodIndex() const { return d()->index; }
QObject *object() const { return d()->object(); }
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 9753ee4b1d..de46d046c9 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1130,6 +1130,12 @@ ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function
return l->globalGetter(l, engine);
}
+ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index)
+{
+ Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ return l->qmlContextPropertyGetter(l, engine, nullptr);
+}
+
ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index)
{
Lookup *l = f->compilationUnit->runtimeLookups + index;
@@ -1390,18 +1396,43 @@ uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const
return v->booleanValue();
}
+static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
+{
+ QString objectAsString = QStringLiteral("[null]");
+ if (!thisObject->isUndefined())
+ objectAsString = thisObject->toQStringNoThrow();
+ QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(propertyName, objectAsString);
+ return engine->throwTypeError(msg);
+}
ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc)
{
+ Scope scope(engine);
Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
Value function = Value::fromReturnedValue(l->globalGetter(l, engine));
+ Value thisObject = Value::undefinedValue();
if (!function.isFunctionObject())
- return engine->throwTypeError();
+ return throwPropertyIsNotAFunctionTypeError(engine, &thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
- Value thisObject = Value::undefinedValue();
return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc);
}
+ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index,
+ Value *argv, int argc)
+{
+ Scope scope(engine);
+ ScopedValue thisObject(scope);
+ Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject));
+ if (!function.isFunctionObject())
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
+
+ return static_cast<FunctionObject &>(function).call(thisObject, argv, argc);
+}
+
ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc)
{
Scope scope(engine);
@@ -1412,13 +1443,8 @@ ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Val
if (engine->hasException)
return Encode::undefined();
- if (!function) {
- QString objectAsString = QStringLiteral("[null]");
- if (!thisObject->isUndefined())
- objectAsString = thisObject->toQStringNoThrow();
- QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString);
- return engine->throwTypeError(msg);
- }
+ if (!function)
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject, QLatin1String("eval"));
if (function->d() == engine->evalFunction()->d())
return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true);
@@ -1437,15 +1463,9 @@ ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Va
if (engine->hasException)
return Encode::undefined();
- if (!f) {
- QString objectAsString = QStringLiteral("[null]");
- if (!thisObject->isUndefined())
- objectAsString = thisObject->toQStringNoThrow();
- QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
- objectAsString);
- return engine->throwTypeError(msg);
- }
+ if (!f)
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString());
return f->call(thisObject, argv, argc);
}
@@ -1536,41 +1556,6 @@ ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Val
return static_cast<const FunctionObject &>(func).call(&thisObject, argv, argc);
}
-ReturnedValue Runtime::CallQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &base,
- int propertyIndex, Value argv[], int argc)
-{
- Scope scope(engine);
- ScopedFunctionObject fo(scope, LoadQmlScopeObjectProperty::call(engine, base, propertyIndex,
- /*captureRequired*/true));
- if (!fo) {
- QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex);
- return engine->throwTypeError(error);
- }
-
- QObject *qmlScopeObj = static_cast<const QmlContext *>(&base)->d()->qml()->scopeObject;
- ScopedValue qmlScopeValue(scope, QObjectWrapper::wrap(engine, qmlScopeObj));
- return fo->call(qmlScopeValue, argv, argc);
-}
-
-ReturnedValue Runtime::CallQmlContextObjectProperty::call(ExecutionEngine *engine,
- const Value &base,
- int propertyIndex,
- Value argv[],
- int argc)
-{
- Scope scope(engine);
- ScopedFunctionObject fo(scope, LoadQmlContextObjectProperty::call(engine, base, propertyIndex,
- /*captureRequired*/true));
- if (!fo) {
- QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex);
- return engine->throwTypeError(error);
- }
-
- QObject *qmlContextObj = static_cast<const QmlContext *>(&base)->d()->qml()->context->contextData()->contextObject;
- ScopedValue qmlContextValue(scope, QObjectWrapper::wrap(engine, qmlContextObj));
- return fo->call(qmlContextValue, argv, argc);
-}
-
struct CallArgs {
Value *argv;
int argc;
@@ -2013,66 +1998,12 @@ QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, i
return engine->newArrayObject(values, nValues)->asReturnedValue();
}
-
-ReturnedValue Runtime::LoadQmlContext::call(ExecutionEngine *engine)
-{
- Heap::QmlContext *ctx = engine->qmlContext();
- Q_ASSERT(ctx);
- return ctx->asReturnedValue();
-}
-
ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id)
{
Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>());
return ro->asReturnedValue();
}
-ReturnedValue Runtime::LoadQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, c.d()->qml()->scopeObject, propertyIndex, captureRequired);
-}
-
-ReturnedValue Runtime::LoadQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, captureRequired);
-}
-
-ReturnedValue Runtime::LoadQmlIdObject::call(ExecutionEngine *engine, const Value &c, uint index)
-{
- const QmlContext &qmlContext = static_cast<const QmlContext &>(c);
- QQmlContextData *context = *qmlContext.d()->qml()->context;
- if (!context || index >= (uint)context->idValueCount)
- return Encode::undefined();
-
- QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
- if (ep && ep->propertyCapture)
- ep->propertyCapture->captureProperty(&context->idValues[index].bindings);
-
- return QObjectWrapper::wrap(engine, context->idValues[index].data());
-}
-
-void Runtime::StoreQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::setProperty(engine, c.d()->qml()->scopeObject, propertyIndex, value);
-}
-
-void Runtime::StoreQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, value);
-}
-
-ReturnedValue Runtime::LoadQmlImportedScripts::call(ExecutionEngine *engine)
-{
- QQmlContextData *context = engine->callingQmlContext();
- if (!context)
- return Encode::undefined();
- return context->importedScripts.value();
-}
-
ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj)
{
if (obj.isObject())
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 0312522d90..86cbccde23 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -58,7 +58,6 @@ namespace QV4 {
typedef uint Bool;
-
struct Q_QML_PRIVATE_EXPORT Runtime {
typedef ReturnedValue (*UnaryOperation)(const Value &value);
typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right);
@@ -87,6 +86,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
{
static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
};
+ struct Q_QML_PRIVATE_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
+ };
struct Q_QML_PRIVATE_EXPORT CallName : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, int, Value[], int);
@@ -187,6 +190,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
{
static ReturnedValue call(ExecutionEngine *, Function *, int);
};
+ struct Q_QML_PRIVATE_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, uint);
+ };
struct Q_QML_PRIVATE_EXPORT GetLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, Function *, const Value &, int);
@@ -495,45 +502,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
static ReturnedValue call(Function *, int);
};
- /* qml */
- struct Q_QML_PRIVATE_EXPORT LoadQmlContext : Method<Throws::No>
- {
- static ReturnedValue call(ExecutionEngine *);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlImportedScripts : Method<Throws::No>
- {
- static ReturnedValue call(ExecutionEngine *);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlScopeObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlContextObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlIdObject : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, uint);
- };
- struct Q_QML_PRIVATE_EXPORT CallQmlScopeObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
- };
- struct Q_QML_PRIVATE_EXPORT CallQmlContextObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
- };
-
- struct Q_QML_PRIVATE_EXPORT StoreQmlScopeObjectProperty : Method<Throws::Yes>
- {
- static void call(ExecutionEngine *, const Value &, int, const Value &);
- };
- struct Q_QML_PRIVATE_EXPORT StoreQmlContextObjectProperty : Method<Throws::Yes>
- {
- static void call(ExecutionEngine *, const Value &, int, const Value &);
- };
-
struct StackOffsets {
static const int tailCall_function = -1;
static const int tailCall_thisObject = -2;
diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h
index be66dc57ca..006a6a3cde 100644
--- a/src/qml/jsruntime/qv4runtimecodegen_p.h
+++ b/src/qml/jsruntime/qv4runtimecodegen_p.h
@@ -71,6 +71,7 @@ public:
void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override;
void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override;
+
private:
ExecutionEngine *engine;
};
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 8186153ba4..dee6a67792 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -130,7 +130,7 @@ PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Prope
return PropertyKey::fromArrayIndex(index);
} else if (arrayIndex == slen) {
if (s->arrayData()) {
- arrayNode = s->sparseBegin();
+ SparseArrayNode *arrayNode = s->sparseBegin();
// iterate until we're past the end of the string
while (arrayNode && arrayNode->key() < slen)
arrayNode = arrayNode->nextNode();
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index b1cae8796f..da08841026 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -877,6 +877,22 @@ struct ValueArray {
// have wrong offsets between host and target.
Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8);
+class OptionalReturnedValue {
+ ReturnedValue value;
+public:
+
+ OptionalReturnedValue() : value(Value::emptyValue().asReturnedValue()) {}
+ explicit OptionalReturnedValue(ReturnedValue v)
+ : value(v)
+ {
+ Q_ASSERT(!Value::fromReturnedValue(v).isEmpty());
+ }
+
+ ReturnedValue operator->() const { return value; }
+ ReturnedValue operator*() const { return value; }
+ explicit operator bool() const { return !Value::fromReturnedValue(value).isEmpty(); }
+};
+
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index e3cdc4552a..98e4f4f7b9 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -669,6 +669,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadGlobalLookup)
+ MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
+ STORE_IP();
+ QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ acc = l->qmlContextPropertyGetter(l, engine, nullptr);
+ CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
+ MOTH_END_INSTR(LoadQmlContextPropertyLookup)
+
MOTH_BEGIN_INSTR(StoreNameStrict)
STORE_IP();
STORE_ACC();
@@ -719,7 +727,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(GetLookup)
STORE_IP();
STORE_ACC();
+
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+
+ if (accumulator.isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot read property '%1' of %2")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(accumulator.toQStringNoThrow());
+ acc = engine->throwTypeError(message);
+ goto handleUnwind;
+ }
+
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
@@ -755,37 +773,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreSuperProperty)
- MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
- STORE_ACC();
- Runtime::StoreQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(StoreScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
- STORE_IP();
- acc = Runtime::LoadQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(StoreContextObjectProperty)
- STORE_IP();
- STORE_ACC();
- Runtime::StoreQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(StoreContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadContextObjectProperty)
- STORE_IP();
- acc = Runtime::LoadQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadIdObject)
- STORE_IP();
- acc = Runtime::LoadQmlIdObject::call(engine, STACK_VALUE(base), index);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadIdObject)
-
MOTH_BEGIN_INSTR(Yield)
frame->yield = code;
frame->yieldIsIterator = false;
@@ -852,11 +839,23 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(CallPropertyLookup)
STORE_IP();
Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex;
+
+ if (stack[base].isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot call method '%1' of %2")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(stack[base].toQStringNoThrow());
+ acc = engine->throwTypeError(message);
+ goto handleUnwind;
+ }
+
// ok to have the value on the stack here
Value f = Value::fromReturnedValue(l->getter(l, engine, stack[base]));
if (Q_UNLIKELY(!f.isFunctionObject())) {
- acc = engine->throwTypeError();
+ QString message = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(stack[base].toQStringNoThrow());
+ acc = engine->throwTypeError(message);
goto handleUnwind;
}
@@ -893,19 +892,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallGlobalLookup)
- MOTH_BEGIN_INSTR(CallScopeObjectProperty)
+ MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
STORE_IP();
- acc = Runtime::CallQmlScopeObjectProperty::call(engine, stack[base], name, stack + argv, argc);
+ acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
- MOTH_END_INSTR(CallScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(CallContextObjectProperty)
- STORE_IP();
- acc = Runtime::CallQmlContextObjectProperty::call(engine, stack[base], name, stack + argv, argc);
- CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
- MOTH_END_INSTR(CallContextObjectProperty)
+ MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
STORE_IP();
@@ -1520,14 +1512,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
#endif // QT_CONFIG(qml_debug)
MOTH_END_INSTR(Debug)
- MOTH_BEGIN_INSTR(LoadQmlContext)
- STACK_VALUE(result) = Runtime::LoadQmlContext::call(engine);
- MOTH_END_INSTR(LoadQmlContext)
-
- MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
- STACK_VALUE(result) = Runtime::LoadQmlImportedScripts::call(engine);
- MOTH_END_INSTR(LoadQmlImportedScripts)
-
handleUnwind:
Q_ASSERT(engine->hasException || frame->unwindLevel);
if (!frame->unwindHandler) {
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
index 00dcb962d3..a4d91640c5 100644
--- a/src/qml/jsruntime/qv4vtable_p.h
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+struct Lookup;
+
struct OwnPropertyKeyIterator {
virtual ~OwnPropertyKeyIterator() = 0;
virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
@@ -84,6 +86,9 @@ struct VTable
typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget);
+ typedef ReturnedValue (*ResolveLookupGetter)(const Object *, ExecutionEngine *, Lookup *);
+ typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &);
+
const VTable * const parent;
quint16 inlinePropertyOffset;
quint16 nInlineProperties;
@@ -118,6 +123,9 @@ struct VTable
Call call;
CallAsConstructor callAsConstructor;
+
+ ResolveLookupGetter resolveLookupGetter;
+ ResolveLookupSetter resolveLookupSetter;
};
@@ -142,6 +150,9 @@ protected:
static constexpr VTable::Call virtualCall = nullptr;
static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr;
+
+ static constexpr VTable::ResolveLookupGetter virtualResolveLookupGetter = nullptr;
+ static constexpr VTable::ResolveLookupSetter virtualResolveLookupSetter = nullptr;
};
#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
@@ -181,6 +192,9 @@ protected:
\
classname::virtualCall, \
classname::virtualCallAsConstructor, \
+ \
+ classname::virtualResolveLookupGetter, \
+ classname::virtualResolveLookupSetter \
}
#define DEFINE_MANAGED_VTABLE(classname) \