aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2021-04-23 16:19:35 +0200
committerUlf Hermann <ulf.hermann@qt.io>2021-04-30 17:54:10 +0200
commit2b49615db4381fa7c54b1ebbc9875d214ad292aa (patch)
treeb79a86598e02af8a497e8e05446291d4769b8e5b
parent499fcb5d399321c3d887ead8f5a5b8696841a7f9 (diff)
Redesign the AOT lookups
Each kind of lookup should come with a function that tries to execute the lookup, taking a minimal number of parameters, and another one that initializes the lookup, taking whatever is needed for that. No initialization should be done in the execution step and vice versa. Rather, the execution step should be repeated if an initialization had to be done first. This way, the happy path can be very fast if the lookups have been initialized before. Change-Id: Ic435b3dd4906d00144138cb05161a99a0a9c64ed Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp1
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h4
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp11
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h4
-rw-r--r--src/qml/qml/qqml.cpp654
-rw-r--r--src/qml/qml/qqmlprivate.h82
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp6
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h2
8 files changed, 537 insertions, 227 deletions
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index 01c8f0a7c9..a4e7ce4466 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -319,6 +319,7 @@ void ExecutableCompilationUnit::unlink()
for (uint i = 0; i < data->lookupTableSize; ++i) {
QV4::Lookup &l = runtimeLookups[i];
if (l.getter == QV4::QObjectWrapper::lookupGetter
+ || l.setter == QV4::QObjectWrapper::lookupSetter
|| l.getter == QQmlTypeWrapper::lookupSingletonProperty) {
if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
pc->release();
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index aab6b66597..b1eb1ea982 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -144,8 +144,8 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
QV4::ReturnedValue singletonValue;
} qmlContextSingletonLookup;
struct {
- quintptr unused1;
- quintptr unused2;
+ Heap::Base *objectContextWrapper;
+ Heap::Base *ownContextWrapper;
int objectId;
} qmlContextIdObjectLookup;
struct {
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 62a3c9f025..5940729b78 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -964,6 +964,17 @@ ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engi
return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
}
+ReturnedValue QObjectWrapper::lookupAttached(
+ Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ return QV4::Lookup::getterGeneric(l, engine, object);
+}
+
+bool QObjectWrapper::lookupSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v)
+{
+ return QV4::Lookup::setterFallback(l, engine, object, v);
+}
+
bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
const Value &value)
{
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 50e3f41e67..37cb4d3cac 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -190,6 +190,10 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue lookupAttached(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static bool lookupSetter(QV4::Lookup *l, QV4::ExecutionEngine *engine,
+ QV4::Value &object, const QV4::Value &value);
+
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);
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 9da48fc0e0..afe6bedf56 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -53,6 +53,8 @@
#include <private/qqmlvaluetypewrapper_p.h>
#include <private/qv4lookup_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4errorobject_p.h>
#include <QtCore/qmutex.h>
@@ -652,67 +654,167 @@ void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
}
namespace QQmlPrivate {
- template<>
- void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
- const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject,
- QVector<int> *qmlTypeIds, const QMetaObject *extension)
- {
- using T = QQmlTypeNotAvailable;
-
- RegisterTypeAndRevisions type = {
- 0,
- QMetaType::fromType<T *>(),
- QMetaType::fromType<QQmlListProperty<T>>(),
- 0,
- nullptr,
- nullptr,
- nullptr,
+template<>
+void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
+ const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject,
+ QVector<int> *qmlTypeIds, const QMetaObject *extension)
+{
+ using T = QQmlTypeNotAvailable;
- uri,
- QTypeRevision::fromMajorVersion(versionMajor),
+ RegisterTypeAndRevisions type = {
+ 0,
+ QMetaType::fromType<T *>(),
+ QMetaType::fromType<QQmlListProperty<T>>(),
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
- &QQmlTypeNotAvailable::staticMetaObject,
- classInfoMetaObject,
+ uri,
+ QTypeRevision::fromMajorVersion(versionMajor),
- attachedPropertiesFunc<T>(),
- attachedPropertiesMetaObject<T>(),
+ &QQmlTypeNotAvailable::staticMetaObject,
+ classInfoMetaObject,
- StaticCastSelector<T, QQmlParserStatus>::cast(),
- StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
- StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
+ attachedPropertiesFunc<T>(),
+ attachedPropertiesMetaObject<T>(),
- nullptr, extension, qmlCreateCustomParser<T>, qmlTypeIds
- };
+ StaticCastSelector<T, QQmlParserStatus>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
- qmlregister(TypeAndRevisionsRegistration, &type);
- }
+ nullptr, extension, qmlCreateCustomParser<T>, qmlTypeIds
+ };
+
+ qmlregister(TypeAndRevisionsRegistration, &type);
}
-QQmlEngine *QQmlPrivate::AOTCompiledContext::qmlEngine() const
+
+QQmlEngine *AOTCompiledContext::qmlEngine() const
{
return qmlContext ? qmlContext->engine() : nullptr;
}
-QJSValue QQmlPrivate::AOTCompiledContext::jsMetaType(int index) const
+QJSValue AOTCompiledContext::jsMetaType(int index) const
{
return QJSValuePrivate::fromReturnedValue(
compilationUnit->runtimeClasses[index]->asReturnedValue());
}
-void QQmlPrivate::AOTCompiledContext::setInstructionPointer(int offset) const
+void AOTCompiledContext::setInstructionPointer(int offset) const
{
if (auto *frame = engine->handle()->currentStackFrame)
frame->instructionPointer = offset;
}
-QJSValue QQmlPrivate::AOTCompiledContext::loadQmlContextPropertyLookup(uint index) const
+
+static void captureObjectProperty(
+ QObject *object, const QQmlPropertyCache *propertyCache,
+ const QQmlPropertyData *property, QQmlContextData *qmlContext)
+{
+ if (!qmlContext || property->isConstant())
+ return;
+
+ QQmlEngine *engine = qmlContext->engine();
+ Q_ASSERT(engine);
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
+ Q_ASSERT(ep);
+ if (QQmlPropertyCapture *capture = ep->propertyCapture)
+ capture->captureProperty(object, propertyCache, property);
+}
+
+static void loadObjectProperty(QV4::Lookup *l, QObject *object, void *target,
+ QQmlContextData *qmlContext)
+{
+ const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache;
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+ Q_ASSERT(QQmlData::get(object)->propertyCache == propertyCache);
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ captureObjectProperty(object, propertyCache, property, qmlContext);
+ property->readProperty(object, target);
+}
+
+static void storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
+{
+ const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache;
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+ Q_ASSERT(QQmlData::get(object)->propertyCache == propertyCache);
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
+ property->writeProperty(object, value, {});
+}
+
+static void initObjectLookup(
+ const AOTCompiledContext *aotContext, QV4::Lookup *l, QObject *object)
+{
+ QV4::Scope scope(aotContext->engine->handle());
+ QV4::PropertyKey id = scope.engine->identifierTable->asPropertyKey(
+ aotContext->compilationUnit->runtimeStrings[l->nameIndex]);
+
+ Q_ASSERT(id.isString());
+
+ QV4::ScopedString name(scope, id.asStringOrSymbol());
+
+ Q_ASSERT(!name->equals(scope.engine->id_toString()));
+ Q_ASSERT(!name->equals(scope.engine->id_destroy()));
+
+ QQmlData *ddata = QQmlData::get(object, true);
+ Q_ASSERT(ddata);
+
+ QQmlPropertyData *property;
+ if (!ddata->propertyCache) {
+ property = QQmlPropertyCache::property(
+ aotContext->engine, object, name, aotContext->qmlContext, nullptr);
+ } else {
+ property = ddata->propertyCache->property(
+ name.getPointer(), object, aotContext->qmlContext);
+ }
+
+ Q_ASSERT(property);
+ Q_ASSERT(ddata->propertyCache);
+
+ l->qobjectLookup.propertyCache = ddata->propertyCache;
+ l->qobjectLookup.propertyCache->addref();
+ l->qobjectLookup.propertyData = property;
+}
+
+static void initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
+ const QMetaObject *metaObject)
+{
+ Q_ASSERT(metaObject);
+ l->qgadgetLookup.metaObject = quintptr(metaObject) + 1;
+ const QByteArray name = compilationUnit->runtimeStrings[l->nameIndex]->toQString().toUtf8();
+ l->qgadgetLookup.coreIndex = metaObject->indexOfProperty(name.constData());
+ l->qgadgetLookup.metaType = metaObject->property(
+ l->qgadgetLookup.coreIndex).metaType().iface();
+}
+
+static void amendException(QV4::ExecutionEngine *engine)
+{
+ const int lineNumber = engine->currentStackFrame->lineNumber();
+ engine->exceptionStackTrace.front().line = lineNumber;
+
+ QV4::Scope scope(engine);
+ QV4::Scoped<QV4::ErrorObject> error(scope, *engine->exceptionValue);
+ if (error) // else some other value was thrown
+ error->d()->stackTrace->front().line = lineNumber;
+}
+
+
+bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- return QJSValuePrivate::fromReturnedValue(l->qmlContextPropertyGetter(
- l, engine->handle(), nullptr));
+ if (l->getter != QV4::QQmlTypeWrapper::lookupSingletonProperty
+ && l->getter != QV4::QObjectWrapper::lookupGetter) {
+ return false;
+ }
+
+ captureObjectProperty(
+ object, l->qobjectLookup.propertyCache, l->qobjectLookup.propertyData, qmlContext);
+ return true;
}
-bool QQmlPrivate::AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
+bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty
@@ -720,133 +822,308 @@ bool QQmlPrivate::AOTCompiledContext::captureQmlContextPropertyLookup(uint index
return false;
}
- const auto *property = l->qobjectLookup.propertyData;
- Q_ASSERT(property);
- if (property->isConstant())
- return true;
+ captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache,
+ l->qobjectLookup.propertyData, qmlContext);
+ return true;
+}
- if (QQmlEngine *engine = qmlContext->engine()) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
- if (QQmlPropertyCapture *capture = ep->propertyCapture)
- capture->captureProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property);
+QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
+ || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty
+ || l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
+ || l->getter == QV4::QObjectWrapper::lookupGetter) {
+ return l->qobjectLookup.propertyData->propType();
+ } else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) {
+ return QMetaType(l->qgadgetLookup.metaType);
+ } else if (l->getter == QV4::QQmlTypeWrapper::lookupEnumValue) {
+ return QMetaType::fromType<int>();
+ } else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject
+ || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType
+ || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton
+ || l->getter == QV4::QObjectWrapper::lookupAttached) {
+ return QMetaType::fromType<QObject *>();
}
+ return QMetaType();
+}
- return true;
+bool AOTCompiledContext::callQmlContextPropertyLookup(
+ uint index, void **args, const QMetaType *types, int argc) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedValue thisObject(scope);
+ QV4::ScopedFunctionObject function(
+ scope, l->qmlContextPropertyGetter(l, scope.engine, thisObject));
+ if (!function) {
+ scope.engine->throwTypeError(
+ QStringLiteral("Property '%1' of object [null] is not a function").arg(
+ compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
+ return false;
+ }
+
+ function->call(thisObject, args, types, argc);
+ return !scope.engine->hasException;
+}
+
+void AOTCompiledContext::initCallQmlContextPropertyLookup(uint index) const
+{
+ Q_UNUSED(index);
+ Q_ASSERT(engine->hasError());
+ amendException(engine->handle());
}
-QObject *QQmlPrivate::AOTCompiledContext::loadQmlContextPropertyIdLookup(uint index) const
+bool AOTCompiledContext::loadContextIdLookup(uint index, void *target) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject) {
- // Shortcut this to avoid the wrapping when returning from lookupIdObject().
- if (!qmlContext)
- return nullptr;
+ if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupIdObject)
+ return false;
- QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(qmlContext->engine());
- const int objectId = l->qmlContextIdObjectLookup.objectId;
+ Q_ASSERT(qmlContext);
+
+ QQmlContextData *contextData;
+ if (auto *wrapper = l->qmlContextIdObjectLookup.ownContextWrapper) {
+ QV4::Scope scope(engine->handle());
+ QV4::Scoped<QV4::QQmlContextWrapper> ownContext(scope, wrapper);
+ if (ownContext->d()->context != qmlContext)
+ return false; // context has changed. Look up again.
+ QV4::Scoped<QV4::QQmlContextWrapper> objectContext(
+ scope, l->qmlContextIdObjectLookup.objectContextWrapper);
+ contextData = objectContext->d()->context;
+ } else {
+ contextData = qmlContext;
+ }
+
+ const int objectId = l->qmlContextIdObjectLookup.objectId;
+ QQmlEnginePrivate *engine = QQmlEnginePrivate::get(qmlEngine());
- if (QQmlPropertyCapture *capture = qmlEngine->propertyCapture)
- capture->captureProperty(qmlContext->idValueBindings(objectId));
- return qmlContext->idValue(objectId);
+ if (QQmlPropertyCapture *capture = engine->propertyCapture)
+ capture->captureProperty(contextData->idValueBindings(objectId));
+ *static_cast<QObject **>(target) = contextData->idValue(objectId);
+ return true;
+}
+
+void AOTCompiledContext::initLoadContextIdLookup(uint index) const
+{
+ Q_ASSERT(!engine->hasError());
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
+ const QQmlRefPointer<QQmlContextData> ownContext = qmlContext;
+ for (auto context = ownContext; context; context = context->parent()) {
+ const int propertyIdx = context->propertyIndex(name);
+ if (propertyIdx == -1 || propertyIdx >= context->numIdValues())
+ continue;
+
+ l->qmlContextIdObjectLookup.objectId = propertyIdx;
+ l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupIdObject;
+ if (context.data() != ownContext.data()) {
+ l->qmlContextIdObjectLookup.objectContextWrapper
+ = scope.engine->memoryManager->allocate<QV4::QQmlContextWrapper>(
+ context, nullptr);
+ l->qmlContextIdObjectLookup.ownContextWrapper
+ = scope.engine->memoryManager->allocate<QV4::QQmlContextWrapper>(
+ ownContext, nullptr);
+ }
+ return;
}
+ Q_UNREACHABLE();
+}
+
+bool AOTCompiledContext::callObjectPropertyLookup(
+ uint index, QObject *object, void **args, const QMetaType *types, int argc) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
QV4::Scope scope(engine->handle());
- QV4::Scoped<QV4::QObjectWrapper> o(
- scope, l->qmlContextPropertyGetter(l, scope.engine, nullptr));
- return o ? o->object() : nullptr;
+ QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(scope.engine, object));
+ QV4::ScopedFunctionObject function(scope, l->getter(l, engine->handle(), thisObject));
+ if (!function) {
+ scope.engine->throwTypeError(
+ QStringLiteral("Property '%1' of object [object Object] is not a function")
+ .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
+ return false;
+ }
+
+ function->call(thisObject, args, types, argc);
+ return !scope.engine->hasException;
}
-static void captureObjectProperty(
- QObject *object, const QQmlPropertyCache *propertyCache,
- const QQmlPropertyData *property, QQmlContextData *qmlContext)
+void AOTCompiledContext::initCallObjectPropertyLookup(uint index) const
{
- if (!qmlContext || property->isConstant())
- return;
+ Q_UNUSED(index);
+ Q_ASSERT(engine->hasError());
+ amendException(engine->handle());
+}
- QQmlEngine *engine = qmlContext->engine();
- Q_ASSERT(engine);
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
- Q_ASSERT(ep);
- if (QQmlPropertyCapture *capture = ep->propertyCapture)
- capture->captureProperty(object, propertyCache, property);
+bool AOTCompiledContext::callGlobalLookup(
+ uint index, void **args, const QMetaType *types, int argc) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedFunctionObject function(scope, l->globalGetter(l, scope.engine));
+ if (!function) {
+ scope.engine->throwTypeError(
+ QStringLiteral("Property '%1' of object [null] is not a function")
+ .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
+ return false;
+ }
+
+ QV4::ScopedValue thisObject(scope, QV4::Encode::undefined());
+ function->call(thisObject, args, types, argc);
+ return true;
}
-static void *transformVariant(void *target, QMetaType propertyType)
+void AOTCompiledContext::initCallGlobalLookup(uint index) const
{
- QVariant *v = static_cast<QVariant *>(target);
- if (v->metaType() != propertyType)
- *v = QVariant(propertyType);
- return v->data();
+ Q_UNUSED(index);
+ Q_ASSERT(engine->hasError());
+ amendException(engine->handle());
}
-static void loadObjectProperty(QV4::Lookup *l, QObject *object, void *target, QMetaType type,
- QQmlContextData *qmlContext)
+bool AOTCompiledContext::loadGlobalLookup(uint index, void *target, QMetaType type) const
{
- const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache;
- Q_ASSERT(!QQmlData::wasDeleted(object));
- Q_ASSERT(QQmlData::get(object)->propertyCache == propertyCache);
- const QQmlPropertyData *property = l->qobjectLookup.propertyData;
- const QMetaType propertyType = property->propType();
- if (type != propertyType && type == QMetaType::fromType<QVariant>()) {
- // We can read into the contents of a QVariant without conversion.
- captureObjectProperty(object, propertyCache, property, qmlContext);
- property->readProperty(object, transformVariant(target, propertyType));
- } else {
- Q_ASSERT(propertyType == type
- || (type.flags() & QMetaType::PointerToQObject
- && propertyType.flags() & QMetaType::PointerToQObject));
- captureObjectProperty(object, propertyCache, property, qmlContext);
- property->readProperty(object, target);
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::ExecutionEngine *v4 = engine->handle();
+ if (!v4->metaTypeFromJS(l->globalGetter(l, engine->handle()), type, target)) {
+ v4->throwTypeError();
+ return false;
}
+ return true;
+}
+
+void AOTCompiledContext::initLoadGlobalLookup(uint index) const
+{
+ Q_UNUSED(index);
+ Q_ASSERT(engine->hasError());
+ amendException(engine->handle());
}
-bool QQmlPrivate::AOTCompiledContext::loadQmlContextPropertyLookup(
- uint index, void *target, QMetaType type) const
+bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty) {
- loadObjectProperty(l, qmlScopeObject, target, type, qmlContext);
+ if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+ return false;
+ loadObjectProperty(l, qmlScopeObject, target, qmlContext);
+ return true;
+}
+
+void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index) const
+{
+ Q_ASSERT(!engine->hasError());
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ initObjectLookup(this, l, qmlScopeObject);
+ l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
+}
+
+bool AOTCompiledContext::loadTypeLookup(uint index, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType) {
+ // We resolve it right away. An AOT compiler, in contrast to a naive interpreter
+ // should only request objects it actually needs.
+ QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(scope, l->qmlTypeLookup.qmlTypeWrapper);
+ Q_ASSERT(wrapper);
+ *static_cast<QObject **>(target) = wrapper->object();
return true;
}
- QV4::Scope scope(engine->handle());
- QV4::ScopedValue result(scope, l->qmlContextPropertyGetter(l, scope.engine, nullptr));
- if (type == QMetaType::fromType<QVariant>()) {
- // Special case QVariant in order to retain JS objects.
- // We don't want to convert JS arrays into QVariantList, for example.
- *static_cast<QVariant *>(target) = scope.engine->toVariant(result, QMetaType {});
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton) {
+ QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
+ scope, l->qmlContextSingletonLookup.singletonObject);
+
+ // We don't handle non-QObject singletons (as those can't be declared in qmltypes anyway)
+ Q_ASSERT(wrapper);
+ *static_cast<QObject **>(target) = wrapper->object();
return true;
}
- return scope.engine->metaTypeFromJS(result, type, target);
+
+ return false;
}
-bool QQmlPrivate::AOTCompiledContext::getObjectLookup(
- uint index, QObject *object, void *target, QMetaType type) const
+void AOTCompiledContext::initLoadTypeLookup(uint index) const
{
+ Q_ASSERT(!engine->hasError());
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ l->qmlContextPropertyGetter(l, engine->handle(), nullptr);
- if (!object)
+ // Singleton instances can be retrieved via either lookupType or lookupSingleton
+ // and both use QQmlTypeWrapper to store them.
+ // TODO: Wat? Looking up the singleton instances on each access is horribly inefficient.
+ // There is plenty of space in the lookup to store the instances.
+
+ Q_ASSERT(l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType
+ || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton);
+}
+
+bool AOTCompiledContext::loadAttachedLookup(uint index, QObject *object, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->getter != QV4::QObjectWrapper::lookupAttached)
return false;
- if (l->getter == QV4::QObjectWrapper::lookupGetter) {
- loadObjectProperty(l, object, target, type, qmlContext);
- return true;
- }
+ QV4::Scope scope(engine->handle());
+ QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(scope, l->qmlTypeLookup.qmlTypeWrapper);
+ Q_ASSERT(wrapper);
+ *static_cast<QObject **>(target) = qmlAttachedPropertiesObject(
+ object, wrapper->d()->type().attachedPropertiesFunction(
+ QQmlEnginePrivate::get(qmlEngine())));
+ return true;
+}
+void AOTCompiledContext::initLoadAttachedLookup(uint index, QObject *object) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
QV4::Scope scope(engine->handle());
- QV4::ScopedValue o(scope, QV4::QObjectWrapper::wrap(scope.engine, object));
- QV4::ScopedValue result(scope, l->getter(l, scope.engine, o));
- if (type == QMetaType::fromType<QVariant>()) {
- // Special case QVariant in order to retain JS objects.
- // We don't want to convert JS arrays into QVariantList, for example.
- *static_cast<QVariant *>(target) = scope.engine->toVariant(result, QMetaType {});
- return true;
+ QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
+ QQmlTypeNameCache::Result r = qmlContext->imports()->query(name);
+
+ if (!r.isValid() || !r.type.isValid()) {
+ scope.engine->throwTypeError();
+ return;
}
- return scope.engine->metaTypeFromJS(result, type, target);
+
+ QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
+ scope, QV4::QQmlTypeWrapper::create(scope.engine, object, r.type,
+ QV4::Heap::QQmlTypeWrapper::ExcludeEnums));
+
+ l->qmlTypeLookup.qmlTypeWrapper = wrapper->d();
+ l->getter = QV4::QObjectWrapper::lookupAttached;
}
-bool QQmlPrivate::AOTCompiledContext::getValueLookup(
- uint index, void *value, QMetaType valueType, void *target, QMetaType type) const
+bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (!object) {
+ engine->handle()->throwTypeError(
+ QStringLiteral("Cannot read property '%1' of null")
+ .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
+ return false;
+ }
+
+ if (l->getter != QV4::QObjectWrapper::lookupGetter)
+ return false;
+
+ loadObjectProperty(l, object, target, qmlContext);
+ return true;
+}
+
+void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object) const
+{
+ QV4::ExecutionEngine *v4 = engine->handle();
+ if (v4->hasException) {
+ amendException(v4);
+ } else {
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ initObjectLookup(this, l, object);
+ l->getter = QV4::QObjectWrapper::lookupGetter;
+ }
+}
+
+bool AOTCompiledContext::getValueLookup(uint index, void *value, void *target) const
{
Q_ASSERT(value);
@@ -858,129 +1135,96 @@ bool QQmlPrivate::AOTCompiledContext::getValueLookup(
= reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
Q_ASSERT(metaObject);
- // We cannot assert on anything here. You may even override the metaObject of Q_GADGET
- // types with the foreign/extended mechanism. See for example QQuickFontValueType.
- Q_UNUSED(valueType);
-
void *args[] = { target, nullptr };
- if (type == QMetaType::fromType<QVariant>())
- args[0] = transformVariant(target, QMetaType(l->qgadgetLookup.metaType));
- else if (type != QMetaType(l->qgadgetLookup.metaType))
- return false;
-
metaObject->d.static_metacall(
reinterpret_cast<QObject*>(value), QMetaObject::ReadProperty,
l->qgadgetLookup.coreIndex, args);
return true;
}
-QJSValue QQmlPrivate::AOTCompiledContext::callQmlContextPropertyLookup(
- uint index, const QJSValueList &args) const
-{
- QV4::Scope scope(engine->handle());
- const int argc = args.length();
- QV4::Value *argv = scope.alloc(args.length());
- for (int i = 0; i < argc; ++i)
- argv[i] = QJSValuePrivate::convertToReturnedValue(scope.engine, args.at(i));
- return QJSValuePrivate::fromReturnedValue(QV4::Runtime::CallQmlContextPropertyLookup::call(
- engine->handle(), index, argv, argc));
-}
-
-QJSValue QQmlPrivate::AOTCompiledContext::getLookup(uint index, const QJSValue &object) const
+void AOTCompiledContext::initGetValueLookup(uint index, const QMetaObject *metaObject) const
{
+ Q_ASSERT(!engine->hasError());
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
-
- if (object.isNull() || object.isUndefined()) {
- QString message = QStringLiteral("Cannot read property '%1' of %2")
- .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString(), object.toString());
- return QJSValuePrivate::fromReturnedValue(engine->handle()->throwTypeError(message));
- }
-
- return QJSValuePrivate::fromReturnedValue(
- l->getter(l, engine->handle(),
- QJSValuePrivate::convertToReturnedValue(engine->handle(), object)));
+ initValueLookup(l, compilationUnit, metaObject);
+ l->getter = QV4::QQmlValueTypeWrapper::lookupGetter;
}
-bool QQmlPrivate::AOTCompiledContext::captureLookup(uint index, QObject *object) const
+bool AOTCompiledContext::getEnumLookup(uint index, int *target) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- if (l->getter != QV4::QQmlTypeWrapper::lookupSingletonProperty
- && l->getter != QV4::QObjectWrapper::lookupGetter) {
+ if (l->getter != QV4::QQmlTypeWrapper::lookupEnumValue)
return false;
- }
-
- captureObjectProperty(
- object, l->qobjectLookup.propertyCache, l->qobjectLookup.propertyData, qmlContext);
+ *target = l->qmlEnumValueLookup.encodedEnumValue;
return true;
}
-void QQmlPrivate::AOTCompiledContext::setLookup(
- uint index, const QJSValue &object, const QJSValue &value) const
+void AOTCompiledContext::initGetEnumLookup(
+ uint index, const QMetaObject *metaObject,
+ const char *enumerator, const char *enumValue) const
{
+ Q_ASSERT(!engine->hasError());
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- QV4::Scope scope(engine->handle());
- QV4::ScopedValue o(scope, QJSValuePrivate::convertToReturnedValue(scope.engine, object));
- if (!l->setter(l, engine->handle(), *o,
- QJSValuePrivate::convertToReturnedValue(engine->handle(), value))) {
- engine->handle()->throwTypeError();
- }
+ Q_ASSERT(metaObject);
+ const int enumIndex = metaObject->indexOfEnumerator(enumerator);
+ const int value = metaObject->enumerator(enumIndex).keyToValue(enumValue);
+ l->qmlEnumValueLookup.encodedEnumValue = value;
+ l->getter = QV4::QQmlTypeWrapper::lookupEnumValue;
}
-QJSValue QQmlPrivate::AOTCompiledContext::callPropertyLookup(
- uint index, const QJSValue &object, const QJSValueList &args) const
+bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *value) const
{
- QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- QV4::Scope scope(engine->handle());
- QV4::ScopedValue o(scope, QJSValuePrivate::convertToReturnedValue(scope.engine, object));
-
- // ok to have the value on the stack here
- QV4::Value f = QV4::Value::fromReturnedValue(l->getter(l, engine->handle(), o));
-
- if (Q_UNLIKELY(!f.isFunctionObject())) {
- QString message = QStringLiteral("Property '%1' of object %2 is not a function")
- .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString(),
- object.toString());
- engine->handle()->throwTypeError(message);
- return QJSValue();
+ if (!object) {
+ engine->handle()->throwTypeError(
+ QStringLiteral("Value is null and could not be converted to an object"));
+ return false;
}
- const int argc = args.length();
- QV4::Value *argv = scope.alloc(args.length());
- for (int i = 0; i < argc; ++i)
- argv[i] = QJSValuePrivate::convertToReturnedValue(scope.engine, args.at(i));
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->setter != QV4::QObjectWrapper::lookupSetter)
+ return false;
- return QJSValuePrivate::fromReturnedValue(
- static_cast<QV4::FunctionObject &>(f).call(o, argv, argc));
+ storeObjectProperty(l, object, value);
+ return true;
}
-QJSValue QQmlPrivate::AOTCompiledContext::callGlobalLookup(
- uint index, const QJSValueList &args) const
+void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object) const
{
- QV4::Scope scope(engine->handle());
- QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- QV4::Value function = QV4::Value::fromReturnedValue(l->globalGetter(l, scope.engine));
- if (!function.isFunctionObject()) {
- const QString msg = QStringLiteral("Property '%1' of object [null] is not a function")
- .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString());
- return QJSValuePrivate::fromReturnedValue(scope.engine->throwTypeError(msg));
+ QV4::ExecutionEngine *v4 = engine->handle();
+ if (v4->hasException) {
+ amendException(v4);
+ } else {
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ initObjectLookup(this, l, object);
+ l->setter = QV4::QObjectWrapper::lookupSetter;
}
+}
- const int argc = args.length();
- QV4::Value *argv = scope.alloc(args.length());
- for (int i = 0; i < argc; ++i)
- argv[i] = QJSValuePrivate::convertToReturnedValue(scope.engine, args.at(i));
+bool AOTCompiledContext::setValueLookup(
+ uint index, void *target, void *value) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->setter != QV4::QQmlValueTypeWrapper::lookupSetter)
+ return false;
- QV4::Value thisObject = QV4::Value::undefinedValue();
- QV4::ReturnedValue result = static_cast<QV4::FunctionObject &>(function).call(
- &thisObject, argv, argc);
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
- return scope.engine->hasException ? QJSValue() : QJSValuePrivate::fromReturnedValue(result);
+ void *args[] = { value, nullptr };
+ metaObject->d.static_metacall(
+ reinterpret_cast<QObject*>(target), QMetaObject::WriteProperty,
+ l->qgadgetLookup.coreIndex, args);
+ return true;
}
-QJSValue QQmlPrivate::AOTCompiledContext::loadGlobalLookup(uint index) const
+void AOTCompiledContext::initSetValueLookup(uint index, const QMetaObject *metaObject) const
{
+ Q_ASSERT(!engine->hasError());
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
- return QJSValuePrivate::fromReturnedValue(l->globalGetter(l, engine->handle()));
+ initValueLookup(l, compilationUnit, metaObject);
+ l->setter = QV4::QQmlValueTypeWrapper::lookupSetter;
}
+} // namespace QQmlPrivate
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index a98427d594..de0b5f9217 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -613,29 +613,71 @@ namespace QQmlPrivate
QJSValue jsMetaType(int index) const;
void setInstructionPointer(int offset) const;
- QJSValue loadQmlContextPropertyLookup(uint index) const;
- QJSValue callQmlContextPropertyLookup(uint index, const QJSValueList &args) const;
- QJSValue getLookup(uint index, const QJSValue &object) const;
- void setLookup(uint index, const QJSValue &object, const QJSValue &value) const;
- QJSValue callPropertyLookup(uint index, const QJSValue &object,
- const QJSValueList &args) const;
- QJSValue callGlobalLookup(uint index, const QJSValueList &args) const;
- QJSValue loadGlobalLookup(uint index) const;
-
// Run QQmlPropertyCapture::captureProperty() without retrieving the value.
bool captureLookup(uint index, QObject *object) const;
bool captureQmlContextPropertyLookup(uint index) const;
-
- // Look up a context property of which we know it's a QObject
- QObject *loadQmlContextPropertyIdLookup(uint index) const;
- // Look up a context property of given type and store it in the target pointer
- bool loadQmlContextPropertyLookup(uint index, void *target, QMetaType type) const;
-
- // Look up a property of which we know it belongs to a QObject, and write to target.
- bool getObjectLookup(uint index, QObject *object, void *target, QMetaType type) const;
- // Look up a property that belongs to value of type valueType, and write to target.
- bool getValueLookup(uint index, void *value, QMetaType valueType,
- void *target, QMetaType type) const;
+ QMetaType lookupResultMetaType(uint index) const;
+
+ // All of these lookup functions should be used as follows:
+ //
+ // while (!fooBarLookup(...)) {
+ // setInstructionPointer(...);
+ // initFooBarLookup(...);
+ // if (engine->hasException()) {
+ // ...
+ // break;
+ // }
+ // }
+ //
+ // The bool-returning *Lookup functions exclusively run the happy path and return false if
+ // that fails in any way. The failure may either be in the lookup structs not being
+ // initialized or an exception being thrown.
+ // The init*Lookup functions initialize the lookup structs and amend any exceptions
+ // previously thrown with line numbers. They might also throw their own exceptions. If an
+ // exception is present after the initialization there is no way to carry out the lookup and
+ // the exception should be propagated. If not, the original lookup can be tried again.
+
+ bool callQmlContextPropertyLookup(
+ uint index, void **args, const QMetaType *types, int argc) const;
+ void initCallQmlContextPropertyLookup(uint index) const;
+
+ bool loadContextIdLookup(uint index, void *target) const;
+ void initLoadContextIdLookup(uint index) const;
+
+ bool callObjectPropertyLookup(uint index, QObject *object,
+ void **args, const QMetaType *types, int argc) const;
+ void initCallObjectPropertyLookup(uint index) const;
+
+ bool callGlobalLookup(uint index, void **args, const QMetaType *types, int argc) const;
+ void initCallGlobalLookup(uint index) const;
+
+ bool loadGlobalLookup(uint index, void *target, QMetaType type) const;
+ void initLoadGlobalLookup(uint index) const;
+
+ bool loadScopeObjectPropertyLookup(uint index, void *target) const;
+ void initLoadScopeObjectPropertyLookup(uint index) const;
+
+ bool loadTypeLookup(uint index, void *target) const;
+ void initLoadTypeLookup(uint index) const;
+
+ bool loadAttachedLookup(uint index, QObject *object, void *target) const;
+ void initLoadAttachedLookup(uint index, QObject *object) const;
+
+ bool getObjectLookup(uint index, QObject *object, void *target) const;
+ void initGetObjectLookup(uint index, QObject *object) const;
+
+ bool getValueLookup(uint index, void *value, void *target) const;
+ void initGetValueLookup(uint index, const QMetaObject *metaObject) const;
+
+ bool getEnumLookup(uint index, int *target) const;
+ void initGetEnumLookup(uint index, const QMetaObject *metaObject,
+ const char *enumerator, const char *enumValue) const;
+
+ bool setObjectLookup(uint index, QObject *object, void *value) const;
+ void initSetObjectLookup(uint index, QObject *object) const;
+
+ bool setValueLookup(uint index, void *target, void *value) const;
+ void initSetValueLookup(uint index, const QMetaObject *metaObject) const;
};
struct AOTCompiledFunction {
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 9b2dda23e9..b3c073a3f0 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -563,6 +563,12 @@ ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine
return getGadgetProperty(engine, valueTypeWrapper, QMetaType(lookup->qgadgetLookup.metaType), lookup->qgadgetLookup.coreIndex, lookup->qgadgetLookup.isFunction, lookup->qgadgetLookup.isEnum);
}
+bool QQmlValueTypeWrapper::lookupSetter(
+ Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+{
+ return QV4::Lookup::setterFallback(l, engine, object, value);
+}
+
bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
const Value &value)
{
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index 5d43f8b7eb..0eb1088a64 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -143,6 +143,8 @@ public:
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 bool lookupSetter(QV4::Lookup *l, QV4::ExecutionEngine *engine,
+ QV4::Value &object, const QV4::Value &value);
static void initProto(ExecutionEngine *v4);
};