diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-03-22 14:47:51 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-03-22 14:57:04 +0100 |
commit | a768780f36a9913d4371c4a61706fc90bbba18c5 (patch) | |
tree | 141606d8b87fcb932172444369fa0df5cf1acf9a /src/qml/jsruntime | |
parent | b7eebec9597b38fed52710bc1dcc166d456a415d (diff) | |
parent | 56b3232a7d35fe2b856d1d87a7e1c59906b46681 (diff) |
Merge remote-tracking branch 'origin/5.13' into HEAD
Conflicts:
src/qml/compiler/qv4compileddata_p.h
src/qml/jit/qv4baselinejit.cpp
src/qml/jit/qv4jithelpers.cpp
src/qml/jsruntime/qv4lookup.cpp
src/qml/jsruntime/qv4runtime.cpp
src/qml/jsruntime/qv4runtimeapi_p.h
src/qml/jsruntime/qv4vme_moth.cpp
src/qml/qml/qqmltypemodule_p.h
Change-Id: If28793e9e08418457a11fc2c5832f03cab2fcc76
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 8 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function.cpp | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4internalclass.cpp | 12 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4lookup.cpp | 87 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4lookup_p.h | 35 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4object.cpp | 97 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4object_p.h | 8 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qmlcontext.cpp | 283 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qmlcontext_p.h | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 187 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper_p.h | 55 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 145 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimeapi_p.h | 48 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimecodegen_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4stringobject.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4value_p.h | 16 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 84 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vtable_p.h | 14 |
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) \ |