aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4qobjectwrapper.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2018-12-21 16:00:48 +0100
committerSimon Hausmann <simon.hausmann@qt.io>2019-03-19 21:44:31 +0000
commitdb292d1fe70a0cfaf315a72d099441cf3969e284 (patch)
treeb628abb412e6169976aec5c2a0543736dd68eac6 /src/qml/jsruntime/qv4qobjectwrapper.cpp
parent0dd884aca1fffcd94fbe55006c94363415aa0965 (diff)
Enable lookups in QML
The main feature that needs to be implemented in order to enable lookups in QML files is to respect that the QObject wrapper has its own storage layer (meta-object properties). Lookups need to be able to index those when the base is a QObject. This is done by caching the property data and guarding the validity by comparing property cache pointers. The same lookup logic is also implemented for value type wrappers. OVerall there's more that can be done with lookups in meta-objects, for constant properties for example. For "global" lookups we have a safeguard in place that generates a LoadName instruction for property access that should end up in the qml context wrapper. So no changes are needed here at first, but the lookup in the QML context can be optimized in the future. The way of storing the property cache in the lookup itself trades ugliness on destruction against the creation of less internal classes. Another option would be to store the property cache in the internal class and let QObjectWrapper always transition via the property cache. Task-number: QTBUG-69898 Change-Id: I9c378c071acc6d7d4a34a2a76616f9594119d515 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/jsruntime/qv4qobjectwrapper.cpp')
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp173
1 files changed, 136 insertions, 37 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 40be6f41c8..711c910906 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>
@@ -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);
}
@@ -361,13 +385,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;
@@ -829,6 +848,86 @@ 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, /*captureRequired*/true);
+ }
+ 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.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);
+ };
+
+ // 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 = 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 || ddata->propertyCache != lookup->qobjectLookup.propertyCache)
+ return revertLookup();
+
+ QQmlPropertyData *property = lookup->qobjectLookup.propertyData;
+ return getProperty(engine, qobj, property, /*captureRequired = */true);
+}
+
+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
@@ -1920,13 +2019,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();
}