aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmltypewrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmltypewrapper.cpp')
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp544
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);
}