aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
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/qml
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/qml')
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp151
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h3
2 files changed, 117 insertions, 37 deletions
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index b503d75a47..b22d1530e2 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -51,6 +51,8 @@
#include <private/qv4stackframe_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4lookup_p.h>
#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
@@ -372,6 +374,117 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
return Encode(b->engine()->newString(result));
}
+Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
+ Heap::QQmlValueTypeWrapper *valueTypeWrapper,
+ QQmlPropertyData *property)
+{
+ if (property->isFunction()) {
+ // calling a Q_INVOKABLE function of a value type
+ return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, property->coreIndex());
+ }
+
+#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
+ if (property->propType() == metatype) { \
+ cpptype v; \
+ void *args[] = { &v, nullptr }; \
+ metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), \
+ QMetaObject::ReadProperty, index, args); \
+ return QV4::Encode(constructor(v)); \
+ }
+
+ const QMetaObject *metaObject = valueTypeWrapper->propertyCache()->metaObject();
+
+ int index = property->coreIndex();
+ QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
+
+ // These four types are the most common used by the value type wrappers
+ VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal);
+ VALUE_TYPE_LOAD(QMetaType::Int || property->isEnum(), int, int);
+ VALUE_TYPE_LOAD(QMetaType::Int, int, int);
+ VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString);
+ VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
+
+ QVariant v;
+ void *args[] = { nullptr, nullptr };
+ if (property->propType() == QMetaType::QVariant) {
+ args[0] = &v;
+ } else {
+ v = QVariant(property->propType(), static_cast<void *>(nullptr));
+ args[0] = v.data();
+ }
+ metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), QMetaObject::ReadProperty,
+ index, args);
+ return engine->fromVariant(v);
+#undef VALUE_TYPE_LOAD
+}
+
+ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine,
+ Lookup *lookup)
+{
+ PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[lookup->nameIndex]);
+ if (!id.isString())
+ return Object::virtualResolveLookupGetter(object, engine, lookup);
+
+ const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object);
+ QV4::ExecutionEngine *v4 = r->engine();
+ Scope scope(v4);
+ ScopedString name(scope, id.asStringOrSymbol());
+
+ // Note: readReferenceValue() can change the reference->type.
+ if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) {
+ if (!reference->readReferenceValue())
+ return Value::undefinedValue().asReturnedValue();
+ }
+
+ QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
+ if (!result)
+ return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
+
+ lookup->qgadgetLookup.ic = r->internalClass();
+ lookup->qgadgetLookup.propertyCache = r->d()->propertyCache();
+ lookup->qgadgetLookup.propertyCache->addref();
+ lookup->qgadgetLookup.propertyData = result;
+ lookup->getter = QQmlValueTypeWrapper::lookupGetter;
+ return lookup->getter(lookup, engine, *object);
+}
+
+ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
+{
+ const auto revertLookup = [lookup, engine, &object]() {
+ lookup->qgadgetLookup.propertyCache->release();
+ lookup->qgadgetLookup.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->qgadgetLookup.ic)
+ return revertLookup();
+
+ Heap::QQmlValueTypeWrapper *valueTypeWrapper =
+ const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o));
+ if (valueTypeWrapper->propertyCache() != lookup->qgadgetLookup.propertyCache)
+ return revertLookup();
+
+ if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) {
+ Scope scope(engine);
+ Scoped<QQmlValueTypeReference> referenceWrapper(scope, valueTypeWrapper);
+ referenceWrapper->readReferenceValue();
+ }
+
+ QQmlPropertyData *property = lookup->qgadgetLookup.propertyData;
+ return getGadgetProperty(engine, valueTypeWrapper, property);
+}
+
+bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
+ const Value &value)
+{
+ return Object::virtualResolveLookupSetter(object, engine, lookup, value);
+}
+
ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlValueTypeWrapper>());
@@ -397,43 +510,7 @@ ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id,
if (hasProperty)
*hasProperty = true;
- if (result->isFunction())
- // calling a Q_INVOKABLE function of a value type
- return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex());
-
-#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
- if (result->propType() == metatype) { \
- cpptype v; \
- void *args[] = { &v, 0 }; \
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args); \
- return QV4::Encode(constructor(v)); \
- }
-
- const QMetaObject *metaObject = r->d()->propertyCache()->metaObject();
-
- int index = result->coreIndex();
- QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
-
- void *gadget = r->d()->gadgetPtr;
-
- // These four types are the most common used by the value type wrappers
- VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal);
- VALUE_TYPE_LOAD(QMetaType::Int || result->isEnum(), int, int);
- VALUE_TYPE_LOAD(QMetaType::Int, int, int);
- VALUE_TYPE_LOAD(QMetaType::QString, QString, v4->newString);
- VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
-
- QVariant v;
- void *args[] = { nullptr, nullptr };
- if (result->propType() == QMetaType::QVariant) {
- args[0] = &v;
- } else {
- v = QVariant(result->propType(), static_cast<void *>(nullptr));
- args[0] = v.data();
- }
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args);
- return v4->fromVariant(v);
-#undef VALUE_TYPE_ACCESSOR
+ return getGadgetProperty(v4, r->d(), result);
}
bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index 8db9474132..baac129afa 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -112,6 +112,9 @@ public:
static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
+ static ReturnedValue lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object);
static void initProto(ExecutionEngine *v4);
};