diff options
Diffstat (limited to 'src/qml/qml/qqmltypewrapper.cpp')
-rw-r--r-- | src/qml/qml/qqmltypewrapper.cpp | 544 |
1 files changed, 378 insertions, 166 deletions
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index ef4a628a04..92603b6cad 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -1,83 +1,124 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmltypewrapper_p.h" -#include <private/qqmlengine_p.h> +#include <private/qjsvalue_p.h> + #include <private/qqmlcontext_p.h> +#include <private/qqmlengine_p.h> #include <private/qqmlmetaobject_p.h> #include <private/qqmltypedata_p.h> +#include <private/qqmlvaluetypewrapper_p.h> -#include <private/qjsvalue_p.h> -#include <private/qv4functionobject_p.h> -#include <private/qv4objectproto_p.h> -#include <private/qv4qobjectwrapper_p.h> #include <private/qv4identifiertable_p.h> #include <private/qv4lookup_p.h> +#include <private/qv4objectproto_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qv4symbol_p.h> QT_BEGIN_NAMESPACE using namespace QV4; DEFINE_OBJECT_VTABLE(QQmlTypeWrapper); +DEFINE_OBJECT_VTABLE(QQmlTypeConstructor); DEFINE_OBJECT_VTABLE(QQmlScopedEnumWrapper); -void Heap::QQmlTypeWrapper::init() + +void Heap::QQmlTypeWrapper::init(TypeNameMode m, QObject *o, const QQmlTypePrivate *type) { - Object::init(); - mode = IncludeEnums; - object.init(); + Q_ASSERT(type); + FunctionObject::init(); + flags = quint8(m) | quint8(Type); + object.init(o); + QQmlType::refHandle(type); + t.typePrivate = type; +} + +void Heap::QQmlTypeWrapper::init( + TypeNameMode m, QObject *o, QQmlTypeNameCache *type, const QQmlImportRef *import) +{ + Q_ASSERT(type); + FunctionObject::init(); + flags = quint8(m) | quint8(Namespace); + object.init(o); + n.typeNamespace = type; + n.typeNamespace->addref(); + n.importNamespace = import; } void Heap::QQmlTypeWrapper::destroy() { - QQmlType::derefHandle(typePrivate); - typePrivate = nullptr; - if (typeNamespace) - typeNamespace->release(); + switch (kind()) { + case Type: + Q_ASSERT(t.typePrivate); + QQmlType::derefHandle(t.typePrivate); + delete[] t.constructors; + break; + case Namespace: + Q_ASSERT(n.typeNamespace); + n.typeNamespace->release(); + break; + } + object.destroy(); - Object::destroy(); + FunctionObject::destroy(); } QQmlType Heap::QQmlTypeWrapper::type() const { - return QQmlType(typePrivate); + switch (kind()) { + case Type: + return QQmlType(t.typePrivate); + case Namespace: + return QQmlType(); + } + + Q_UNREACHABLE_RETURN(QQmlType()); +} + +QQmlTypeNameCache::Result Heap::QQmlTypeWrapper::queryNamespace( + const QV4::String *name, QQmlEnginePrivate *enginePrivate) const +{ + Q_ASSERT(kind() == Namespace); + Q_ASSERT(n.typeNamespace); + Q_ASSERT(n.importNamespace); + return n.typeNamespace->query(name, n.importNamespace, QQmlTypeLoader::get(enginePrivate)); + +} + +template<typename Callback> +void warnWithLocation(const Heap::QQmlTypeWrapper *wrapper, Callback &&callback) +{ + auto log = qWarning().noquote().nospace(); + if (const CppStackFrame *frame = wrapper->internalClass->engine->currentStackFrame) + log << frame->source() << ':' << frame->lineNumber() << ':'; + callback(log.space()); +} + +void Heap::QQmlTypeWrapper::warnIfUncreatable() const +{ + const QQmlType t = type(); + Q_ASSERT(t.isValid()); + + if (t.isValueType()) + return; + + if (t.isSingleton()) { + warnWithLocation(this, [&](QDebug &log) { + log << "You are calling a Q_INVOKABLE constructor of" << t.typeName() + << "which is a singleton in QML."; + }); + return; + } + + if (!t.isCreatable()) { + warnWithLocation(this, [&](QDebug &log) { + log << "You are calling a Q_INVOKABLE constructor of" << t.typeName() + << "which is uncreatable in QML."; + }); + } } bool QQmlTypeWrapper::isSingleton() const @@ -85,6 +126,50 @@ bool QQmlTypeWrapper::isSingleton() const return d()->type().isSingleton(); } +const QMetaObject *QQmlTypeWrapper::metaObject() const +{ + const QQmlType type = d()->type(); + if (!type.isValid()) + return nullptr; + + if (type.isSingleton()) { + auto metaObjectCandidate = type.metaObject(); + // if the candidate is the same as te baseMetaObject, we know that + // we don't have an extended singleton; in that case the + // actual instance might be subclass of type instead of type itself + // so we need to query the actual object for it's meta-object + if (metaObjectCandidate == type.baseMetaObject()) { + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine()); + auto object = qmlEngine->singletonInstance<QObject *>(type); + if (object) + return object->metaObject(); + } + /* if we instead have an extended singleton, the dynamic proxy + meta-object must alreday be set up correctly + ### TODO: it isn't, as QQmlTypePrivate::init has no way to + query the object + */ + return metaObjectCandidate; + } + + return type.attachedPropertiesType(QQmlEnginePrivate::get(engine()->qmlEngine())); +} + +QObject *QQmlTypeWrapper::object() const +{ + const QQmlType type = d()->type(); + if (!type.isValid()) + return nullptr; + + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine()); + if (type.isSingleton()) + return qmlEngine->singletonInstance<QObject *>(type); + + return qmlAttachedPropertiesObject( + d()->object, + type.attachedPropertiesFunction(qmlEngine)); +} + QObject* QQmlTypeWrapper::singletonObject() const { if (!isSingleton()) @@ -96,74 +181,97 @@ QObject* QQmlTypeWrapper::singletonObject() const QVariant QQmlTypeWrapper::toVariant() const { - if (!isSingleton()) - return QVariant::fromValue<QObject *>(d()->object); - QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine()); const QQmlType type = d()->type(); + + if (!isSingleton()) { + return QVariant::fromValue(qmlAttachedPropertiesObject( + d()->object, type.attachedPropertiesFunction(e))); + } + if (type.isQJSValueSingleton()) return QVariant::fromValue<QJSValue>(e->singletonInstance<QJSValue>(type)); return QVariant::fromValue<QObject*>(e->singletonInstance<QObject*>(type)); } +ReturnedValue QQmlTypeWrapper::method_hasInstance( + const FunctionObject *, const Value *thisObject, const Value *argv, int argc) +{ + // we want to immediately call instanceOf rather than going through Function + + if (!argc) + return Encode(false); + if (const Object *o = thisObject->as<Object>()) + return o->instanceOf(argv[0]); + return Encode(false); +} + +ReturnedValue QQmlTypeWrapper::method_toString( + const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + const QQmlTypeWrapper *typeWrapper = thisObject->as<QQmlTypeWrapper>(); + if (!typeWrapper) + RETURN_UNDEFINED(); + + const QString name = typeWrapper->d()->type().qmlTypeName(); + return Encode(b->engine()->newString(name.isEmpty() + ? QLatin1String("Unknown Type") + : name)); +} + +void QQmlTypeWrapper::initProto(ExecutionEngine *v4) +{ + if (v4->typeWrapperPrototype()->d_unchecked()) + return; + + Scope scope(v4); + ScopedObject o(scope, v4->newObject()); + + o->defineDefaultProperty(v4->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly); + o->defineDefaultProperty(v4->id_toString(), method_toString, 0); + o->setPrototypeOf(v4->functionPrototype()); + + v4->jsObjects[QV4::ExecutionEngine::TypeWrapperProto] = o->d(); +} // Returns a type wrapper for type t on o. This allows access of enums, and attached properties. ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlType &t, Heap::QQmlTypeWrapper::TypeNameMode mode) { Q_ASSERT(t.isValid()); - Scope scope(engine); + initProto(engine); - Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>()); - w->d()->mode = mode; w->d()->object = o; - w->d()->typePrivate = t.priv(); - QQmlType::refHandle(w->d()->typePrivate); - return w.asReturnedValue(); + QV4::MemoryManager *mm = engine->memoryManager; + + if (const QMetaObject *mo = t.metaObject(); !mo || mo->constructorCount() == 0) + return mm->allocate<QQmlTypeWrapper>(mode, o, t.priv())->asReturnedValue(); + + return mm->allocate<QQmlTypeConstructor>(mode, o, t.priv())->asReturnedValue(); } // Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a // namespace. -ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t, const QQmlImportRef *importNamespace, - Heap::QQmlTypeWrapper::TypeNameMode mode) +ReturnedValue QQmlTypeWrapper::create( + QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t, + const QQmlImportRef *importNamespace, Heap::QQmlTypeWrapper::TypeNameMode mode) { Q_ASSERT(t); Q_ASSERT(importNamespace); + initProto(engine); + Scope scope(engine); - Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>()); - w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t.data(); w->d()->importNamespace = importNamespace; - t->addref(); + Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>( + mode, o, t.data(), importNamespace)); return w.asReturnedValue(); } -static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qobjectSingleton, - const QQmlType &type, bool *ok) +static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, const QQmlType &type, bool *ok) { Q_ASSERT(ok != nullptr); - int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, ok); - if (*ok) - return value; - - // ### Optimize - QByteArray enumName = name->toQString().toUtf8(); - const QMetaObject *metaObject = qobjectSingleton->metaObject(); - for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { - QMetaEnum e = metaObject->enumerator(ii); - value = e.keyToValue(enumName.constData(), ok); - if (*ok) - return value; - } - *ok = false; - return -1; -} - -static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *name, const QQmlType &type) -{ - const QString message = - QStringLiteral("Cannot access enum value '%1' of '%2', enum values need to start with an uppercase letter.") - .arg(name->toQString()).arg(QLatin1String(type.typeName())); - return v4->throwTypeError(message); + const int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, ok); + return *ok ? value : -1; } ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) @@ -174,7 +282,7 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons if (!id.isString()) return Object::virtualGet(m, id, receiver, hasProperty); - QV4::ExecutionEngine *v4 = static_cast<const QQmlTypeWrapper *>(m)->engine(); + QV4::ExecutionEngine *v4 = m->engine(); QV4::Scope scope(v4); ScopedString name(scope, id.asStringOrSymbol()); @@ -183,28 +291,30 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons if (hasProperty) *hasProperty = true; - QQmlContextData *context = v4->callingQmlContext(); + QQmlRefPointer<QQmlContextData> context = v4->callingQmlContext(); QObject *object = w->d()->object; QQmlType type = w->d()->type(); + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(v4->qmlEngine()); if (type.isValid()) { // singleton types are handled differently to other types. if (type.isSingleton()) { - QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine()); + QJSValue scriptSingleton; if (type.isQObjectSingleton() || type.isCompositeSingleton()) { - if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { + if (QObject *qobjectSingleton = enginePrivate->singletonInstance<QObject*>(type)) { // check for enum value - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + const bool includeEnums + = w->d()->typeNameMode() == Heap::QQmlTypeWrapper::IncludeEnums; if (includeEnums && name->startsWithUpper()) { bool ok = false; - int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok); + int value = enumForSingleton(v4, name, type, &ok); if (ok) return QV4::Value::fromInt32(value).asReturnedValue(); - value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); + value = type.scopedEnumIndex(enginePrivate, name, &ok); if (ok) { Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); enumWrapper->d()->typePrivate = type.priv(); @@ -216,24 +326,19 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons // check for property. bool ok; - const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); + const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty( + v4, context, w->d(), qobjectSingleton, name, + QV4::QObjectWrapper::AttachMethods, &ok); if (hasProperty) *hasProperty = ok; - // Warn when attempting to access a lowercased enum value, singleton case - if (!ok && includeEnums && !name->startsWithUpper()) { - enumForSingleton(v4, name, qobjectSingleton, type, &ok); - if (ok) - return throwLowercaseEnumError(v4, name, type); - } - return result; } } else if (type.isQJSValueSingleton()) { - QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); + QJSValue scriptSingleton = enginePrivate->singletonInstance<QJSValue>(type); if (!scriptSingleton.isUndefined()) { // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, scriptSingleton)); + QV4::ScopedObject o(scope, QJSValuePrivate::asReturnedValue(&scriptSingleton)); if (!!o) return o->get(name); } @@ -245,11 +350,11 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons if (name->startsWithUpper()) { bool ok = false; - int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); + int value = type.enumValue(enginePrivate, name, &ok); if (ok) return QV4::Value::fromInt32(value).asReturnedValue(); - value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); + value = type.scopedEnumIndex(enginePrivate, name, &ok); if (ok) { Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); enumWrapper->d()->typePrivate = type.priv(); @@ -265,7 +370,9 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons object, type.attachedPropertiesFunction(QQmlEnginePrivate::get(v4->qmlEngine()))); if (ao) - return QV4::QObjectWrapper::getQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty); + return QV4::QObjectWrapper::getQmlProperty( + v4, context, w->d(), ao, name, QV4::QObjectWrapper::AttachMethods, + hasProperty); // Fall through to base implementation } @@ -275,18 +382,16 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons // Fall through to base implementation - } else if (w->d()->typeNamespace) { - Q_ASSERT(w->d()->importNamespace); - QQmlTypeNameCache::Result r = w->d()->typeNamespace->query(name, w->d()->importNamespace); - + } else if (w->d()->kind() == Heap::QQmlTypeWrapper::Namespace) { + const QQmlTypeNameCache::Result r = w->d()->queryNamespace(name, enginePrivate); if (r.isValid()) { if (r.type.isValid()) { - return create(scope.engine, object, r.type, w->d()->mode); + return create(scope.engine, object, r.type, w->d()->typeNameMode()); } else if (r.scriptIndex != -1) { - QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); + QV4::ScopedObject scripts(scope, context->importedScripts().valueRef()); return scripts->get(r.scriptIndex); } else if (r.importNamespace) { - return create(scope.engine, object, context->imports, r.importNamespace); + return create(scope.engine, object, context->imports(), r.importNamespace); } return QV4::Encode::undefined(); @@ -304,14 +409,6 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons if (hasProperty) *hasProperty = ok; - // Warn when attempting to access a lowercased enum value, non-singleton case - if (!ok && type.isValid() && !type.isSingleton() && !name->startsWithUpper()) { - bool enumOk = false; - type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &enumOk); - if (enumOk) - return throwLowercaseEnumError(v4, name, type); - } - return result; } @@ -325,11 +422,11 @@ bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Q_ASSERT(m->as<QQmlTypeWrapper>()); QQmlTypeWrapper *w = static_cast<QQmlTypeWrapper *>(m); QV4::Scope scope(w); - if (scope.engine->hasException) + if (scope.hasException()) return false; ScopedString name(scope, id.asStringOrSymbol()); - QQmlContextData *context = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> context = scope.engine->callingQmlContext(); QQmlType type = w->d()->type(); if (type.isValid() && !type.isSingleton() && w->d()->object) { @@ -338,18 +435,21 @@ bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, QObject *ao = qmlAttachedPropertiesObject( object, type.attachedPropertiesFunction(QQmlEnginePrivate::get(e))); if (ao) - return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty( + scope.engine, context, ao, name, QV4::QObjectWrapper::NoFlag, value); return false; } else if (type.isSingleton()) { QQmlEnginePrivate *e = QQmlEnginePrivate::get(scope.engine->qmlEngine()); if (type.isQObjectSingleton() || type.isCompositeSingleton()) { if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) - return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty( + scope.engine, context, qobjectSingleton, name, + QV4::QObjectWrapper::NoFlag, value); } else { QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); if (!scriptSingleton.isUndefined()) { - QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, scriptSingleton)); + QV4::ScopedObject apiprivate(scope, QJSValuePrivate::asReturnedValue(&scriptSingleton)); if (!apiprivate) { QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); scope.engine->throwError(error); @@ -390,39 +490,35 @@ bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b) return false; } -ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var) +static ReturnedValue instanceOfQObject(const QV4::QQmlTypeWrapper *typeWrapper, const QObjectWrapper *objectWrapper) { - Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>()); - const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject); - QV4::ExecutionEngine *engine = typeObject->internalClass()->engine; - QQmlEnginePrivate *qenginepriv = QQmlEnginePrivate::get(engine->qmlEngine()); - - // can only compare a QObject* against a QML type - const QObjectWrapper *wrapper = var.as<QObjectWrapper>(); - if (!wrapper) - return engine->throwTypeError(); - + QV4::ExecutionEngine *engine = typeWrapper->internalClass()->engine; // in case the wrapper outlived the QObject* - const QObject *wrapperObject = wrapper->object(); + const QObject *wrapperObject = objectWrapper->object(); if (!wrapperObject) return engine->throwTypeError(); - const int myTypeId = typeWrapper->d()->type().typeId(); + const QMetaType myTypeId = typeWrapper->d()->type().typeId(); QQmlMetaObject myQmlType; - if (myTypeId == 0) { + if (!myTypeId.isValid()) { // we're a composite type; a composite type cannot be equal to a // non-composite object instance (Rectangle{} is never an instance of // CustomRectangle) - QQmlData *theirDData = QQmlData::get(wrapperObject, /*create=*/false); + QQmlData *theirDData = QQmlData::get(wrapperObject); Q_ASSERT(theirDData); // must exist, otherwise how do we have a QObjectWrapper for it?! if (!theirDData->compilationUnit) return Encode(false); + QQmlEnginePrivate *qenginepriv = QQmlEnginePrivate::get(engine->qmlEngine()); QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); - ExecutableCompilationUnit *cu = td->compilationUnit(); - myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); + if (CompiledData::CompilationUnit *cu = td->compilationUnit()) + myQmlType = QQmlMetaType::metaObjectForType(cu->metaType()); + else + return Encode(false); // It seems myQmlType has some errors, so we could not compile it. } else { - myQmlType = qenginepriv->metaObjectForType(myTypeId); + myQmlType = QQmlMetaType::metaObjectForType(myTypeId); + if (myQmlType.isNull()) + return Encode(false); } const QMetaObject *theirType = wrapperObject->metaObject(); @@ -430,6 +526,48 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType)); } +ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var) +{ + Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>()); + const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject); + + if (const QObjectWrapper *objectWrapper = var.as<QObjectWrapper>()) + return instanceOfQObject(typeWrapper, objectWrapper); + + const QQmlType type = typeWrapper->d()->type(); + + // If the target type is an object type we want null. + if (!type.isValueType()) + return Encode(false); + + const auto canCastValueType = [&]() -> bool { + if (const QQmlValueTypeWrapper *valueWrapper = var.as<QQmlValueTypeWrapper>()) { + return QQmlMetaObject::canConvert( + valueWrapper->metaObject(), type.metaObjectForValueType()); + } + + switch (type.typeId().id()) { + case QMetaType::Void: + return var.isUndefined(); + case QMetaType::QVariant: + return true; // Everything is a var + case QMetaType::Int: + return var.isInteger(); + case QMetaType::Double: + return var.isDouble(); // Integers are also doubles + case QMetaType::QString: + return var.isString(); + case QMetaType::Bool: + return var.isBoolean(); + } + + return false; + }; + + // We want "foo as valuetype" to return undefined if it doesn't match. + return canCastValueType() ? Encode(true) : Encode::undefined(); +} + ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) { // Keep this code in sync with ::virtualGet @@ -440,7 +578,7 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object); ScopedString name(scope, id.asStringOrSymbol()); - QQmlContextData *qmlContext = engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> qmlContext = engine->callingQmlContext(); Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This)); QQmlType type = w->d()->type(); @@ -451,19 +589,24 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine()); if (type.isQObjectSingleton() || type.isCompositeSingleton()) { if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + const bool includeEnums + = w->d()->typeNameMode() == Heap::QQmlTypeWrapper::IncludeEnums; if (!includeEnums || !name->startsWithUpper()) { QQmlData *ddata = QQmlData::get(qobjectSingleton, false); if (ddata && ddata->propertyCache) { - QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); + const QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); if (property) { ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); - lookup->qobjectLookup.qmlTypeIc = This->internalClass(); - lookup->qobjectLookup.ic = val->objectValue()->internalClass(); - lookup->qobjectLookup.propertyCache = ddata->propertyCache; - lookup->qobjectLookup.propertyCache->addref(); - lookup->qobjectLookup.propertyData = property; - lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; + if (qualifiesForMethodLookup(property)) { + QV4::Heap::QObjectMethod *method = nullptr; + setupQObjectMethodLookup( + lookup, ddata, property, val->objectValue(), method); + lookup->getter = QQmlTypeWrapper::lookupSingletonMethod; + } else { + setupQObjectLookup( + lookup, ddata, property, val->objectValue(), This); + lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; + } return lookup->getter(lookup, engine, *object); } // Fall through to base implementation @@ -481,7 +624,7 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, bool ok = false; int value = type.enumValue(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok); if (ok) { - lookup->qmlEnumValueLookup.ic = This->internalClass(); + lookup->qmlEnumValueLookup.ic.set(engine, This->internalClass()); lookup->qmlEnumValueLookup.encodedEnumValue = QV4::Value::fromInt32(value).asReturnedValue(); lookup->getter = QQmlTypeWrapper::lookupEnumValue; @@ -496,9 +639,9 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, QQmlType::refHandle(enumWrapper->d()->typePrivate); enumWrapper->d()->scopeEnumIndex = value; - lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass(); - lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper - = static_cast<Heap::Object*>(enumWrapper->heapObject()); + lookup->qmlScopedEnumWrapperLookup.ic.set(engine, This->internalClass()); + lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper.set(engine, + static_cast<Heap::Object*>(enumWrapper->heapObject())); lookup->getter = QQmlTypeWrapper::lookupScopedEnum; return enumWrapper.asReturnedValue(); } @@ -514,6 +657,30 @@ bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine return Object::virtualResolveLookupSetter(object, engine, lookup, value); } +OwnPropertyKeyIterator *QQmlTypeWrapper::virtualOwnPropertyKeys(const Object *m, Value *target) +{ + QV4::Scope scope(m->engine()); + QV4::Scoped<QQmlTypeWrapper> typeWrapper(scope, m); + Q_ASSERT(typeWrapper); + if (QObject *object = typeWrapper->object()) { + QV4::Scoped<QV4::QObjectWrapper> objectWrapper(scope, QV4::QObjectWrapper::wrap(typeWrapper->engine(), object)); + return QV4::QObjectWrapper::virtualOwnPropertyKeys(objectWrapper, target); + } + + return Object::virtualOwnPropertyKeys(m, target); +} + +int QQmlTypeWrapper::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a) +{ + QQmlTypeWrapper *wrapper = object->as<QQmlTypeWrapper>(); + Q_ASSERT(wrapper); + + if (QObject *qObject = wrapper->object()) + return QMetaObject::metacall(qObject, call, index, a); + + return 0; +} + ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &object) { const auto revertLookup = [l, engine, &object]() { @@ -526,6 +693,16 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin // 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()); + + // The qmlTypeIc check is not strictly necessary. + // If we have different ways to get to the same QObject type + // we can use the same lookup to get its properties, no matter + // how we've found the object. Most of the few times this check + // fails, we will, of course have different object types. So + // this check provides an early exit for the error case. + // + // So, if we ever need more bits in qobjectLookup, qmlTypeIc is the + // member to be replaced. if (!o || o->internalClass != l->qobjectLookup.qmlTypeIc) return revertLookup(); @@ -544,7 +721,42 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin Scope scope(engine); ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton)); - return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::AllowOverride + : (QObjectWrapper::AttachMethods | QObjectWrapper::AllowOverride); + return QObjectWrapper::lookupPropertyGetterImpl(l, engine, obj, flags, revertLookup); +} + +ReturnedValue QQmlTypeWrapper::lookupSingletonMethod(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [l, engine, &object]() { + l->qobjectMethodLookup.propertyCache->release(); + l->qobjectMethodLookup.propertyCache = nullptr; + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, object); + }; + + // We cannot safely cast here as we don't explicitly check the IC. Therefore as(). + const QQmlTypeWrapper *This = object.as<QQmlTypeWrapper>(); + if (!This) + return revertLookup(); + + QQmlType type = This->d()->type(); + if (!type.isValid()) + return revertLookup(); + + if (!type.isQObjectSingleton() && !type.isCompositeSingleton()) + return revertLookup(); + + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine()); + QObject *qobjectSingleton = e->singletonInstance<QObject *>(type); + Q_ASSERT(qobjectSingleton); + + Scope scope(engine); + ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton)); + return QObjectWrapper::lookupMethodGetterImpl( + l, engine, obj, l->forCall ? QObjectWrapper::NoFlag : QObjectWrapper::AttachMethods, + revertLookup); } ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base) @@ -562,12 +774,12 @@ ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engi { Scope scope(engine); Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>( - l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper)); + l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper.get())); auto *o = static_cast<Heap::Object *>(base.heapObject()); if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) { QQmlType::derefHandle(enumWrapper->d()->typePrivate); - l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr; + l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper.clear(); l->getter = Lookup::getterGeneric; return Lookup::getterGeneric(l, engine, base); } |