aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4qobjectwrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4qobjectwrapper.cpp')
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp2492
1 files changed, 1620 insertions, 872 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 41a57e36c9..5f85aae89e 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 "qv4qobjectwrapper_p.h"
@@ -50,6 +14,9 @@
#include <private/qqmlvaluetypewrapper_p.h>
#include <private/qqmllistwrapper_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
+#if QT_CONFIG(qml_locale)
+#include <private/qqmllocale_p.h>
+#endif
#include <private/qv4arraybuffer_p.h>
#include <private/qv4functionobject_p.h>
@@ -58,11 +25,7 @@
#include <private/qv4identifiertable_p.h>
#include <private/qv4lookup_p.h>
#include <private/qv4qmlcontext_p.h>
-
-#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
-#endif
-
#include <private/qv4objectproto_p.h>
#include <private/qv4jsonobject_p.h>
#include <private/qv4regexpobject_p.h>
@@ -73,6 +36,8 @@
#include <private/qqmlscriptstring_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qqmlpropertybinding_p.h>
+#include <private/qqmlpropertycachemethodarguments_p.h>
+#include <private/qqmlsignalnames_p.h>
#include <QtQml/qjsvalue.h>
#include <QtCore/qjsonarray.h>
@@ -86,25 +51,32 @@
#include <QtCore/qabstractitemmodel.h>
#endif
#include <QtCore/qloggingcategory.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qtypes.h>
#include <vector>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcBindingRemoval, "qt.qml.binding.removal", QtWarningMsg)
Q_LOGGING_CATEGORY(lcObjectConnect, "qt.qml.object.connect", QtWarningMsg)
+Q_LOGGING_CATEGORY(lcOverloadResolution, "qt.qml.overloadresolution", QtWarningMsg)
+Q_LOGGING_CATEGORY(lcMethodBehavior, "qt.qml.method.behavior")
+Q_LOGGING_CATEGORY(lcSignalHandler, "qt.qml.signalhandler")
// The code in this file does not violate strict aliasing, but GCC thinks it does
// so turn off the warnings for us to have a clean build
QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
-using namespace QV4;
+using namespace Qt::StringLiterals;
-QPair<QObject *, int> QObjectMethod::extractQtMethod(const QV4::FunctionObject *function)
+namespace QV4 {
+
+QPair<QObject *, int> QObjectMethod::extractQtMethod(const FunctionObject *function)
{
- QV4::ExecutionEngine *v4 = function->engine();
+ ExecutionEngine *v4 = function->engine();
if (v4) {
- QV4::Scope scope(v4);
- QV4::Scoped<QObjectMethod> method(scope, function->as<QObjectMethod>());
+ Scope scope(v4);
+ Scoped<QObjectMethod> method(scope, function->as<QObjectMethod>());
if (method)
return qMakePair(method->object(), method->methodIndex());
}
@@ -112,16 +84,16 @@ QPair<QObject *, int> QObjectMethod::extractQtMethod(const QV4::FunctionObject *
return qMakePair((QObject *)nullptr, -1);
}
-static QPair<QObject *, int> extractQtSignal(const QV4::Value &value)
+static QPair<QObject *, int> extractQtSignal(const Value &value)
{
if (value.isObject()) {
- QV4::ExecutionEngine *v4 = value.as<QV4::Object>()->engine();
- QV4::Scope scope(v4);
- QV4::ScopedFunctionObject function(scope, value);
+ ExecutionEngine *v4 = value.as<Object>()->engine();
+ Scope scope(v4);
+ ScopedFunctionObject function(scope, value);
if (function)
return QObjectMethod::extractQtMethod(function);
- QV4::Scoped<QV4::QmlSignalHandler> handler(scope, value);
+ Scoped<QmlSignalHandler> handler(scope, value);
if (handler)
return qMakePair(handler->object(), handler->signalIndex());
}
@@ -129,94 +101,223 @@ static QPair<QObject *, int> extractQtSignal(const QV4::Value &value)
return qMakePair((QObject *)nullptr, -1);
}
-static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object,
- const QQmlPropertyData &property)
+static Heap::ReferenceObject::Flags referenceFlags(
+ ExecutionEngine *v4,
+ const QQmlPropertyData &property)
+{
+ Heap::ReferenceObject::Flags flags = Heap::ReferenceObject::NoFlag;
+ if (CppStackFrame *stackFrame = v4->currentStackFrame) {
+ if (stackFrame->v4Function->executableCompilationUnit()->valueTypesAreCopied())
+ flags |= Heap::ReferenceObject::EnforcesLocation;
+ }
+
+ if (property.isWritable())
+ flags |= Heap::ReferenceObject::CanWriteBack;
+
+ return flags;
+}
+
+static ReturnedValue loadProperty(
+ ExecutionEngine *v4, Heap::Object *wrapper,
+ QObject *object, const QQmlPropertyData &property)
{
Q_ASSERT(!property.isFunction());
- QV4::Scope scope(v4);
- const QMetaType propMetaType = property.propType();
- const int propType = propMetaType.id();
+ Scope scope(v4);
+ const QMetaType propMetaType = property.propType();
if (property.isQObject()) {
QObject *rv = nullptr;
property.readProperty(object, &rv);
- QV4::ReturnedValue ret = QV4::QObjectWrapper::wrap(v4, rv);
- if (property.propType().flags().testFlag(QMetaType::IsConst)) {
- QV4::ScopedValue v(scope, ret);
- if (auto obj = v->as<Object>()) {
- obj->setInternalClass(obj->internalClass()->cryopreserved());
- return obj->asReturnedValue();
- }
- }
- return ret;
+ if (propMetaType.flags().testFlag(QMetaType::IsConst))
+ return QObjectWrapper::wrapConst(v4, rv);
+ else
+ return QObjectWrapper::wrap(v4, rv);
+ }
+
+ if (property.isQList() && propMetaType.flags().testFlag(QMetaType::IsQmlList))
+ return QmlListWrapper::create(v4, object, property.coreIndex(), propMetaType);
- } else if (property.isQList()) {
- return QmlListWrapper::create(v4, object, property.coreIndex(), property.propType().id());
- } else if (propType == QMetaType::QReal) {
- qreal v = 0;
+ const auto encodeSimple = [&](auto v) {
property.readProperty(object, &v);
- return QV4::Encode(v);
- } else if (propType == QMetaType::Int || property.isEnum()) {
- int v = 0;
+ return Encode(v);
+ };
+
+ const auto encodeInt = [&](auto v) {
property.readProperty(object, &v);
- return QV4::Encode(v);
- } else if (propType == QMetaType::Bool) {
- bool v = false;
+ return Encode(int(v));
+ };
+
+ const auto encodeDouble = [&](auto v) {
property.readProperty(object, &v);
- return QV4::Encode(v);
- } else if (propType == QMetaType::QString) {
- QString v;
+ return Encode(double(v));
+ };
+
+ const auto encodeDate = [&](auto v) {
+ property.readProperty(object, &v);
+ return Encode(v4->newDateObject(
+ v, wrapper, property.coreIndex(), referenceFlags(scope.engine, property)));
+ };
+
+ const auto encodeString = [&](auto v) {
property.readProperty(object, &v);
return v4->newString(v)->asReturnedValue();
- } else if (propType == QMetaType::UInt) {
- uint v = 0;
+ };
+
+ const auto encodeSequence = [&](QMetaSequence metaSequence) {
+ // Pass nullptr as data. It's lazy-loaded.
+ return QV4::SequencePrototype::newSequence(
+ v4, propMetaType, metaSequence, nullptr,
+ wrapper, property.coreIndex(), referenceFlags(scope.engine, property));
+ };
+
+
+ switch (property.isEnum() ? propMetaType.underlyingType().id() : propMetaType.id()) {
+ case QMetaType::UnknownType:
+ case QMetaType::Void:
+ return Encode::undefined();
+ case QMetaType::Nullptr:
+ case QMetaType::VoidStar:
+ return Encode::null();
+ case QMetaType::Int:
+ return encodeSimple(int());
+ case QMetaType::Bool:
+ return encodeSimple(bool());
+ case QMetaType::QString:
+ return encodeString(QString());
+ case QMetaType::QByteArray: {
+ QByteArray v;
property.readProperty(object, &v);
- return QV4::Encode(v);
- } else if (propType == QMetaType::Float) {
- float v = 0;
+ return v4->newArrayBuffer(v)->asReturnedValue();
+ }
+ case QMetaType::QChar:
+ return encodeString(QChar());
+ case QMetaType::Char16:
+ return encodeString(char16_t());
+ case QMetaType::UInt:
+ return encodeSimple(uint());
+ case QMetaType::Float:
+ return encodeSimple(float());
+ case QMetaType::Double:
+ return encodeSimple(double());
+ case QMetaType::Short:
+ return encodeInt(short());
+ case QMetaType::UShort:
+ return encodeInt(ushort());
+ case QMetaType::Char:
+ return encodeInt(char());
+ case QMetaType::UChar:
+ return encodeInt(uchar());
+ case QMetaType::SChar:
+ return encodeInt(qint8());
+ case QMetaType::Long:
+ return encodeDouble(long());
+ case QMetaType::ULong:
+ return encodeDouble(ulong());
+ case QMetaType::LongLong:
+ return encodeDouble(qlonglong());
+ case QMetaType::ULongLong:
+ return encodeDouble(qulonglong());
+ case QMetaType::QDateTime:
+ return encodeDate(QDateTime());
+ case QMetaType::QDate:
+ return encodeDate(QDate());
+ case QMetaType::QTime:
+ return encodeDate(QTime());
+#if QT_CONFIG(regularexpression)
+ case QMetaType::QRegularExpression: {
+ QRegularExpression v;
property.readProperty(object, &v);
- return QV4::Encode(v);
- } else if (propType == QMetaType::Double) {
- double v = 0;
+ return Encode(v4->newRegExpObject(v));
+ }
+#endif
+ case QMetaType::QVariantMap: {
+ QVariantMap v;
property.readProperty(object, &v);
- return QV4::Encode(v);
- } else if (propType == qMetaTypeId<QJSValue>()) {
+ return scope.engine->fromData(
+ propMetaType, &v, wrapper, property.coreIndex(), referenceFlags(v4, property));
+ }
+ case QMetaType::QJsonValue: {
+ QJsonValue v;
+ property.readProperty(object, &v);
+ return QV4::JsonObject::fromJsonValue(v4, v);
+ }
+ case QMetaType::QJsonObject: {
+ QJsonObject v;
+ property.readProperty(object, &v);
+ return QV4::JsonObject::fromJsonObject(v4, v);
+ }
+ case QMetaType::QJsonArray: {
+ QJsonArray v;
+ property.readProperty(object, &v);
+ return QV4::JsonObject::fromJsonArray(v4, v);
+ }
+ case QMetaType::QStringList:
+ return encodeSequence(QMetaSequence::fromContainer<QStringList>());
+ case QMetaType::QVariantList:
+ return encodeSequence(QMetaSequence::fromContainer<QVariantList>());
+ case QMetaType::QUrl: {
+ // ### Qt7: We really want this to be a JS URL object, but that would break things.
+ QUrl v;
+ property.readProperty(object, &v);
+ return Encode(v4->newVariantObject(propMetaType, &v));
+ }
+ case QMetaType::QPixmap:
+ case QMetaType::QImage: {
+ // Scarce value types
+ QVariant v(propMetaType);
+ property.readProperty(object, v.data());
+ return Encode(v4->newVariantObject(propMetaType, v.constData()));
+ }
+ default:
+ break;
+ }
+
+ if (propMetaType == QMetaType::fromType<QJSValue>()) {
QJSValue v;
property.readProperty(object, &v);
return QJSValuePrivate::convertToReturnedValue(v4, v);
- } else if (property.isQVariant()) {
+ }
+
+ if (property.isQVariant()) {
+ // We have to read the property even if it's a lazy-loaded reference object.
+ // Without reading it, we wouldn't know its inner type.
QVariant v;
property.readProperty(object, &v);
-
- if (QQmlMetaType::isValueType(v.metaType())) {
- if (const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForMetaType(v.metaType()))
- return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, v.metaType()); // VariantReference value-type.
- }
-
- return scope.engine->fromVariant(v);
- } else if (QQmlMetaType::isValueType(propMetaType)) {
- if (const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForMetaType(propMetaType))
- return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, propMetaType);
- } else {
-#if QT_CONFIG(qml_sequence_object)
- // see if it's a sequence type
- bool succeeded = false;
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, propType, object, property.coreIndex(), !property.isWritable(), &succeeded));
- if (succeeded)
- return retn->asReturnedValue();
-#endif
+ return scope.engine->fromVariant(
+ v, wrapper, property.coreIndex(),
+ referenceFlags(scope.engine, property) | Heap::ReferenceObject::IsVariant);
}
- if (propType == QMetaType::UnknownType) {
+ if (!propMetaType.isValid()) {
QMetaProperty p = object->metaObject()->property(property.coreIndex());
qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
"'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
- return QV4::Encode::undefined();
- } else {
- QVariant v(property.propType(), (void *)nullptr);
- property.readProperty(object, v.data());
- return scope.engine->fromVariant(v);
+ return Encode::undefined();
+ }
+
+ // TODO: For historical reasons we don't enforce locations for reference objects here.
+ // Once we do, we can eager load and use the fromVariant() below.
+ // Then the extra checks for value types and sequences can be dropped.
+
+ if (QQmlMetaType::isValueType(propMetaType)) {
+ if (const QMetaObject *valueTypeMetaObject
+ = QQmlMetaType::metaObjectForValueType(propMetaType)) {
+ // Lazy loaded value type reference. Pass nullptr as data.
+ return QQmlValueTypeWrapper::create(
+ v4, nullptr, valueTypeMetaObject, propMetaType, wrapper,
+ property.coreIndex(), referenceFlags(scope.engine, property));
+ }
}
+
+ // See if it's a sequence type.
+ const QQmlType qmlType = QQmlMetaType::qmlListType(propMetaType);
+ if (qmlType.isSequentialContainer())
+ return encodeSequence(qmlType.listMetaSequence());
+
+ QVariant v(propMetaType);
+ property.readProperty(object, v.data());
+ return scope.engine->fromVariant(
+ v, wrapper, property.coreIndex(), referenceFlags(scope.engine, property));
}
void QObjectWrapper::initializeBindings(ExecutionEngine *engine)
@@ -225,32 +326,33 @@ void QObjectWrapper::initializeBindings(ExecutionEngine *engine)
engine->functionPrototype()->defineDefaultProperty(QStringLiteral("disconnect"), method_disconnect);
}
-QQmlPropertyData *QObjectWrapper::findProperty(
- ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, String *name,
- RevisionMode revisionMode, QQmlPropertyData *local) const
+const QQmlPropertyData *QObjectWrapper::findProperty(
+ const QQmlRefPointer<QQmlContextData> &qmlContext, String *name,
+ Flags flags, QQmlPropertyData *local) const
{
- QObject *o = d()->object();
- return findProperty(engine, o, qmlContext, name, revisionMode, local);
+ return findProperty(d()->object(), qmlContext, name, flags, local);
}
-QQmlPropertyData *QObjectWrapper::findProperty(
- ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext,
- String *name, RevisionMode revisionMode, QQmlPropertyData *local)
+const QQmlPropertyData *QObjectWrapper::findProperty(
+ QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext,
+ String *name, Flags flags, QQmlPropertyData *local)
{
- Q_UNUSED(revisionMode);
+ Q_UNUSED(flags);
QQmlData *ddata = QQmlData::get(o, false);
- QQmlPropertyData *result = nullptr;
+ const QQmlPropertyData *result = nullptr;
if (ddata && ddata->propertyCache)
result = ddata->propertyCache->property(name, o, qmlContext);
else
- result = QQmlPropertyCache::property(engine->jsEngine(), o, name, qmlContext, local);
+ result = QQmlPropertyCache::property(o, name, qmlContext, local);
return result;
}
-ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property)
+ReturnedValue QObjectWrapper::getProperty(
+ ExecutionEngine *engine, Heap::Object *wrapper, QObject *object,
+ const QQmlPropertyData *property, Flags flags)
{
- QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex()));
+ QQmlData::flushPendingBinding(object, property->coreIndex());
if (property->isFunction() && !property->isVarProperty()) {
if (property->isVMEFunction()) {
@@ -262,44 +364,49 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
ScopedContext global(scope, engine->qmlContext());
if (!global)
global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, property->coreIndex());
+ return QObjectMethod::create(
+ global, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
} else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine);
- return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
+ return engine->memoryManager->allocate<QmlSignalHandler>(
+ object, property->coreIndex())->asReturnedValue();
} else {
ExecutionContext *global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, property->coreIndex());
+ return QObjectMethod::create(
+ global, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
}
}
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
if (ep && ep->propertyCapture && !property->isConstant())
- ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
+ if (!property->isBindable() || ep->propertyCapture->expression->mustCaptureBindableProperty())
+ ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
if (property->isVarProperty()) {
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
Q_ASSERT(vmemo);
return vmemo->vmeProperty(property->coreIndex());
} else {
- return loadProperty(engine, object, *property);
+ return loadProperty(engine, wrapper, object, *property);
}
}
-static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr)
+static OptionalReturnedValue getDestroyOrToStringMethod(
+ ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
{
int index = 0;
if (name->equals(v4->id_destroy()))
- index = QV4::QObjectMethod::DestroyMethod;
+ index = QObjectMethod::DestroyMethod;
else if (name->equals(v4->id_toString()))
- index = QV4::QObjectMethod::ToStringMethod;
+ index = QObjectMethod::ToStringMethod;
else
return OptionalReturnedValue();
if (hasProperty)
*hasProperty = true;
ExecutionContext *global = v4->rootContext();
- return OptionalReturnedValue(QV4::QObjectMethod::create(global, qobj, index));
+ return OptionalReturnedValue(QObjectMethod::create(global, qobj, index));
}
static OptionalReturnedValue getPropertyFromImports(
@@ -309,96 +416,102 @@ static OptionalReturnedValue getPropertyFromImports(
if (!qmlContext || !qmlContext->imports())
return OptionalReturnedValue();
- QQmlTypeNameCache::Result r = qmlContext->imports()->query(name);
-
if (hasProperty)
*hasProperty = true;
- if (!r.isValid())
+ if (QQmlTypeLoader *typeLoader = v4->typeLoader()) {
+ QQmlTypeNameCache::Result r = qmlContext->imports()->query(name, typeLoader);
+
+ if (!r.isValid())
+ return OptionalReturnedValue();
+
+ if (r.scriptIndex != -1) {
+ return OptionalReturnedValue(Encode::undefined());
+ } else if (r.type.isValid()) {
+ return OptionalReturnedValue(
+ QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
+ } else if (r.importNamespace) {
+ return OptionalReturnedValue(QQmlTypeWrapper::create(
+ v4, qobj, qmlContext->imports(), r.importNamespace,
+ Heap::QQmlTypeWrapper::ExcludeEnums));
+ }
+ Q_UNREACHABLE_RETURN(OptionalReturnedValue());
+ } else {
return OptionalReturnedValue();
-
- if (r.scriptIndex != -1) {
- return OptionalReturnedValue(QV4::Encode::undefined());
- } else if (r.type.isValid()) {
- return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
- } else if (r.importNamespace) {
- return OptionalReturnedValue(QQmlTypeWrapper::create(
- v4, qobj, qmlContext->imports(), r.importNamespace,
- Heap::QQmlTypeWrapper::ExcludeEnums));
}
- Q_UNREACHABLE();
- return OptionalReturnedValue();
}
ReturnedValue QObjectWrapper::getQmlProperty(
const QQmlRefPointer<QQmlContextData> &qmlContext, String *name,
- QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports) const
+ QObjectWrapper::Flags flags, bool *hasProperty) const
{
// Keep this code in sync with ::virtualResolveLookupGetter
if (QQmlData::wasDeleted(d()->object())) {
if (hasProperty)
*hasProperty = false;
- return QV4::Encode::undefined();
+ return Encode::undefined();
}
ExecutionEngine *v4 = engine();
- if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty))
+ if (auto methodValue = getDestroyOrToStringMethod(v4, name, d(), hasProperty))
return *methodValue;
QQmlPropertyData local;
- QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local);
+ const QQmlPropertyData *result = findProperty(qmlContext, name, flags, &local);
if (!result) {
// Check for attached properties
- if (includeImports && name->startsWithUpper()) {
- if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty))
+ if ((flags & IncludeImports) && name->startsWithUpper()) {
+ if (auto importProperty = getPropertyFromImports(
+ v4, name, qmlContext, d()->object(), hasProperty))
return *importProperty;
}
- return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty);
+ return Object::virtualGet(this, name->propertyKey(), this, hasProperty);
}
QQmlData *ddata = QQmlData::get(d()->object(), false);
- if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
+ if ((flags & CheckRevision) && result->hasRevision()) {
if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) {
if (hasProperty)
*hasProperty = false;
- return QV4::Encode::undefined();
+ return Encode::undefined();
}
}
if (hasProperty)
*hasProperty = true;
- return getProperty(v4, d()->object(), result);
+ return getProperty(v4, d(), d()->object(), result, flags);
}
ReturnedValue QObjectWrapper::getQmlProperty(
- QV4::ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
- QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty,
- QQmlPropertyData **property)
+ ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
+ Heap::Object *wrapper, QObject *object, String *name, QObjectWrapper::Flags flags,
+ bool *hasProperty, const QQmlPropertyData **property)
{
if (QQmlData::wasDeleted(object)) {
if (hasProperty)
*hasProperty = false;
- return QV4::Encode::null();
+ return Encode::null();
}
- if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty))
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, wrapper, hasProperty))
return *methodValue;
QQmlData *ddata = QQmlData::get(object, false);
QQmlPropertyData local;
- QQmlPropertyData *result = findProperty(engine, object, qmlContext, name, revisionMode, &local);
+ const QQmlPropertyData *result = findProperty(object, qmlContext, name, flags, &local);
if (result) {
- if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
- if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) {
+ if ((flags & QObjectWrapper::CheckRevision) && result->hasRevision()) {
+ if (ddata && ddata->propertyCache
+ && !ddata->propertyCache->isAllowedInRevision(result)) {
if (hasProperty)
*hasProperty = false;
- return QV4::Encode::undefined();
+ return Encode::undefined();
}
}
@@ -408,7 +521,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
if (property && result != &local)
*property = result;
- return getProperty(engine, object, result);
+ return getProperty(engine, wrapper, object, result, flags);
} else {
// Check if this object is already wrapped.
if (!ddata || (ddata->jsWrapper.isUndefined() &&
@@ -418,7 +531,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
// Not wrapped. Last chance: try query QObjectWrapper's prototype.
// If it can't handle this, then there is no point
// to wrap the QObject just to look at an empty set of JS props.
- QV4::Object *proto = QObjectWrapper::defaultPrototype(engine);
+ Object *proto = QObjectWrapper::defaultPrototype(engine);
return proto->get(name, hasProperty);
}
}
@@ -427,30 +540,30 @@ ReturnedValue QObjectWrapper::getQmlProperty(
// There's no point wrapping again, as there wouldn't be any new props.
Q_ASSERT(ddata);
- QV4::Scope scope(engine);
- QV4::Scoped<QObjectWrapper> wrapper(scope, wrap(engine, object));
- if (!wrapper) {
+ Scope scope(engine);
+ Scoped<QObjectWrapper> rewrapped(scope, wrap(engine, object));
+ if (!rewrapped) {
if (hasProperty)
*hasProperty = false;
- return QV4::Encode::null();
+ return Encode::null();
}
- return wrapper->getQmlProperty(qmlContext, name, revisionMode, hasProperty);
+ return rewrapped->getQmlProperty(qmlContext, name, flags, hasProperty);
}
bool QObjectWrapper::setQmlProperty(
ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, QObject *object,
- String *name, QObjectWrapper::RevisionMode revisionMode, const Value &value)
+ String *name, QObjectWrapper::Flags flags, const Value &value)
{
if (QQmlData::wasDeleted(object))
return false;
QQmlPropertyData local;
- QQmlPropertyData *result = QQmlPropertyCache::property(engine->jsEngine(), object, name, qmlContext, &local);
+ const QQmlPropertyData *result = QQmlPropertyCache::property(object, name, qmlContext, &local);
if (!result)
return false;
- if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
+ if ((flags & QObjectWrapper::CheckRevision) && result->hasRevision()) {
QQmlData *ddata = QQmlData::get(object);
if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
return false;
@@ -460,7 +573,31 @@ bool QObjectWrapper::setQmlProperty(
return true;
}
-void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value)
+/*!
+ \internal
+ If an QObjectWrapper is created via wrap, then it needs to be stored somewhere.
+ Otherwise, the garbage collector will immediately collect it if it is already
+ past the "mark QObjectWrapper's" phase (note that QObjectWrapper are marked
+ by iterating over a list of all QObjectWrapper, and then checking if the
+ wrapper fulfills some conditions).
+ However, sometimes we don't really want to keep a reference to the wrapper,
+ but just want to make sure that it exists (and we know that the wrapper
+ already fulfills the conditions to be kept alive). Then ensureWrapper
+ can be used, which creates the wrapper and ensures that it is also
+ marked.
+ */
+void QObjectWrapper::ensureWrapper(ExecutionEngine *engine, QObject *object)
+{
+ QV4::Scope scope(engine);
+ QV4::Scoped<QV4::QObjectWrapper> wrapper {scope, QV4::QObjectWrapper::wrap(engine, object)};
+ QV4::WriteBarrier::markCustom(engine, [&wrapper](QV4::MarkStack *ms) {
+ wrapper->mark(ms);
+ });
+}
+
+void QObjectWrapper::setProperty(
+ ExecutionEngine *engine, QObject *object,
+ const QQmlPropertyData *property, const Value &value)
{
if (!property->isWritable() && !property->isQList()) {
QString error = QLatin1String("Cannot assign to read-only property \"") +
@@ -469,17 +606,19 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
return;
}
- QV4::Scope scope(engine);
- if (QV4::ScopedFunctionObject f(scope, value); f) {
+ Scope scope(engine);
+ if (ScopedFunctionObject f(scope, value); f) {
if (!f->isBinding()) {
const bool isAliasToAllowed = [&]() {
if (property->isAlias()) {
const QQmlPropertyIndex originalIndex(property->coreIndex(), -1);
auto [targetObject, targetIndex] = QQmlPropertyPrivate::findAliasTarget(object, originalIndex);
Q_ASSERT(targetObject);
- QQmlPropertyCache *targetCache = QQmlData::get(targetObject)->propertyCache;
+ const QQmlPropertyCache *targetCache
+ = QQmlData::get(targetObject)->propertyCache.data();
Q_ASSERT(targetCache);
- QQmlPropertyData *targetProperty = targetCache->property(targetIndex.coreIndex());
+ const QQmlPropertyData *targetProperty
+ = targetCache->property(targetIndex.coreIndex());
object = targetObject;
property = targetProperty;
return targetProperty->isVarProperty() || targetProperty->propType() == QMetaType::fromType<QJSValue>();
@@ -487,7 +626,8 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
return false;
}
}();
- if (!isAliasToAllowed && !property->isVarProperty() && property->propType().id() != qMetaTypeId<QJSValue>()) {
+ if (!isAliasToAllowed && !property->isVarProperty()
+ && property->propType() != QMetaType::fromType<QJSValue>()) {
// assigning a JS function to a non var or QJSValue property or is not allowed.
QString error = QLatin1String("Cannot assign JavaScript function to ");
if (!QMetaType(property->propType()).name())
@@ -500,9 +640,9 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
} else {
QQmlRefPointer<QQmlContextData> callingQmlContext = scope.engine->callingQmlContext();
- QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
- QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
- QV4::ScopedContext ctx(scope, f->scope());
+ Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
+ ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
+ ScopedContext ctx(scope, f->scope());
// binding assignment.
if (property->isBindable()) {
@@ -510,7 +650,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
auto [targetObject, targetIndex] = QQmlPropertyPrivate::findAliasTarget(object, idx);
QUntypedPropertyBinding binding;
if (f->isBoundFunction()) {
- auto boundFunction = static_cast<QV4::BoundFunction *>(f.getPointer());
+ auto boundFunction = static_cast<BoundFunction *>(f.getPointer());
binding = QQmlPropertyBinding::createFromBoundFunction(property, boundFunction, object, callingQmlContext,
ctx, targetObject, targetIndex);
} else {
@@ -523,14 +663,15 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
targetObject->metaObject()->metacall(targetObject, QMetaObject::BindableProperty, targetIndex.coreIndex(), &argv);
bool ok = bindable.setBinding(binding);
if (!ok) {
- auto error = QStringLiteral("Failed to set binding on %1::%2.").arg(object->metaObject()->className(), property->name(object));
+ auto error = QStringLiteral("Failed to set binding on %1::%2.").
+ arg(QString::fromUtf8(object->metaObject()->className()), property->name(object));
scope.engine->throwError(error);
}
} else {
QQmlBinding *newBinding = QQmlBinding::create(property, f->function(), object, callingQmlContext, ctx);
newBinding->setSourceLocation(bindingFunction->currentLocation());
if (f->isBoundFunction())
- newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
+ newBinding->setBoundFunction(static_cast<BoundFunction *>(f.getPointer()));
newBinding->setTarget(object, *property, nullptr);
QQmlPropertyPrivate::setBinding(newBinding);
}
@@ -540,14 +681,26 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) {
- Q_ASSERT(!binding->isValueTypeProxy());
- const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
const auto stackFrame = engine->currentStackFrame;
- qCInfo(lcBindingRemoval,
- "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
- object->metaObject()->className(), qPrintable(property->name(object)),
- qPrintable(stackFrame->source()), stackFrame->lineNumber(),
- qPrintable(qmlBinding->expressionIdentifier()));
+ switch (binding->kind()) {
+ case QQmlAbstractBinding::QmlBinding: {
+ const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
+ qCInfo(lcBindingRemoval,
+ "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
+ object->metaObject()->className(), qPrintable(property->name(object)),
+ qPrintable(stackFrame->source()), stackFrame->lineNumber(),
+ qPrintable(qmlBinding->expressionIdentifier()));
+ break;
+ }
+ case QQmlAbstractBinding::ValueTypeProxy:
+ case QQmlAbstractBinding::PropertyToPropertyBinding: {
+ qCInfo(lcBindingRemoval,
+ "Overwriting binding on %s::%s at %s:%d",
+ object->metaObject()->className(), qPrintable(property->name(object)),
+ qPrintable(stackFrame->source()), stackFrame->lineNumber());
+ break;
+ }
+ }
}
}
QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
@@ -567,59 +720,60 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
void *argv[] = { &o, 0, &status, &flags }; \
QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
- const int propType = property->propType().id();
+ const QMetaType propType = property->propType();
// functions are already handled, except for the QJSValue case
- Q_ASSERT(!value.as<FunctionObject>() || propType == qMetaTypeId<QJSValue>());
+ Q_ASSERT(!value.as<FunctionObject>() || propType == QMetaType::fromType<QJSValue>());
if (value.isNull() && property->isQObject()) {
PROPERTY_STORE(QObject*, nullptr);
} else if (value.isUndefined() && property->isResettable()) {
void *a[] = { nullptr };
QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex(), a);
- } else if (value.isUndefined() && propType == qMetaTypeId<QVariant>()) {
+ } else if (value.isUndefined() && propType == QMetaType::fromType<QVariant>()) {
PROPERTY_STORE(QVariant, QVariant());
- } else if (value.isUndefined() && propType == QMetaType::QJsonValue) {
+ } else if (value.isUndefined() && propType == QMetaType::fromType<QJsonValue>()) {
PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
- } else if (propType == qMetaTypeId<QJSValue>()) {
+ } else if (propType == QMetaType::fromType<QJSValue>()) {
PROPERTY_STORE(QJSValue, QJSValuePrivate::fromReturnedValue(value.asReturnedValue()));
- } else if (value.isUndefined() && propType != qMetaTypeId<QQmlScriptString>()) {
+ } else if (value.isUndefined() && propType != QMetaType::fromType<QQmlScriptString>()) {
QString error = QLatin1String("Cannot assign [undefined] to ");
- if (!property->propType().name())
+ if (!propType.name())
error += QLatin1String("[unknown property type]");
else
- error += QLatin1String(property->propType().name());
+ error += QLatin1String(propType.name());
scope.engine->throwError(error);
return;
- } else if (property->propType().id() == QMetaType::Int && value.isNumber()) {
- PROPERTY_STORE(int, value.asDouble());
- } else if (propType == QMetaType::QReal && value.isNumber()) {
+ } else if (propType == QMetaType::fromType<int>() && value.isNumber()) {
+ PROPERTY_STORE(int, value.toInt32());
+ } else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) {
PROPERTY_STORE(qreal, qreal(value.asDouble()));
- } else if (propType == QMetaType::Float && value.isNumber()) {
+ } else if (propType == QMetaType::fromType<float>() && value.isNumber()) {
PROPERTY_STORE(float, float(value.asDouble()));
- } else if (propType == QMetaType::Double && value.isNumber()) {
+ } else if (propType == QMetaType::fromType<double>() && value.isNumber()) {
PROPERTY_STORE(double, double(value.asDouble()));
- } else if (propType == QMetaType::QString && value.isString()) {
+ } else if (propType == QMetaType::fromType<QString>() && value.isString()) {
PROPERTY_STORE(QString, value.toQStringNoThrow());
} else if (property->isVarProperty()) {
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
Q_ASSERT(vmemo);
vmemo->setVMEProperty(property->coreIndex(), value);
- } else if (propType == qMetaTypeId<QQmlScriptString>() && (value.isUndefined() || value.isPrimitive())) {
+ } else if (propType == QMetaType::fromType<QQmlScriptString>()
+ && (value.isUndefined() || value.isPrimitive())) {
QQmlScriptString ss(value.toQStringNoThrow(), nullptr /* context */, object);
if (value.isNumber()) {
ss.d->numberValue = value.toNumber();
ss.d->isNumberLiteral = true;
} else if (value.isString()) {
- ss.d->script = QV4::CompiledData::Binding::escapedString(ss.d->script);
+ ss.d->script = CompiledData::Binding::escapedString(ss.d->script);
ss.d->isStringLiteral = true;
}
PROPERTY_STORE(QQmlScriptString, ss);
} else {
QVariant v;
- if (property->isQList())
- v = scope.engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
+ if (property->isQList() && propType.flags().testFlag(QMetaType::IsQmlList))
+ v = ExecutionEngine::toVariant(value, QMetaType::fromType<QList<QObject *> >());
else
- v = scope.engine->toVariant(value, propType);
+ v = ExecutionEngine::toVariant(value, propType);
QQmlRefPointer<QQmlContextData> callingQmlContext = scope.engine->callingQmlContext();
if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) {
@@ -627,7 +781,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
? "an unknown type"
: QMetaType(v.userType()).name();
- const char *targetTypeName = QMetaType(property->propType()).name();
+ const char *targetTypeName = propType.name();
if (!targetTypeName)
targetTypeName = "an unregistered type";
@@ -647,7 +801,7 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob
QQmlData *ddata = QQmlData::get(object, true);
if (!ddata)
- return QV4::Encode::undefined();
+ return Encode::undefined();
Scope scope(engine);
@@ -656,7 +810,7 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob
ddata->jsEngineId == 0 || // No one owns the QObject
!ddata->hasTaintedV4Object)) { // Someone else has used the QObject, but it isn't tainted
- QV4::ScopedValue rv(scope, create(engine, object));
+ ScopedValue rv(scope, create(engine, object));
ddata->jsWrapper.set(scope.engine, rv);
ddata->jsEngineId = engine->m_engineId;
return rv->asReturnedValue();
@@ -671,7 +825,7 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob
// If our tainted handle doesn't exist or has been collected, and there isn't
// a handle in the ddata, we can assume ownership of the ddata->jsWrapper
if (ddata->jsWrapper.isUndefined() && !alternateWrapper) {
- QV4::ScopedValue result(scope, create(engine, object));
+ ScopedValue result(scope, create(engine, object));
ddata->jsWrapper.set(scope.engine, result);
ddata->jsEngineId = engine->m_engineId;
return result->asReturnedValue();
@@ -689,6 +843,29 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob
}
}
+ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObject *object)
+{
+ const QObject *constObject = object;
+
+ QQmlData *ddata = QQmlData::get(object, true);
+
+ Scope scope(engine);
+ ScopedObject constWrapper(scope);
+ if (engine->m_multiplyWrappedQObjects && ddata->hasConstWrapper)
+ constWrapper = engine->m_multiplyWrappedQObjects->value(constObject);
+
+ if (!constWrapper) {
+ constWrapper = create(engine, object);
+ constWrapper->setInternalClass(constWrapper->internalClass()->cryopreserved());
+ if (!engine->m_multiplyWrappedQObjects)
+ engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap;
+ engine->m_multiplyWrappedQObjects->insert(constObject, constWrapper->d());
+ ddata->hasConstWrapper = true;
+ }
+
+ return constWrapper.asReturnedValue();
+}
+
void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
{
if (QQmlData::wasDeleted(object))
@@ -698,11 +875,15 @@ void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
if (!ddata)
return;
- const QV4::ExecutionEngine *engine = markStack->engine();
+ const ExecutionEngine *engine = markStack->engine();
if (ddata->jsEngineId == engine->m_engineId)
ddata->jsWrapper.markOnce(markStack);
else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
engine->m_multiplyWrappedQObjects->mark(object, markStack);
+ if (ddata->hasConstWrapper) {
+ Q_ASSERT(engine->m_multiplyWrappedQObjects);
+ engine->m_multiplyWrappedQObjects->mark(static_cast<const QObject *>(object), markStack);
+ }
}
void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value)
@@ -721,40 +902,36 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int p
if (!ddata)
return;
- QQmlPropertyCache *cache = ddata->propertyCache;
- Q_ASSERT(cache);
- QQmlPropertyData *property = cache->property(propertyIndex);
+ Q_ASSERT(ddata->propertyCache);
+ const QQmlPropertyData *property = ddata->propertyCache->property(propertyIndex);
Q_ASSERT(property); // We resolved this property earlier, so it better exist!
return setProperty(engine, object, property, value);
}
bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
{
- Q_ASSERT(a->as<QV4::QObjectWrapper>());
- QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a);
- QV4::Object *o = b->as<Object>();
- if (o) {
- if (QV4::QQmlTypeWrapper *qmlTypeWrapper = o->as<QV4::QQmlTypeWrapper>())
- return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object();
- }
+ Q_ASSERT(a->as<QObjectWrapper>());
+ const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a);
+ if (const QQmlTypeWrapper *qmlTypeWrapper = b->as<QQmlTypeWrapper>())
+ return qmlTypeWrapper->object() == aobjectWrapper->object();
- return false;
+ // We can have a const and a non-const wrapper for the same object.
+ const QObjectWrapper *bobjectWrapper = b->as<QObjectWrapper>();
+ return bobjectWrapper && aobjectWrapper->object() == bobjectWrapper->object();
}
ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
{
- if (QJSEngine *jsEngine = engine->jsEngine()) {
- if (QQmlPropertyCache *cache = QQmlData::ensurePropertyCache(jsEngine, object)) {
- ReturnedValue result = QV4::Encode::null();
- void *args[] = { &result, &engine };
- if (cache->callJSFactoryMethod(object, args))
- return result;
- }
+ if (QQmlPropertyCache::ConstPtr cache = QQmlData::ensurePropertyCache(object)) {
+ ReturnedValue result = Encode::null();
+ void *args[] = { &result, &engine };
+ if (cache->callJSFactoryMethod(object, args))
+ return result;
}
- return (engine->memoryManager->allocate<QV4::QObjectWrapper>(object))->asReturnedValue();
+ return (engine->memoryManager->allocate<QObjectWrapper>(object))->asReturnedValue();
}
-QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
if (!id.isString())
return Object::virtualGet(m, id, receiver, hasProperty);
@@ -763,7 +940,7 @@ QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id,
Scope scope(that);
ScopedString n(scope, id.asStringOrSymbol());
QQmlRefPointer<QQmlContextData> qmlContext = that->engine()->callingQmlContext();
- return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true);
+ return that->getQmlProperty(qmlContext, n, IncludeImports | AttachMethods, hasProperty);
}
bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
@@ -775,19 +952,18 @@ bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value,
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
ScopedString name(scope, id.asStringOrSymbol());
- if (that->internalClass()->isFrozen) {
+ if (that->internalClass()->isFrozen()) {
QString error = QLatin1String("Cannot assign to property \"") +
name->toQString() + QLatin1String("\" of read-only object");
scope.engine->throwError(error);
return false;
}
-
- if (scope.engine->hasException || QQmlData::wasDeleted(that->d()->object()))
+ if (scope.hasException() || QQmlData::wasDeleted(that->d()->object()))
return false;
QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext();
- if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) {
+ if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, NoFlag, value)) {
QQmlData *ddata = QQmlData::get(that->d()->object());
// Types created by QML are not extensible at run-time, but for other QObjects we can store them
// as regular JavaScript properties, like on JavaScript objects.
@@ -797,7 +973,7 @@ bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value,
scope.engine->throwError(error);
return false;
} else {
- return QV4::Object::virtualPut(m, id, value, receiver);
+ return Object::virtualPut(m, id, value, receiver);
}
}
@@ -814,32 +990,33 @@ PropertyAttributes QObjectWrapper::virtualGetOwnProperty(const Managed *m, Prope
ScopedString n(scope, id.asStringOrSymbol());
QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext();
QQmlPropertyData local;
- if (that->findProperty(scope.engine, qmlContext, n, IgnoreRevision, &local)
+ if (that->findProperty(qmlContext, n, NoFlag, &local)
|| n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) {
if (p) {
// ### probably not the fastest implementation
bool hasProperty;
- p->value = that->getQmlProperty(qmlContext, n, IgnoreRevision, &hasProperty, /*includeImports*/ true);
+ p->value = that->getQmlProperty(
+ qmlContext, n, IncludeImports | AttachMethods, &hasProperty);
}
- return QV4::Attr_Data;
+ return Attr_Data;
}
}
}
- return QV4::Object::virtualGetOwnProperty(m, id, p);
+ return Object::virtualGetOwnProperty(m, id, p);
}
struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
{
int propertyIndex = 0;
~QObjectWrapperOwnPropertyKeyIterator() override = default;
- PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
private:
QSet<QByteArray> m_alreadySeen;
};
-PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs)
+PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
{
// Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
@@ -861,11 +1038,13 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro
ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name())));
++propertyIndex;
if (attrs)
- *attrs= QV4::Attr_Data;
+ *attrs= Attr_Data;
if (pd) {
QQmlPropertyData local;
local.load(property);
- pd->value = that->getProperty(thatEngine, thatObject, &local);
+ pd->value = that->getProperty(
+ thatEngine, that->d(), thatObject, &local,
+ QObjectWrapper::AttachMethods);
}
return propName->toPropertyKey();
}
@@ -886,11 +1065,13 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro
Scope scope(thatEngine);
ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name())));
if (attrs)
- *attrs = QV4::Attr_Data;
+ *attrs = Attr_Data;
if (pd) {
QQmlPropertyData local;
local.load(method);
- pd->value = that->getProperty(thatEngine, thatObject, &local);
+ pd->value = that->getProperty(
+ thatEngine, that->d(), thatObject, &local,
+ QObjectWrapper::AttachMethods);
}
return methodName->toPropertyKey();
}
@@ -921,18 +1102,27 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
QObject * const qobj = This->d()->object();
if (QQmlData::wasDeleted(qobj))
- return QV4::Encode::undefined();
-
- if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj))
- return *methodValue;
+ return Encode::undefined();
QQmlData *ddata = QQmlData::get(qobj, false);
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, This->d())) {
+ Scoped<QObjectMethod> method(scope, *methodValue);
+ setupQObjectMethodLookup(
+ lookup, ddata ? ddata : QQmlData::get(qobj, true), nullptr, This, method->d());
+ lookup->getter = Lookup::getterQObjectMethod;
+ return method.asReturnedValue();
+ }
+
if (!ddata || !ddata->propertyCache) {
QQmlPropertyData local;
- QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, &local);
- return property ? getProperty(engine, qobj, property) : QV4::Encode::undefined();
+ const QQmlPropertyData *property = QQmlPropertyCache::property(
+ qobj, name, qmlContext, &local);
+ return property
+ ? getProperty(engine, This->d(), qobj, property,
+ lookup->forCall ? NoFlag : AttachMethods)
+ : Encode::undefined();
}
- QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
+ const QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
if (!property) {
// Check for attached properties
@@ -940,27 +1130,28 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
if (auto importProperty = getPropertyFromImports(engine, name, qmlContext, qobj))
return *importProperty;
}
- return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
+ return Object::virtualResolveLookupGetter(object, engine, lookup);
+ }
+
+ if (property->isFunction()
+ && !property->isVarProperty()
+ && !property->isVMEFunction() // Handled by QObjectLookup
+ && !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too
+ QV4::Heap::QObjectMethod *method = nullptr;
+ setupQObjectMethodLookup(lookup, ddata, property, This, method);
+ lookup->getter = Lookup::getterQObjectMethod;
+ return lookup->getter(lookup, engine, *object);
}
- lookup->qobjectLookup.ic = This->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = property;
- lookup->getter = QV4::QObjectWrapper::lookupGetter;
+ setupQObjectLookup(lookup, ddata, property, This);
+ lookup->getter = Lookup::getterQObject;
return lookup->getter(lookup, engine, *object);
}
-ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
+ReturnedValue QObjectWrapper::lookupAttached(
+ Lookup *l, ExecutionEngine *engine, const Value &object)
{
- const auto revertLookup = [lookup, engine, &object]() {
- lookup->qobjectLookup.propertyCache->release();
- lookup->qobjectLookup.propertyCache = nullptr;
- lookup->getter = Lookup::getterGeneric;
- return Lookup::getterGeneric(lookup, engine, object);
- };
-
- return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
+ return Lookup::getterGeneric(l, engine, object);
}
bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
@@ -969,20 +1160,60 @@ bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine
return Object::virtualResolveLookupSetter(object, engine, lookup, value);
}
-namespace QV4 {
+int QObjectWrapper::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
+{
+ QObjectWrapper *wrapper = object->as<QObjectWrapper>();
+ Q_ASSERT(wrapper);
+
+ if (QObject *qObject = wrapper->object())
+ return QMetaObject::metacall(qObject, call, index, a);
+
+ return 0;
+}
+
+QString QObjectWrapper::objectToString(
+ ExecutionEngine *engine, const QMetaObject *metaObject, QObject *object)
+{
+ if (!metaObject)
+ return QLatin1String("null");
+
+ if (!object)
+ return QString::fromUtf8(metaObject->className()) + QLatin1String("(0x0)");
+
+ const int id = metaObject->indexOfMethod("toString()");
+ if (id >= 0) {
+ const QMetaMethod method = metaObject->method(id);
+ const QMetaType returnType = method.returnMetaType();
+ QVariant result(returnType);
+ method.invoke(object, QGenericReturnArgument(returnType.name(), result.data()));
+ if (result.metaType() == QMetaType::fromType<QString>())
+ return result.toString();
+ QV4::Scope scope(engine);
+ QV4::ScopedValue value(scope, engine->fromVariant(result));
+ return value->toQString();
+ }
+
+ QString result;
+ result += QString::fromUtf8(metaObject->className()) +
+ QLatin1String("(0x") + QString::number(quintptr(object), 16);
+ QString objectName = object->objectName();
+ if (!objectName.isEmpty())
+ result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
+ result += QLatin1Char(')');
+ return result;
+}
struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
{
- QV4::PersistentValue function;
- QV4::PersistentValue thisObject;
- int signalIndex;
+ PersistentValue function;
+ PersistentValue thisObject;
+ QMetaMethod signal;
QObjectSlotDispatcher()
: QtPrivate::QSlotObjectBase(&impl)
- , signalIndex(-1)
{}
- static void impl(int which, QSlotObjectBase *this_, QObject *r, void **metaArgs, bool *ret)
+ static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
{
switch (which) {
case Destroy: {
@@ -990,30 +1221,33 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
}
break;
case Call: {
+ if (QQmlData::wasDeleted(receiver))
+ break;
+
QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
- QV4::ExecutionEngine *v4 = This->function.engine();
+ ExecutionEngine *v4 = This->function.engine();
// Might be that we're still connected to a signal that's emitted long
// after the engine died. We don't track connections in a global list, so
// we need this safeguard.
if (!v4)
break;
- QQmlMetaObject::ArgTypeStorage storage;
- QQmlMetaObject(r).methodParameterTypes(This->signalIndex, &storage, nullptr);
+ QQmlMetaObject::ArgTypeStorage<9> storage;
+ QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr);
int argCount = storage.size();
- QV4::Scope scope(v4);
- QV4::ScopedFunctionObject f(scope, This->function.value());
+ Scope scope(v4);
+ ScopedFunctionObject f(scope, This->function.value());
- QV4::JSCallData jsCallData(scope, argCount);
- *jsCallData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value();
+ JSCallArguments jsCallData(scope, argCount);
+ *jsCallData.thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value();
for (int ii = 0; ii < argCount; ++ii) {
QMetaType type = storage[ii];
if (type == QMetaType::fromType<QVariant>()) {
- jsCallData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
+ jsCallData.args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
} else {
- jsCallData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
+ jsCallData.args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
}
}
@@ -1021,7 +1255,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
if (scope.hasException()) {
QQmlError error = v4->catchExceptionAsQmlError();
if (error.description().isEmpty()) {
- QV4::ScopedString name(scope, f->name());
+ ScopedString name(scope, f->name());
error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString()));
}
if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
@@ -1044,15 +1278,15 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
// This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
// for the new-style QObject::connect. Here we use the engine pointer as sentinel
// to distinguish those type of QSlotObjectBase connections from our QML connections.
- QV4::ExecutionEngine *v4 = reinterpret_cast<QV4::ExecutionEngine*>(metaArgs[0]);
+ ExecutionEngine *v4 = reinterpret_cast<ExecutionEngine*>(metaArgs[0]);
if (v4 != connection->function.engine()) {
*ret = false;
return;
}
- QV4::Scope scope(v4);
- QV4::ScopedValue function(scope, *reinterpret_cast<QV4::Value*>(metaArgs[1]));
- QV4::ScopedValue thisObject(scope, *reinterpret_cast<QV4::Value*>(metaArgs[2]));
+ Scope scope(v4);
+ ScopedValue function(scope, *reinterpret_cast<Value*>(metaArgs[1]));
+ ScopedValue thisObject(scope, *reinterpret_cast<Value*>(metaArgs[2]));
QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
@@ -1061,7 +1295,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
(connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
- QV4::ScopedFunctionObject f(scope, connection->function.value());
+ ScopedFunctionObject f(scope, connection->function.value());
QPair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f);
if (connectedFunctionData.first == receiverToDisconnect &&
connectedFunctionData.second == slotIndexToDisconnect) {
@@ -1088,11 +1322,9 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
};
};
-} // namespace QV4
-
ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QV4::Scope scope(b);
+ Scope scope(b);
if (argc == 0)
THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
@@ -1107,11 +1339,12 @@ ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Valu
if (!signalObject)
THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
- if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
+ auto signalMetaMethod = signalObject->metaObject()->method(signalIndex);
+ if (signalMetaMethod.methodType() != QMetaMethod::Signal)
THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
- QV4::ScopedFunctionObject f(scope);
- QV4::ScopedValue object (scope, QV4::Encode::undefined());
+ ScopedFunctionObject f(scope);
+ ScopedValue object (scope, Encode::undefined());
if (argc == 1) {
f = argv[0];
@@ -1126,20 +1359,29 @@ ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Valu
if (!object->isUndefined() && !object->isObject())
THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object");
- QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher;
- slot->signalIndex = signalIndex;
+ QObjectSlotDispatcher *slot = new QObjectSlotDispatcher;
+ slot->signal = signalMetaMethod;
slot->thisObject.set(scope.engine, object);
slot->function.set(scope.engine, f);
if (QQmlData *ddata = QQmlData::get(signalObject)) {
- if (QQmlPropertyCache *propertyCache = ddata->propertyCache) {
+ if (const QQmlPropertyCache *propertyCache = ddata->propertyCache.data()) {
QQmlPropertyPrivate::flushSignal(signalObject, propertyCache->methodIndexToSignalIndex(signalIndex));
}
}
QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(f); // align with disconnect
- if (QObject *receiver = functionData.first) {
+ QObject *receiver = nullptr;
+
+ if (functionData.first)
+ receiver = functionData.first;
+ else if (auto qobjectWrapper = object->as<QV4::QObjectWrapper>())
+ receiver = qobjectWrapper->object();
+ else if (auto typeWrapper = object->as<QV4::QQmlTypeWrapper>())
+ receiver = typeWrapper->object();
+
+ if (receiver) {
QObjectPrivate::connect(signalObject, signalIndex, receiver, slot, Qt::AutoConnection);
} else {
qCInfo(lcObjectConnect,
@@ -1153,7 +1395,7 @@ ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Valu
ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QV4::Scope scope(b);
+ Scope scope(b);
if (argc == 0)
THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
@@ -1171,8 +1413,8 @@ ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const V
if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
- QV4::ScopedFunctionObject functionValue(scope);
- QV4::ScopedValue functionThisValue(scope, QV4::Encode::undefined());
+ ScopedFunctionObject functionValue(scope);
+ ScopedValue functionThisValue(scope, Encode::undefined());
if (argc == 1) {
functionValue = argv[0];
@@ -1197,7 +1439,16 @@ ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const V
&functionData.second
};
- if (QObject *receiver = functionData.first) {
+ QObject *receiver = nullptr;
+
+ if (functionData.first)
+ receiver = functionData.first;
+ else if (auto qobjectWrapper = functionThisValue->as<QV4::QObjectWrapper>())
+ receiver = qobjectWrapper->object();
+ else if (auto typeWrapper = functionThisValue->as<QV4::QQmlTypeWrapper>())
+ receiver = typeWrapper->object();
+
+ if (receiver) {
QObjectPrivate::disconnect(signalObject, signalIndex, receiver,
reinterpret_cast<void **>(&a));
} else {
@@ -1208,26 +1459,56 @@ ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const V
RETURN_UNDEFINED();
}
-static void markChildQObjectsRecursively(QObject *parent, QV4::MarkStack *markStack)
+static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack)
{
- const QObjectList &children = parent->children();
- for (int i = 0; i < children.count(); ++i) {
- QObject *child = children.at(i);
+ QQueue<QObject *> queue;
+ queue.append(parent->children());
+
+ while (!queue.isEmpty()) {
+ QObject *child = queue.dequeue();
if (!child)
continue;
QObjectWrapper::markWrapper(child, markStack);
- markChildQObjectsRecursively(child, markStack);
+ queue.append(child->children());
}
}
-void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack)
+void Heap::QObjectWrapper::markObjects(Heap::Base *that, MarkStack *markStack)
{
QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
if (QObject *o = This->object()) {
- QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
- if (vme)
- vme->mark(markStack);
+ if (QQmlData *ddata = QQmlData::get(o)) {
+ if (ddata->hasVMEMetaObject) {
+ if (QQmlVMEMetaObject *vme
+ = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) {
+ vme->mark(markStack);
+ }
+ }
+
+ if (ddata->hasConstWrapper) {
+ Scope scope(that->internalClass->engine);
+ Q_ASSERT(scope.engine->m_multiplyWrappedQObjects);
+
+ Scoped<QV4::QObjectWrapper> constWrapper(
+ scope,
+ scope.engine->m_multiplyWrappedQObjects->value(
+ static_cast<const QObject *>(o)));
+
+ Q_ASSERT(constWrapper);
+
+ if (This == constWrapper->d()) {
+ // We've got the const wrapper. Also mark the non-const one
+ if (ddata->jsEngineId == scope.engine->m_engineId)
+ ddata->jsWrapper.markOnce(markStack);
+ else
+ scope.engine->m_multiplyWrappedQObjects->mark(o, markStack);
+ } else {
+ // We've got the non-const wrapper. Also mark the const one.
+ constWrapper->mark(markStack);
+ }
+ }
+ }
// Children usually don't need to be marked, the gc keeps them alive.
// But in the rare case of a "floating" QObject without a parent that
@@ -1243,38 +1524,45 @@ void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markSta
void QObjectWrapper::destroyObject(bool lastCall)
{
Heap::QObjectWrapper *h = d();
- if (!h->internalClass)
- return; // destroyObject already got called
+ Q_ASSERT(h->internalClass);
- if (h->object()) {
- QQmlData *ddata = QQmlData::get(h->object(), false);
+ if (QObject *o = h->object()) {
+ QQmlData *ddata = QQmlData::get(o, false);
if (ddata) {
- if (!h->object()->parent() && !ddata->indestructible) {
+ if (!o->parent() && !ddata->indestructible) {
if (ddata && ddata->ownContext) {
Q_ASSERT(ddata->ownContext.data() == ddata->context);
- ddata->ownContext->emitDestruction();
- ddata->ownContext = nullptr;
+ ddata->ownContext->deepClearContextObject(o);
+ ddata->ownContext.reset();
ddata->context = nullptr;
}
- // This object is notionally destroyed now
+
+ // This object is notionally destroyed now. It might still live until the next
+ // event loop iteration, but it won't need its connections, CU, or deferredData
+ // anymore.
+
ddata->isQueuedForDeletion = true;
+ ddata->disconnectNotifiers(QQmlData::DeleteNotifyList::No);
+ ddata->compilationUnit.reset();
+
+ qDeleteAll(ddata->deferredData);
+ ddata->deferredData.clear();
+
if (lastCall)
- delete h->object();
+ delete o;
else
- h->object()->deleteLater();
+ o->deleteLater();
} else {
// If the object is C++-owned, we still have to release the weak reference we have
// to it.
ddata->jsWrapper.clear();
- if (lastCall && ddata->propertyCache) {
- ddata->propertyCache->release();
- ddata->propertyCache = nullptr;
- }
+ if (lastCall && ddata->propertyCache)
+ ddata->propertyCache.reset();
}
}
}
- h->~Data();
+ h->destroy();
}
@@ -1293,21 +1581,26 @@ public:
};
struct CallArgument {
- inline CallArgument();
- inline ~CallArgument();
+ Q_DISABLE_COPY_MOVE(CallArgument);
+
+ CallArgument() = default;
+ ~CallArgument() { cleanup(); }
+
inline void *dataPtr();
inline void initAsType(QMetaType type);
- inline bool fromValue(QMetaType type, ExecutionEngine *, const QV4::Value &);
+ inline bool fromValue(QMetaType type, ExecutionEngine *, const Value &);
inline ReturnedValue toValue(ExecutionEngine *);
private:
- CallArgument(const CallArgument &);
+ // QVariantWrappedType denotes that we're storing a QVariant, but we mean
+ // the type inside the QVariant, not QVariant itself.
+ enum { QVariantWrappedType = -1 };
inline void cleanup();
template <class T, class M>
- void fromContainerValue(const QV4::Object *object, int type, M CallArgument::*member, bool &queryEngine);
+ bool fromContainerValue(const Value &object, M CallArgument::*member);
union {
float floatValue;
@@ -1346,12 +1639,12 @@ private:
QJsonValue *jsonValuePtr;
};
- int type;
+ int type = QMetaType::UnknownType;
};
}
-static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMetaType returnType, int argCount,
- const QMetaType *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
+static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMetaType returnType, int argCount,
+ const QMetaType *argTypes, ExecutionEngine *engine, CallData *callArgs,
QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
{
if (argCount > 0) {
@@ -1360,7 +1653,7 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index
args[0].initAsType(returnType);
for (int ii = 0; ii < argCount; ++ii) {
if (!args[ii + 1].fromValue(argTypes[ii], engine,
- callArgs->args[ii].asValue<QV4::Value>())) {
+ callArgs->args[ii].asValue<Value>())) {
qWarning() << QString::fromLatin1("Could not convert argument %1 at").arg(ii);
const StackTrace stack = engine->stackTrace();
for (const StackFrame &frame : stack) {
@@ -1374,7 +1667,7 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index
const bool is_signal =
object.metaObject()->method(index).methodType() == QMetaMethod::Signal;
if (is_signal) {
- qWarning() << "Passing incomatible arguments to signals is not supported.";
+ qWarning() << "Passing incompatible arguments to signals is not supported.";
} else {
return engine->throwTypeError(
QLatin1String("Passing incompatible arguments to C++ functions from "
@@ -1382,8 +1675,8 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index
}
}
}
- QVarLengthArray<void *, 9> argData(args.count());
- for (int ii = 0; ii < args.count(); ++ii)
+ QVarLengthArray<void *, 9> argData(args.size());
+ for (int ii = 0; ii < args.size(); ++ii)
argData[ii] = args[ii].dataPtr();
object.metacall(callType, index, argData.data());
@@ -1410,13 +1703,33 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index
}
}
+template<typename Retrieve>
+int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
+ if (conversionMetaType == QMetaType::fromType<QVariant>())
+ return 0;
+
+ const QMetaType type = retrieve();
+ if (type == conversionMetaType)
+ return 0;
+
+ if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
+ if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject))
+ return 1;
+ }
+
+ if (QMetaType::canConvert(type, conversionMetaType))
+ return 5;
+
+ return 10;
+};
+
/*
Returns the match score for converting \a actual to be of type \a conversionType. A
zero score means "perfect match" whereas a higher score is worse.
The conversion table is copied out of the \l QScript::callQtMethod() function.
*/
-static int MatchScore(const QV4::Value &actual, QMetaType conversionMetaType)
+static int MatchScore(const Value &actual, QMetaType conversionMetaType)
{
const int conversionType = conversionMetaType.id();
if (actual.isNumber()) {
@@ -1452,6 +1765,8 @@ static int MatchScore(const QV4::Value &actual, QMetaType conversionMetaType)
return 0;
case QMetaType::QJsonValue:
return 5;
+ case QMetaType::QUrl:
+ return 6; // we like to convert strings to URLs in QML
default:
return 10;
}
@@ -1475,7 +1790,7 @@ static int MatchScore(const QV4::Value &actual, QMetaType conversionMetaType)
default:
return 10;
}
- } else if (actual.as<QV4::RegExpObject>()) {
+ } else if (actual.as<RegExpObject>()) {
switch (conversionType) {
#if QT_CONFIG(regularexpression)
case QMetaType::QRegularExpression:
@@ -1520,65 +1835,98 @@ static int MatchScore(const QV4::Value &actual, QMetaType conversionMetaType)
return 10;
}
}
- } else if (const QV4::Object *obj = actual.as<QV4::Object>()) {
- if (obj->as<QV4::VariantObject>()) {
- if (conversionType == qMetaTypeId<QVariant>())
- return 0;
- if (obj->engine()->toVariant(actual, -1).metaType() == conversionMetaType)
- return 0;
- else
- return 10;
+ } else if (const Object *obj = actual.as<Object>()) {
+ if (const VariantObject *variantObject = obj->as<VariantObject>()) {
+ return MatchVariant(conversionMetaType, [variantObject]() {
+ return variantObject->d()->data().metaType();
+ });
}
- if (obj->as<QObjectWrapper>()) {
+ if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
switch (conversionType) {
case QMetaType::QObjectStar:
return 0;
default:
- return 10;
+ if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
+ QObject *wrapped = wrapper->object();
+ if (!wrapped)
+ return 0;
+ if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject()))
+ return 0;
+ }
+ }
+ return 10;
+ }
+
+ if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
+ const QQmlType type = wrapper->d()->type();
+ if (type.isSingleton()) {
+ const QMetaType metaType = type.typeId();
+ if (metaType == conversionMetaType)
+ return 0;
+
+ if (conversionMetaType.flags() & QMetaType::PointerToQObject
+ && metaType.flags() & QMetaType::PointerToQObject
+ && type.metaObject()->inherits(conversionMetaType.metaObject())) {
+ return 0;
+ }
+ } else if (QObject *object = wrapper->object()) {
+ if (conversionMetaType.flags() & QMetaType::PointerToQObject
+ && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) {
+ return 0;
+ }
}
+
+ return 10;
}
- if (auto sequenceMetaType = SequencePrototype::metaTypeForSequence(obj); sequenceMetaType != -1) {
- if (sequenceMetaType == conversionType)
+ if (const Sequence *sequence = obj->as<Sequence>()) {
+ if (SequencePrototype::metaTypeForSequence(sequence) == conversionMetaType)
return 1;
else
return 10;
}
- if (obj->as<QV4::QQmlValueTypeWrapper>()) {
- const QVariant v = obj->engine()->toVariant(actual, -1);
- if (v.userType() == conversionType)
- return 0;
- else if (v.canConvert(conversionMetaType))
- return 5;
- return 10;
- } else if (conversionType == QMetaType::QJsonObject) {
- return 5;
- } else if (conversionType == qMetaTypeId<QJSValue>()) {
- return 0;
- } else {
- return 10;
+ if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
+ return MatchVariant(conversionMetaType, [wrapper]() {
+ return wrapper->d()->isVariant()
+ ? wrapper->toVariant().metaType()
+ : wrapper->type();
+ });
}
- } else {
- return 10;
+ if (conversionType == QMetaType::QJsonObject)
+ return 5;
+ if (conversionType == qMetaTypeId<QJSValue>())
+ return 0;
+ if (conversionType == QMetaType::QVariantMap)
+ return 5;
}
+
+ return 10;
}
-static int numDefinedArguments(QV4::CallData *callArgs)
+static int numDefinedArguments(CallData *callArgs)
{
int numDefinedArguments = callArgs->argc();
while (numDefinedArguments > 0
- && callArgs->args[numDefinedArguments - 1].type() == QV4::StaticValue::Undefined_Type) {
+ && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) {
--numDefinedArguments;
}
return numDefinedArguments;
}
-static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
- QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
- QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
+static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
+{
+ const QMetaObject *metaObject = object.metaObject();
+ const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
+ return indexOfClassInfo != -1
+ && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
+}
+
+ReturnedValue QObjectMethod::callPrecise(
+ const QQmlObjectOrGadget &object, const QQmlPropertyData &data, ExecutionEngine *engine,
+ CallData *callArgs, QMetaObject::Call callType)
{
QByteArray unknownTypeError;
@@ -1590,11 +1938,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ
}
auto handleTooManyArguments = [&](int expectedArguments) {
- const QMetaObject *metaObject = object.metaObject();
- const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
- if (indexOfClassInfo != -1
- && QString::fromUtf8(metaObject->classInfo(indexOfClassInfo).value())
- == QStringLiteral("true")) {
+ if (requiresStrictArguments(object)) {
engine->throwError(QStringLiteral("Too many arguments"));
return false;
}
@@ -1605,7 +1949,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ
<< "When matching arguments for "
<< object.className() << "::" << data.name(object.metaObject()) << "():";
} else {
- const StackFrame frame = engine->stackTrace().first();
+ const StackFrame frame = stackTrace.first();
qWarning().noquote() << frame.function + QLatin1Char('@') + frame.source
+ (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
: QString());
@@ -1620,7 +1964,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ
if (data.hasArguments()) {
- QQmlMetaObject::ArgTypeStorage storage;
+ QQmlMetaObject::ArgTypeStorage<9> storage;
bool ok = false;
if (data.isConstructor())
@@ -1664,451 +2008,777 @@ Resolve the overloaded method to call. The algorithm works conceptually like th
For example, if we are called with 3 parameters and there are 2 overloads that
take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
3. Find the best remaining overload based on its match score.
- If two or more overloads have the same match score, call the last one. The match
+ If two or more overloads have the same match score, return the last one. The match
score is constructed by adding the matchScore() result for each of the parameters.
*/
-static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount,
- QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
- QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
+const QQmlPropertyData *QObjectMethod::resolveOverloaded(
+ const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount,
+ ExecutionEngine *engine, CallData *callArgs)
{
const int argumentCount = callArgs->argc();
const int definedArgumentCount = numDefinedArguments(callArgs);
- QQmlPropertyData best;
+ const QQmlPropertyData *best = nullptr;
int bestParameterScore = INT_MAX;
- int bestMatchScore = INT_MAX;
+ int bestMaxMatchScore = INT_MAX;
+ int bestSumMatchScore = INT_MAX;
- QV4::Scope scope(engine);
- QV4::ScopedValue v(scope);
+ Scope scope(engine);
+ ScopedValue v(scope);
for (int i = 0; i < methodCount; ++i) {
- const QQmlPropertyData &attempt = methods[i];
- QQmlMetaObject::ArgTypeStorage storage;
- int methodArgumentCount = 0;
- if (attempt.hasArguments()) {
- if (attempt.isConstructor()) {
- if (!object.constructorParameterTypes(attempt.coreIndex(), &storage, nullptr))
- continue;
- } else {
- if (!object.methodParameterTypes(attempt.coreIndex(), &storage, nullptr))
- continue;
- }
- methodArgumentCount = storage.size();
+ const QQmlPropertyData *attempt = methods + i;
+
+ if (lcOverloadResolution().isDebugEnabled()) {
+ const QQmlPropertyData &candidate = methods[i];
+ const QMetaMethod m = candidate.isConstructor()
+ ? object.metaObject()->constructor(candidate.coreIndex())
+ : object.metaObject()->method(candidate.coreIndex());
+ qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
}
- if (methodArgumentCount > argumentCount)
- continue; // We don't have sufficient arguments to call this method
+ // QQmlV4Function overrides anything that doesn't provide the exact number of arguments
+ int methodParameterScore = 1;
+ // QQmlV4Function overrides the "no idea" option, which is 10
+ int maxMethodMatchScore = 9;
+ // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments
+ int sumMethodMatchScore = bestSumMatchScore;
+
+ if (!attempt->isV4Function()) {
+ QQmlMetaObject::ArgTypeStorage<9> storage;
+ int methodArgumentCount = 0;
+ if (attempt->hasArguments()) {
+ if (attempt->isConstructor()) {
+ if (!object.constructorParameterTypes(attempt->coreIndex(), &storage, nullptr)) {
+ qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
+ continue;
+ }
+ } else {
+ if (!object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr)) {
+ qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
+ continue;
+ }
+ }
+ methodArgumentCount = storage.size();
+ }
+
+ if (methodArgumentCount > argumentCount) {
+ qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
+ continue; // We don't have sufficient arguments to call this method
+ }
- int methodParameterScore = definedArgumentCount - methodArgumentCount;
- if (methodParameterScore > bestParameterScore)
- continue; // We already have a better option
+ methodParameterScore = (definedArgumentCount == methodArgumentCount)
+ ? 0
+ : (definedArgumentCount - methodArgumentCount + 1);
+ if (methodParameterScore > bestParameterScore) {
+ qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
+ continue; // We already have a better option
+ }
- int methodMatchScore = 0;
- for (int ii = 0; ii < methodArgumentCount; ++ii) {
- methodMatchScore += MatchScore((v = QV4::Value::fromStaticValue(callArgs->args[ii])),
- storage[ii]);
+ maxMethodMatchScore = 0;
+ sumMethodMatchScore = 0;
+ for (int ii = 0; ii < methodArgumentCount; ++ii) {
+ const int score = MatchScore((v = Value::fromStaticValue(callArgs->args[ii])),
+ storage[ii]);
+ maxMethodMatchScore = qMax(maxMethodMatchScore, score);
+ sumMethodMatchScore += score;
+ }
}
- if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
+ if (bestParameterScore > methodParameterScore || bestMaxMatchScore > maxMethodMatchScore
+ || (bestParameterScore == methodParameterScore
+ && bestMaxMatchScore == maxMethodMatchScore
+ && bestSumMatchScore > sumMethodMatchScore)) {
best = attempt;
bestParameterScore = methodParameterScore;
- bestMatchScore = methodMatchScore;
+ bestMaxMatchScore = maxMethodMatchScore;
+ bestSumMatchScore = sumMethodMatchScore;
+ qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
+ << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
+ << "bestSumMatchScore" << bestSumMatchScore << "\n";
+ } else {
+ qCDebug(lcOverloadResolution) << "did not update best\n"
+ << "bestParameterScore" << bestParameterScore << "\t"
+ << "methodParameterScore" << methodParameterScore << "\n"
+ << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
+ << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
+ << "bestSumMatchScore" << bestSumMatchScore << "\t"
+ << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
}
- if (bestParameterScore == 0 && bestMatchScore == 0)
+ if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
+ qCDebug(lcOverloadResolution, "perfect match");
break; // We can't get better than that
+ }
};
- if (best.isValid()) {
- return CallPrecise(object, best, engine, callArgs, callType);
+ if (best && best->isValid()) {
+ return best;
} else {
QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
for (int i = 0; i < methodCount; ++i) {
- for (int i = 0; i < methodCount; ++i) {
- const QQmlPropertyData &candidate = methods[i];
- const QMetaMethod m = candidate.isConstructor()
- ? object.metaObject()->constructor(candidate.coreIndex())
- : object.metaObject()->method(candidate.coreIndex());
- error += u"\n " + QString::fromUtf8(m.methodSignature());
- }
+ const QQmlPropertyData &candidate = methods[i];
+ const QMetaMethod m = candidate.isConstructor()
+ ? object.metaObject()->constructor(candidate.coreIndex())
+ : object.metaObject()->method(candidate.coreIndex());
+ error += u"\n " + QString::fromUtf8(m.methodSignature());
}
- return engine->throwError(error);
+ engine->throwError(error);
+ return nullptr;
}
}
-CallArgument::CallArgument()
-: type(QMetaType::UnknownType)
+static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
{
+ if (required == QMetaType::fromType<QVariant>()
+ || required == QMetaType::fromType<QJSValue>()
+ || required == QMetaType::fromType<QJSManagedValue>()) {
+ return true;
+ }
+
+ if (data) {
+ if (passed == QMetaType::fromType<QVariant>())
+ passed = static_cast<const QVariant *>(data)->metaType();
+ else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
+ passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
+ }
+
+ if (passed == required)
+ return true;
+
+ if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
+ switch (passed.id()) {
+ case QMetaType::UnknownType:
+ case QMetaType::Nullptr:
+ case QMetaType::Bool:
+ case QMetaType::Int:
+ case QMetaType::Double:
+ case QMetaType::QString:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
}
-CallArgument::~CallArgument()
+const QQmlPropertyData *QObjectMethod::resolveOverloaded(
+ const QQmlPropertyData *methods, int methodCount,
+ void **argv, int argc, const QMetaType *types)
{
- cleanup();
+ // We only accept exact matches here. Everything else goes through the JavaScript conversion.
+ for (int i = 0; i < methodCount; ++i) {
+ const QQmlPropertyData *attempt = methods + i;
+ if (types[0].isValid() && !ExactMatch(attempt->propType(), types[0], nullptr))
+ continue;
+
+ const QMetaMethod method = attempt->metaMethod();
+ if (method.parameterCount() != argc)
+ continue;
+
+ bool valid = true;
+ for (int i = 0; i < argc; ++i) {
+ if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1])) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid)
+ return attempt;
+ }
+
+ return nullptr;
}
void CallArgument::cleanup()
{
- if (type == QMetaType::QString) {
+ switch (type) {
+ case QMetaType::QString:
qstringPtr->~QString();
- } else if (type == QMetaType::QByteArray) {
+ break;
+ case QMetaType::QByteArray:
qbyteArrayPtr->~QByteArray();
- } else if (type == -1 || type == QMetaType::QVariant) {
+ break;
+ case QMetaType::QVariant:
+ case QVariantWrappedType:
qvariantPtr->~QVariant();
- } else if (type == qMetaTypeId<QJSValue>()) {
- qjsValuePtr->~QJSValue();
- } else if (type == qMetaTypeId<QList<QObject *> >()) {
- qlistPtr->~QList<QObject *>();
- } else if (type == QMetaType::QJsonArray) {
+ break;
+ case QMetaType::QJsonArray:
jsonArrayPtr->~QJsonArray();
- } else if (type == QMetaType::QJsonObject) {
+ break;
+ case QMetaType::QJsonObject:
jsonObjectPtr->~QJsonObject();
- } else if (type == QMetaType::QJsonValue) {
+ break;
+ case QMetaType::QJsonValue:
jsonValuePtr->~QJsonValue();
+ break;
+ default:
+ if (type == qMetaTypeId<QJSValue>()) {
+ qjsValuePtr->~QJSValue();
+ break;
+ }
+
+ if (type == qMetaTypeId<QList<QObject *> >()) {
+ qlistPtr->~QList<QObject *>();
+ break;
+ }
+
+ // The sequence types need no cleanup because we don't own them.
+
+ break;
}
}
void *CallArgument::dataPtr()
{
- if (type == -1)
+ switch (type) {
+ case QMetaType::UnknownType:
+ return nullptr;
+ case QVariantWrappedType:
return qvariantPtr->data();
- else if (type == qMetaTypeId<std::vector<int>>())
- return stdVectorIntPtr;
- else if (type == qMetaTypeId<std::vector<qreal>>())
- return stdVectorRealPtr;
- else if (type == qMetaTypeId<std::vector<bool>>())
- return stdVectorBoolPtr;
- else if (type == qMetaTypeId<std::vector<QString>>())
- return stdVectorQStringPtr;
- else if (type == qMetaTypeId<std::vector<QUrl>>())
- return stdVectorQUrlPtr;
+ default:
+ if (type == qMetaTypeId<std::vector<int>>())
+ return stdVectorIntPtr;
+ if (type == qMetaTypeId<std::vector<qreal>>())
+ return stdVectorRealPtr;
+ if (type == qMetaTypeId<std::vector<bool>>())
+ return stdVectorBoolPtr;
+ if (type == qMetaTypeId<std::vector<QString>>())
+ return stdVectorQStringPtr;
+ if (type == qMetaTypeId<std::vector<QUrl>>())
+ return stdVectorQUrlPtr;
#if QT_CONFIG(qml_itemmodel)
- else if (type == qMetaTypeId<std::vector<QModelIndex>>())
- return stdVectorQModelIndexPtr;
+ if (type == qMetaTypeId<std::vector<QModelIndex>>())
+ return stdVectorQModelIndexPtr;
#endif
- else if (type != 0)
- return (void *)&allocData;
- return nullptr;
+ break;
+ }
+
+ return (void *)&allocData;
}
void CallArgument::initAsType(QMetaType metaType)
{
- if (type != 0) { cleanup(); type = 0; }
- const int callType = metaType.id();
- if (callType == QMetaType::UnknownType || callType == QMetaType::Void) return;
-
- if (callType == qMetaTypeId<QJSValue>()) {
- qjsValuePtr = new (&allocData) QJSValue();
- type = callType;
- } else if (callType == QMetaType::Int ||
- callType == QMetaType::UInt ||
- callType == QMetaType::Bool ||
- callType == QMetaType::Double ||
- callType == QMetaType::Float) {
- type = callType;
- } else if (callType == QMetaType::QObjectStar) {
+ if (type != QMetaType::UnknownType)
+ cleanup();
+
+ type = metaType.id();
+ switch (type) {
+ case QMetaType::Void:
+ type = QMetaType::UnknownType;
+ break;
+ case QMetaType::UnknownType:
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ case QMetaType::Bool:
+ case QMetaType::Double:
+ case QMetaType::Float:
+ break;
+ case QMetaType::QObjectStar:
qobjectPtr = nullptr;
- type = callType;
- } else if (callType == QMetaType::QString) {
+ break;
+ case QMetaType::QString:
qstringPtr = new (&allocData) QString();
- type = callType;
- } else if (callType == QMetaType::QVariant) {
- type = callType;
+ break;
+ case QMetaType::QVariant:
qvariantPtr = new (&allocData) QVariant();
- } else if (callType == qMetaTypeId<QList<QObject *> >()) {
- type = callType;
- qlistPtr = new (&allocData) QList<QObject *>();
- } else if (callType == QMetaType::QJsonArray) {
- type = callType;
+ break;
+ case QMetaType::QJsonArray:
jsonArrayPtr = new (&allocData) QJsonArray();
- } else if (callType == QMetaType::QJsonObject) {
- type = callType;
+ break;
+ case QMetaType::QJsonObject:
jsonObjectPtr = new (&allocData) QJsonObject();
- } else if (callType == QMetaType::QJsonValue) {
- type = callType;
+ break;
+ case QMetaType::QJsonValue:
jsonValuePtr = new (&allocData) QJsonValue();
- } else {
- type = -1;
+ break;
+ default: {
+ if (metaType == QMetaType::fromType<QJSValue>()) {
+ qjsValuePtr = new (&allocData) QJSValue();
+ break;
+ }
+
+ if (metaType == QMetaType::fromType<QList<QObject *>>()) {
+ qlistPtr = new (&allocData) QList<QObject *>();
+ break;
+ }
+
+ type = QVariantWrappedType;
qvariantPtr = new (&allocData) QVariant(metaType, (void *)nullptr);
+ break;
+ }
}
}
-#if QT_CONFIG(qml_sequence_object)
template <class T, class M>
-void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine)
+bool CallArgument::fromContainerValue(const Value &value, M CallArgument::*member)
{
- if (object && object->isListType()) {
- T* ptr = static_cast<T*>(QV4::SequencePrototype::getRawContainerPtr(object, callType));
- if (ptr) {
- (this->*member) = ptr;
- type = callType;
- queryEngine = false;
+ if (const Sequence *sequence = value.as<Sequence>()) {
+ if (T* ptr = static_cast<T *>(SequencePrototype::getRawContainerPtr(
+ sequence, QMetaType(type)))) {
+ (this->*member) = ptr;
+ return true;
+ }
}
- }
+ (this->*member) = nullptr;
+ return false;
}
-#endif
-bool CallArgument::fromValue(QMetaType metaType, QV4::ExecutionEngine *engine, const QV4::Value &value)
+bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const Value &value)
{
- if (type != 0) {
+ if (type != QMetaType::UnknownType)
cleanup();
- type = 0;
- }
- QV4::Scope scope(engine);
+ type = metaType.id();
- const int callType = metaType.id();
- bool queryEngine = false;
- if (callType == qMetaTypeId<QJSValue>()) {
- qjsValuePtr = new (&allocData) QJSValue;
- QJSValuePrivate::setValue(qjsValuePtr, value.asReturnedValue());
- type = qMetaTypeId<QJSValue>();
- } else if (callType == QMetaType::Int) {
+ switch (type) {
+ case QMetaType::Int:
intValue = quint32(value.toInt32());
- type = callType;
- } else if (callType == QMetaType::UInt) {
+ return true;
+ case QMetaType::UInt:
intValue = quint32(value.toUInt32());
- type = callType;
- } else if (callType == QMetaType::Bool) {
+ return true;
+ case QMetaType::Bool:
boolValue = value.toBoolean();
- type = callType;
- } else if (callType == QMetaType::Double) {
+ return true;
+ case QMetaType::Double:
doubleValue = double(value.toNumber());
- type = callType;
- } else if (callType == QMetaType::Float) {
+ return true;
+ case QMetaType::Float:
floatValue = float(value.toNumber());
- type = callType;
- } else if (callType == QMetaType::QString) {
- if (value.isNull() || value.isUndefined())
+ return true;
+ case QMetaType::QString:
+ if (value.isNullOrUndefined())
qstringPtr = new (&allocData) QString();
else
qstringPtr = new (&allocData) QString(value.toQStringNoThrow());
- type = callType;
- } else if (callType == QMetaType::QObjectStar) {
- qobjectPtr = nullptr;
- type = callType;
- if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>())
+ return true;
+ case QMetaType::QByteArray:
+ qbyteArrayPtr = new (&allocData) QByteArray();
+ ExecutionEngine::metaTypeFromJS(value, metaType, qbyteArrayPtr);
+ return true;
+ case QMetaType::QObjectStar:
+ if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) {
qobjectPtr = qobjectWrapper->object();
- else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>())
- queryEngine = qmlTypeWrapper->isSingleton();
- else if (!value.isNull() && !value.isUndefined()) // null and undefined are nullptr
- return false;
- } else if (callType == qMetaTypeId<QVariant>()) {
- qvariantPtr = new (&allocData) QVariant(scope.engine->toVariant(value, -1));
- type = callType;
- } else if (callType == qMetaTypeId<QList<QObject*> >()) {
- qlistPtr = new (&allocData) QList<QObject *>();
- type = callType;
- QV4::ScopedArrayObject array(scope, value);
- if (array) {
- Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
-
- uint length = array->getLength();
- for (uint ii = 0; ii < length; ++ii) {
- QObject *o = nullptr;
- qobjectWrapper = array->get(ii);
- if (!!qobjectWrapper)
- o = qobjectWrapper->object();
- qlistPtr->append(o);
- }
- } else {
- if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) {
- qlistPtr->append(qobjectWrapper->object());
- } else {
- qlistPtr->append(nullptr);
- if (!value.isNull() && !value.isUndefined())
- return false;
+ return true;
+ }
+
+ if (const QQmlTypeWrapper *qmlTypeWrapper = value.as<QQmlTypeWrapper>()) {
+ if (qmlTypeWrapper->isSingleton()) {
+ // Convert via QVariant below.
+ // TODO: Can't we just do qobjectPtr = qmlTypeWrapper->object() instead?
+ break;
+ } else if (QObject *obj = qmlTypeWrapper->object()) {
+ // attached object case
+ qobjectPtr = obj;
+ return true;
}
+
+ // If this is a plain type wrapper without an instance,
+ // then we got a namespace, and that's a type error
+ type = QMetaType::UnknownType;
+ return false;
}
- } else if (callType == QMetaType::QJsonArray) {
- QV4::ScopedArrayObject a(scope, value);
- jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(a));
- type = callType;
- } else if (callType == QMetaType::QJsonObject) {
- QV4::ScopedObject o(scope, value);
- jsonObjectPtr = new (&allocData) QJsonObject(QV4::JsonObject::toJsonObject(o));
- type = callType;
- } else if (callType == QMetaType::QJsonValue) {
- jsonValuePtr = new (&allocData) QJsonValue(QV4::JsonObject::toJsonValue(value));
- type = callType;
- } else if (callType == QMetaType::Void) {
+
+ qobjectPtr = nullptr;
+ return value.isNullOrUndefined(); // null and undefined are nullptr
+ case QMetaType::QVariant:
+ qvariantPtr = new (&allocData) QVariant(ExecutionEngine::toVariant(value, QMetaType {}));
+ return true;
+ case QMetaType::QJsonArray: {
+ Scope scope(engine);
+ ScopedObject o(scope, value);
+ jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(o));
+ return true;
+ }
+ case QMetaType::QJsonObject: {
+ Scope scope(engine);
+ ScopedObject o(scope, value);
+ jsonObjectPtr = new (&allocData) QJsonObject(JsonObject::toJsonObject(o));
+ return true;
+ }
+ case QMetaType::QJsonValue:
+ jsonValuePtr = new (&allocData) QJsonValue(JsonObject::toJsonValue(value));
+ return true;
+ case QMetaType::Void:
+ type = QMetaType::UnknownType;
+ // TODO: This only doesn't leak because a default constructed QVariant doesn't allocate.
*qvariantPtr = QVariant();
-#if QT_CONFIG(qml_sequence_object)
- } else if (callType == qMetaTypeId<std::vector<int>>()
- || callType == qMetaTypeId<std::vector<qreal>>()
- || callType == qMetaTypeId<std::vector<bool>>()
- || callType == qMetaTypeId<std::vector<QString>>()
- || callType == qMetaTypeId<std::vector<QUrl>>()
-#if QT_CONFIG(qml_itemmodel)
- || callType == qMetaTypeId<std::vector<QModelIndex>>()
-#endif
- ) {
- queryEngine = true;
- const QV4::Object* object = value.as<QV4::Object>();
- if (callType == qMetaTypeId<std::vector<int>>()) {
- stdVectorIntPtr = nullptr;
- fromContainerValue<std::vector<int>>(object, callType, &CallArgument::stdVectorIntPtr, queryEngine);
- } else if (callType == qMetaTypeId<std::vector<qreal>>()) {
- stdVectorRealPtr = nullptr;
- fromContainerValue<std::vector<qreal>>(object, callType, &CallArgument::stdVectorRealPtr, queryEngine);
- } else if (callType == qMetaTypeId<std::vector<bool>>()) {
- stdVectorBoolPtr = nullptr;
- fromContainerValue<std::vector<bool>>(object, callType, &CallArgument::stdVectorBoolPtr, queryEngine);
- } else if (callType == qMetaTypeId<std::vector<QString>>()) {
- stdVectorQStringPtr = nullptr;
- fromContainerValue<std::vector<QString>>(object, callType, &CallArgument::stdVectorQStringPtr, queryEngine);
- } else if (callType == qMetaTypeId<std::vector<QUrl>>()) {
- stdVectorQUrlPtr = nullptr;
- fromContainerValue<std::vector<QUrl>>(object, callType, &CallArgument::stdVectorQUrlPtr, queryEngine);
-#if QT_CONFIG(qml_itemmodel)
- } else if (callType == qMetaTypeId<std::vector<QModelIndex>>()) {
- stdVectorQModelIndexPtr = nullptr;
- fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine);
-#endif
- }
-#endif
- } else if (metaType.flags()
- & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) {
- // You can assign null or undefined to any pointer. The result is a nullptr.
- if (value.isNull() || value.isUndefined()) {
- qvariantPtr = new (&allocData) QVariant(metaType, nullptr);
- type = callType;
- } else {
- queryEngine = true;
+ return true;
+ default:
+ if (type == qMetaTypeId<QJSValue>()) {
+ qjsValuePtr = new (&allocData) QJSValue;
+ QJSValuePrivate::setValue(qjsValuePtr, value.asReturnedValue());
+ return true;
}
- } else {
- queryEngine = true;
- }
- if (queryEngine) {
- qvariantPtr = new (&allocData) QVariant();
- type = -1;
+ if (type == qMetaTypeId<QList<QObject*> >()) {
+ qlistPtr = new (&allocData) QList<QObject *>();
+ Scope scope(engine);
+ ScopedArrayObject array(scope, value);
+ if (array) {
+ Scoped<QObjectWrapper> qobjectWrapper(scope);
+
+ uint length = array->getLength();
+ for (uint ii = 0; ii < length; ++ii) {
+ QObject *o = nullptr;
+ qobjectWrapper = array->get(ii);
+ if (!!qobjectWrapper)
+ o = qobjectWrapper->object();
+ qlistPtr->append(o);
+ }
+ return true;
+ }
- QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
- QVariant v = scope.engine->toVariant(value, callType);
+ if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) {
+ qlistPtr->append(qobjectWrapper->object());
+ return true;
+ }
- const QMetaType callMetaType(callType);
- if (v.metaType() == callMetaType) {
- *qvariantPtr = v;
- } else if (v.canConvert(callMetaType)) {
- *qvariantPtr = v;
- qvariantPtr->convert(callMetaType);
- } else {
- QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
- if (!mo.isNull()) {
- QObject *obj = QQmlMetaType::toQObject(v);
+ if (const QmlListWrapper *listWrapper = value.as<QmlListWrapper>()) {
+ *qlistPtr = listWrapper->d()->property()->toList<QList<QObject *>>();
+ return true;
+ }
- if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) {
- *qvariantPtr = QVariant(metaType, nullptr);
- return false;
- }
+ qlistPtr->append(nullptr);
+ return value.isNullOrUndefined();
+ }
- *qvariantPtr = QVariant(metaType, &obj);
+ if (metaType.flags() & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) {
+ // You can assign null or undefined to any pointer. The result is a nullptr.
+ if (value.isNullOrUndefined()) {
+ qvariantPtr = new (&allocData) QVariant(metaType, nullptr);
return true;
}
+ break;
+ }
- *qvariantPtr = QVariant(metaType, (void *)nullptr);
- return false;
+ if (type == qMetaTypeId<std::vector<int>>()) {
+ if (fromContainerValue<std::vector<int>>(value, &CallArgument::stdVectorIntPtr))
+ return true;
+ } else if (type == qMetaTypeId<std::vector<qreal>>()) {
+ if (fromContainerValue<std::vector<qreal>>(value, &CallArgument::stdVectorRealPtr))
+ return true;
+ } else if (type == qMetaTypeId<std::vector<bool>>()) {
+ if (fromContainerValue<std::vector<bool>>(value, &CallArgument::stdVectorBoolPtr))
+ return true;
+ } else if (type == qMetaTypeId<std::vector<QString>>()) {
+ if (fromContainerValue<std::vector<QString>>(value, &CallArgument::stdVectorQStringPtr))
+ return true;
+ } else if (type == qMetaTypeId<std::vector<QUrl>>()) {
+ if (fromContainerValue<std::vector<QUrl>>(value, &CallArgument::stdVectorQUrlPtr))
+ return true;
+#if QT_CONFIG(qml_itemmodel)
+ } else if (type == qMetaTypeId<std::vector<QModelIndex>>()) {
+ if (fromContainerValue<std::vector<QModelIndex>>(
+ value, &CallArgument::stdVectorQModelIndexPtr)) {
+ return true;
+ }
+#endif
}
+ break;
}
- return true;
-}
-QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
-{
- QV4::Scope scope(engine);
+ // Convert via QVariant through the QML engine.
+ qvariantPtr = new (&allocData) QVariant(metaType);
+ type = QVariantWrappedType;
+
+ if (ExecutionEngine::metaTypeFromJS(value, metaType, qvariantPtr->data()))
+ return true;
+
+ const QVariant v = ExecutionEngine::toVariant(value, metaType);
+ return QMetaType::convert(v.metaType(), v.constData(), metaType, qvariantPtr->data());
+}
+
+ReturnedValue CallArgument::toValue(ExecutionEngine *engine)
+{
+ switch (type) {
+ case QMetaType::Int:
+ return Encode(int(intValue));
+ case QMetaType::UInt:
+ return Encode((uint)intValue);
+ case QMetaType::Bool:
+ return Encode(boolValue);
+ case QMetaType::Double:
+ return Encode(doubleValue);
+ case QMetaType::Float:
+ return Encode(floatValue);
+ case QMetaType::QString:
+ return Encode(engine->newString(*qstringPtr));
+ case QMetaType::QByteArray:
+ return Encode(engine->newArrayBuffer(*qbyteArrayPtr));
+ case QMetaType::QObjectStar:
+ if (qobjectPtr)
+ QQmlData::get(qobjectPtr, true)->setImplicitDestructible();
+ return QObjectWrapper::wrap(engine, qobjectPtr);
+ case QMetaType::QJsonArray:
+ return JsonObject::fromJsonArray(engine, *jsonArrayPtr);
+ case QMetaType::QJsonObject:
+ return JsonObject::fromJsonObject(engine, *jsonObjectPtr);
+ case QMetaType::QJsonValue:
+ return JsonObject::fromJsonValue(engine, *jsonValuePtr);
+ case QMetaType::QVariant:
+ case QVariantWrappedType: {
+ Scope scope(engine);
+ ScopedValue rv(scope, scope.engine->fromVariant(*qvariantPtr));
+ Scoped<QObjectWrapper> qobjectWrapper(scope, rv);
+ if (!!qobjectWrapper) {
+ if (QObject *object = qobjectWrapper->object())
+ QQmlData::get(object, true)->setImplicitDestructible();
+ }
+ return rv->asReturnedValue();
+ }
+ default:
+ break;
+ }
if (type == qMetaTypeId<QJSValue>()) {
// The QJSValue can be passed around via dataPtr()
QJSValuePrivate::manageStringOnV4Heap(engine, qjsValuePtr);
return QJSValuePrivate::asReturnedValue(qjsValuePtr);
- } else if (type == QMetaType::Int) {
- return QV4::Encode(int(intValue));
- } else if (type == QMetaType::UInt) {
- return QV4::Encode((uint)intValue);
- } else if (type == QMetaType::Bool) {
- return QV4::Encode(boolValue);
- } else if (type == QMetaType::Double) {
- return QV4::Encode(doubleValue);
- } else if (type == QMetaType::Float) {
- return QV4::Encode(floatValue);
- } else if (type == QMetaType::QString) {
- return QV4::Encode(engine->newString(*qstringPtr));
- } else if (type == QMetaType::QByteArray) {
- return QV4::Encode(engine->newArrayBuffer(*qbyteArrayPtr));
- } else if (type == QMetaType::QObjectStar) {
- QObject *object = qobjectPtr;
- if (object)
- QQmlData::get(object, true)->setImplicitDestructible();
- return QV4::QObjectWrapper::wrap(scope.engine, object);
- } else if (type == qMetaTypeId<QList<QObject *> >()) {
+ }
+
+ if (type == qMetaTypeId<QList<QObject *> >()) {
// XXX Can this be made more by using Array as a prototype and implementing
// directly against QList<QObject*>?
QList<QObject *> &list = *qlistPtr;
- QV4::ScopedArrayObject array(scope, scope.engine->newArrayObject());
- array->arrayReserve(list.count());
- QV4::ScopedValue v(scope);
- for (int ii = 0; ii < list.count(); ++ii)
- array->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(scope.engine, list.at(ii))));
- array->setArrayLengthUnchecked(list.count());
+ Scope scope(engine);
+ ScopedArrayObject array(scope, engine->newArrayObject());
+ array->arrayReserve(list.size());
+ ScopedValue v(scope);
+ for (int ii = 0; ii < list.size(); ++ii)
+ array->arrayPut(ii, (v = QObjectWrapper::wrap(engine, list.at(ii))));
+ array->setArrayLengthUnchecked(list.size());
return array.asReturnedValue();
- } else if (type == QMetaType::QJsonArray) {
- return QV4::JsonObject::fromJsonArray(scope.engine, *jsonArrayPtr);
- } else if (type == QMetaType::QJsonObject) {
- return QV4::JsonObject::fromJsonObject(scope.engine, *jsonObjectPtr);
- } else if (type == QMetaType::QJsonValue) {
- return QV4::JsonObject::fromJsonValue(scope.engine, *jsonValuePtr);
- } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
- QVariant value = *qvariantPtr;
- QV4::ScopedValue rv(scope, scope.engine->fromVariant(value));
- QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, rv);
- if (!!qobjectWrapper) {
- if (QObject *object = qobjectWrapper->object())
- QQmlData::get(object, true)->setImplicitDestructible();
- }
- return rv->asReturnedValue();
- } else {
- return QV4::Encode::undefined();
}
+
+ return Encode::undefined();
}
-ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
+ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::Object *wrapper, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->setObject(object);
-
- method->d()->index = index;
+ Scoped<QObjectMethod> method(
+ valueScope,
+ valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, wrapper, index));
return method.asReturnedValue();
}
ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->index = index;
- method->d()->valueTypeWrapper.set(valueScope.engine, valueType);
+ Scoped<QObjectMethod> method(
+ valueScope,
+ valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, valueType, index));
+ return method.asReturnedValue();
+}
+
+ReturnedValue QObjectMethod::create(
+ ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
+ Heap::Object *wrapper, Heap::Object *object)
+{
+ Scope valueScope(engine);
+
+ Scoped<QQmlValueTypeWrapper> valueTypeWrapper(valueScope);
+ if (cloneFrom->wrapper) {
+ Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->wrapper);
+ if (ref) {
+ valueTypeWrapper = QQmlValueTypeWrapper::create(engine, ref->d(), wrapper);
+ } else {
+ // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what
+ // value we should operate on. Without knowledge of the property the value
+ // was read from, we cannot load the value from the given object.
+ return Encode::undefined();
+ }
+ }
+
+ Scoped<ExecutionContext> context(valueScope, cloneFrom->scope.get());
+ Scoped<QObjectMethod> method(
+ valueScope,
+ engine->memoryManager->allocate<QV4::QObjectMethod>(
+ context, valueTypeWrapper ? valueTypeWrapper->d() : object, cloneFrom->index));
+
+ method->d()->methodCount = cloneFrom->methodCount;
+
+ Q_ASSERT(method->d()->methods == nullptr);
+ switch (cloneFrom->methodCount) {
+ case 0:
+ Q_ASSERT(cloneFrom->methods == nullptr);
+ break;
+ case 1:
+ Q_ASSERT(cloneFrom->methods
+ == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
+ method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
+ *method->d()->methods = *cloneFrom->methods;
+ break;
+ default:
+ Q_ASSERT(cloneFrom->methods != nullptr);
+ method->d()->methods = new QQmlPropertyData[cloneFrom->methodCount];
+ memcpy(method->d()->methods, cloneFrom->methods,
+ cloneFrom->methodCount * sizeof(QQmlPropertyData));
+ break;
+ }
+
return method.asReturnedValue();
}
-void Heap::QObjectMethod::init(QV4::ExecutionContext *scope)
+void Heap::QObjectMethod::init(QV4::ExecutionContext *scope, Object *object, int methodIndex)
{
Heap::FunctionObject::init(scope);
+ wrapper.set(internalClass->engine, object);
+ index = methodIndex;
+}
+
+const QMetaObject *Heap::QObjectMethod::metaObject() const
+{
+ Scope scope(internalClass->engine);
+
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
+ return valueWrapper->metaObject();
+ if (QObject *self = object())
+ return self->metaObject();
+
+ return nullptr;
+}
+
+QObject *Heap::QObjectMethod::object() const
+{
+ Scope scope(internalClass->engine);
+
+ if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
+ return objectWrapper->object();
+ if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
+ return typeWrapper->object();
+ return nullptr;
+}
+
+bool Heap::QObjectMethod::isDetached() const
+{
+ if (!wrapper)
+ return true;
+
+ QV4::Scope scope(internalClass->engine);
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
+ return valueWrapper->d()->object() == nullptr;
+
+ return false;
+}
+
+bool Heap::QObjectMethod::isAttachedTo(QObject *o) const
+{
+ QV4::Scope scope(internalClass->engine);
+ if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
+ return objectWrapper->object() == o;
+ if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
+ return typeWrapper->object() == o;
+
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper) {
+ QV4::Scope scope(wrapper->internalClass->engine);
+ if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, valueWrapper->d()->object()); qobject)
+ return qobject->object() == o;
+ if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, valueWrapper->d()->object()); type)
+ return type->object() == o;
+
+ // Attached to some nested value type or sequence object
+ return false;
+ }
+
+ return false;
+}
+
+Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
+ const QMetaObject *thisMeta) const
+{
+ // Check that the metaobject matches.
+
+ if (!thisMeta) {
+ // You can only get a detached method via a lookup, and then you have a thisObject.
+ Q_ASSERT(wrapper);
+ return Included;
+ }
+
+ const auto check = [&](const QMetaObject *included) {
+ const auto stackFrame = internalClass->engine->currentStackFrame;
+ if (stackFrame && !stackFrame->v4Function->executableCompilationUnit()
+ ->nativeMethodsAcceptThisObjects()) {
+ qCWarning(lcMethodBehavior,
+ "%s:%d: Calling C++ methods with 'this' objects different from the one "
+ "they were retrieved from is broken, due to historical reasons. The "
+ "original object is used as 'this' object. You can allow the given "
+ "'this' object to be used by setting "
+ "'pragma NativeMethodBehavior: AcceptThisObject'",
+ qPrintable(stackFrame->source()), stackFrame->lineNumber());
+ return Included;
+ }
+
+ // destroy() and toString() can be called on all QObjects, but not on gadgets.
+ if (index < 0)
+ return thisMeta->inherits(&QObject::staticMetaObject) ? Explicit : Invalid;
+
+ // Find the base type the method belongs to.
+ int methodOffset = included->methodOffset();
+ while (true) {
+ if (included == thisMeta)
+ return Explicit;
+
+ if (methodOffset <= index)
+ return thisMeta->inherits(included) ? Explicit : Invalid;
+
+ included = included->superClass();
+ Q_ASSERT(included);
+ methodOffset -= QMetaObjectPrivate::get(included)->methodCount;
+ };
+
+ Q_UNREACHABLE_RETURN(Invalid);
+ };
+
+ if (const QMetaObject *meta = metaObject())
+ return check(meta);
+
+ // If the QObjectMethod is detached, we can only have gotten here via a lookup.
+ // The lookup checks that the QQmlPropertyCache matches.
+ return Explicit;
}
-const QMetaObject *Heap::QObjectMethod::metaObject()
+QString Heap::QObjectMethod::name() const
{
- if (valueTypeWrapper)
- return valueTypeWrapper->metaObject();
- return object()->metaObject();
+ if (index == QV4::QObjectMethod::DestroyMethod)
+ return QStringLiteral("destroy");
+ else if (index == QV4::QObjectMethod::ToStringMethod)
+ return QStringLiteral("toString");
+
+ const QMetaObject *mo = metaObject();
+ if (!mo)
+ return QString();
+
+ int methodOffset = mo->methodOffset();
+ while (methodOffset > index) {
+ mo = mo->superClass();
+ methodOffset -= QMetaObjectPrivate::get(mo)->methodCount;
+ }
+
+ return "%1::%2"_L1.arg(QLatin1StringView{mo->className()},
+ QLatin1StringView{mo->method(index).name()});
}
-void Heap::QObjectMethod::ensureMethodsCache()
+void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
{
- if (methods)
+ if (methods) {
+ Q_ASSERT(methodCount > 0);
return;
+ }
+
const QMetaObject *mo = metaObject();
+
+ if (!mo)
+ mo = thisMeta;
+
+ Q_ASSERT(mo);
+
int methodOffset = mo->methodOffset();
while (methodOffset > index) {
mo = mo->superClass();
@@ -2118,6 +2788,7 @@ void Heap::QObjectMethod::ensureMethodsCache()
QQmlPropertyData dummy;
QMetaMethod method = mo->method(index);
dummy.load(method);
+ dummy.setMetaObject(mo);
resolvedMethods.append(dummy);
// Look for overloaded methods
QByteArray methodName = method.name();
@@ -2137,35 +2808,24 @@ void Heap::QObjectMethod::ensureMethodsCache()
*methods = resolvedMethods.at(0);
methodCount = 1;
}
+
+ Q_ASSERT(methodCount > 0);
}
-QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionEngine *engine) const
+ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, QObject *o) const
{
- QString result;
- if (const QMetaObject *metaObject = d()->metaObject()) {
-
- result += QString::fromUtf8(metaObject->className()) +
- QLatin1String("(0x") + QString::number((quintptr)d()->object(),16);
-
- if (d()->object()) {
- QString objectName = d()->object()->objectName();
- if (!objectName.isEmpty())
- result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
- }
-
- result += QLatin1Char(')');
- } else {
- result = QLatin1String("null");
- }
-
- return engine->newString(result)->asReturnedValue();
+ return engine->newString(
+ QObjectWrapper::objectToString(
+ engine, o ? o->metaObject() : d()->metaObject(), o))->asReturnedValue();
}
-QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, const Value *args, int argc) const
+ReturnedValue QObjectMethod::method_destroy(
+ ExecutionEngine *engine, QObject *o, const Value *args, int argc) const
{
- if (!d()->object())
+ if (!o)
return Encode::undefined();
- if (QQmlData::keepAliveDuringGarbageCollection(d()->object()))
+
+ if (QQmlData::keepAliveDuringGarbageCollection(o))
return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
int delay = 0;
@@ -2173,171 +2833,256 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, c
delay = args[0].toUInt32();
if (delay > 0)
- QTimer::singleShot(delay, d()->object(), SLOT(deleteLater()));
+ QTimer::singleShot(delay, o, SLOT(deleteLater()));
else
- d()->object()->deleteLater();
+ o->deleteLater();
return Encode::undefined();
}
-ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue QObjectMethod::virtualCall(
+ const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
{
const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
return This->callInternal(thisObject, argv, argc);
}
+void QObjectMethod::virtualCallWithMetaTypes(
+ const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
+{
+ const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
+ This->callInternalWithMetaTypes(thisObject, argv, types, argc);
+}
+
ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const
{
ExecutionEngine *v4 = engine();
- if (d()->index == DestroyMethod)
- return method_destroy(v4, argv, argc);
- else if (d()->index == ToStringMethod)
- return method_toString(v4);
- d()->ensureMethodsCache();
+ const QMetaObject *thisMeta = nullptr;
+
+ QObject *o = nullptr;
+ Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
+ if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
+ thisMeta = w->metaObject();
+ o = w->object();
+ } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
+ thisMeta = w->metaObject();
+ o = w->object();
+ } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
+ thisMeta = w->metaObject();
+ valueWrapper = w->d();
+ }
- QQmlObjectOrGadget object(d()->object());
- if (!d()->object()) {
- if (!d()->valueTypeWrapper)
+ Heap::QObjectMethod::ThisObjectMode mode = Heap::QObjectMethod::Invalid;
+ if (o && o == d()->object()) {
+ mode = Heap::QObjectMethod::Explicit;
+ // Nothing to do; objects are the same. This should be common
+ } else if (valueWrapper && valueWrapper == d()->wrapper) {
+ mode = Heap::QObjectMethod::Explicit;
+ // Nothing to do; gadgets are the same. This should be somewhat common
+ } else {
+ mode = d()->checkThisObject(thisMeta);
+ if (mode == Heap::QObjectMethod::Invalid) {
+ v4->throwError(QLatin1String("Cannot call method %1 on %2").arg(
+ d()->name(), thisObject->toQStringNoThrow()));
return Encode::undefined();
-
- object = QQmlObjectOrGadget(d()->metaObject(), d()->valueTypeWrapper->gadgetPtr());
+ }
}
- Scope scope(v4);
- JSCallData cData(scope, argc, argv, thisObject);
- CallData *callData = cData.callData();
+ QQmlObjectOrGadget object = [&](){
+ if (mode == Heap::QObjectMethod::Included) {
+ QV4::Scope scope(v4);
+ if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, d()->wrapper); qobject)
+ return QQmlObjectOrGadget(qobject->object());
+ if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, d()->wrapper); type)
+ return QQmlObjectOrGadget(type->object());
+ if (QV4::Scoped<QV4::QQmlValueTypeWrapper> value(scope, d()->wrapper); value) {
+ valueWrapper = value->d();
+ return QQmlObjectOrGadget(valueWrapper->metaObject(), valueWrapper->gadgetPtr());
+ }
+ Q_UNREACHABLE();
+ } else {
+ if (o)
+ return QQmlObjectOrGadget(o);
- auto method = d()->methods[0];
+ Q_ASSERT(valueWrapper);
+ if (!valueWrapper->enforcesLocation())
+ QV4::ReferenceObject::readReference(valueWrapper);
+ return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr());
+ }
+ }();
- if (method.isV4Function()) {
- QV4::ScopedValue rv(scope, QV4::Value::undefinedValue());
- QQmlV4Function func(callData, rv, v4);
- QQmlV4Function *funcptr = &func;
+ if (object.isNull())
+ return Encode::undefined();
- void *args[] = { nullptr, &funcptr };
- object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex(), args);
+ if (d()->index == DestroyMethod)
+ return method_destroy(v4, object.qObject(), argv, argc);
+ else if (d()->index == ToStringMethod)
+ return method_toString(v4, object.qObject());
- return rv->asReturnedValue();
- }
+ d()->ensureMethodsCache(thisMeta);
- if (d()->methodCount == 1) {
- return CallPrecise(object, method, v4, callData);
- } else {
- return CallOverloaded(object, d()->methods, d()->methodCount, v4, callData);
+ Scope scope(v4);
+ JSCallData cData(thisObject, argv, argc);
+ CallData *callData = cData.callData(scope);
+
+ const QQmlPropertyData *method = d()->methods;
+
+ // If we call the method, we have to write back any value type references afterwards.
+ // The method might change the value.
+ const auto doCall = [&](const auto &call) {
+ if (!method->isConstant()) {
+ if (valueWrapper && valueWrapper->isReference()) {
+ ScopedValue rv(scope, call());
+ valueWrapper->writeBack();
+ return rv->asReturnedValue();
+ }
+ }
+
+ return call();
+ };
+
+ if (d()->methodCount != 1) {
+ Q_ASSERT(d()->methodCount > 0);
+ method = resolveOverloaded(object, d()->methods, d()->methodCount, v4, callData);
+ if (method == nullptr)
+ return Encode::undefined();
}
-}
-DEFINE_OBJECT_VTABLE(QObjectMethod);
+ if (method->isV4Function()) {
+ return doCall([&]() {
+ ScopedValue rv(scope, Value::undefinedValue());
+ QQmlV4Function func(callData, rv, v4);
+ QQmlV4FunctionPtr funcptr = &func;
+ void *args[] = { nullptr, &funcptr };
+ object.metacall(QMetaObject::InvokeMetaMethod, method->coreIndex(), args);
-void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject)
-{
- FunctionObject::init();
- this->metaObject = metaObject;
- constructors = nullptr;
- constructorCount = 0;
+ return rv->asReturnedValue();
+ });
+ }
+
+ return doCall([&]() { return callPrecise(object, *method, v4, callData); });
}
-void Heap::QMetaObjectWrapper::destroy()
+struct ToStringMetaMethod
{
- delete[] constructors;
-}
+ constexpr int parameterCount() const { return 0; }
+ constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
+ constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
+};
-void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
+void QObjectMethod::callInternalWithMetaTypes(
+ QObject *thisObject, void **argv, const QMetaType *types, int argc) const
+{
+ ExecutionEngine *v4 = engine();
- const int count = metaObject->constructorCount();
- if (constructorCount != count) {
- delete[] constructors;
- constructorCount = count;
- if (count == 0) {
- constructors = nullptr;
- return;
- }
- constructors = new QQmlPropertyData[count];
+ const QMetaObject *thisMeta = nullptr;
+ Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
- for (int i = 0; i < count; ++i) {
- QMetaMethod method = metaObject->constructor(i);
- QQmlPropertyData &d = constructors[i];
- d.load(method);
- d.setCoreIndex(i);
- }
+ if (thisObject) {
+ thisMeta = thisObject->metaObject();
+ } else {
+ Q_ASSERT(Value::fromHeapObject(d()->wrapper).as<QQmlValueTypeWrapper>());
+ valueWrapper = d()->wrapper.cast<Heap::QQmlValueTypeWrapper>();
+ thisMeta = valueWrapper->metaObject();
}
-}
+ QQmlObjectOrGadget object = [&](){
+ if (thisObject)
+ return QQmlObjectOrGadget(thisObject);
-ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
+ Scope scope(v4);
+ Scoped<QQmlValueTypeWrapper> wrapper(scope, d()->wrapper);
+ Q_ASSERT(wrapper);
- QV4::Scope scope(engine);
- Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue());
- mo->init(engine);
- return mo->asReturnedValue();
-}
+ Heap::QQmlValueTypeWrapper *valueWrapper = wrapper->d();
+ if (!valueWrapper->enforcesLocation())
+ QV4::ReferenceObject::readReference(valueWrapper);
+ return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr());
+ }();
-void QMetaObjectWrapper::init(ExecutionEngine *) {
- const QMetaObject & mo = *d()->metaObject;
+ if (object.isNull())
+ return;
- for (int i = 0; i < mo.enumeratorCount(); i++) {
- QMetaEnum Enum = mo.enumerator(i);
- for (int k = 0; k < Enum.keyCount(); k++) {
- const char* key = Enum.key(k);
- const int value = Enum.value(k);
- defineReadonlyProperty(QLatin1String(key), Value::fromInt32(value));
- }
+ if (d()->index == DestroyMethod) {
+ // method_destroy will use at most one argument
+ QV4::convertAndCall(
+ v4, thisObject, argv, types, std::min(argc, 1),
+ [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
+ Q_UNUSED(thisObject);
+ return method_destroy(v4, object.qObject(), argv, argc);
+ });
+ return;
}
-}
-ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
-{
- const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f);
- return This->constructInternal(argv, argc);
-}
-
-ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const
-{
-
- d()->ensureConstructorsCache();
-
- ExecutionEngine *v4 = engine();
- const QMetaObject* mo = d()->metaObject;
- if (d()->constructorCount == 0) {
- return v4->throwTypeError(QLatin1String(mo->className())
- + QLatin1String(" has no invokable constructor"));
+ if (d()->index == ToStringMethod) {
+ const ToStringMetaMethod metaMethod;
+ QV4::coerceAndCall(
+ v4, &metaMethod, argv, types, argc,
+ [v4, thisMeta, object](void **argv, int) {
+ *static_cast<QString *>(argv[0])
+ = QObjectWrapper::objectToString(v4, thisMeta, object.qObject());
+ });
+ return;
}
- Scope scope(v4);
- Scoped<QObjectWrapper> object(scope);
- JSCallData cData(scope, argc, argv);
- CallData *callData = cData.callData();
+ d()->ensureMethodsCache(thisMeta);
- const QQmlObjectOrGadget objectOrGadget(mo);
+ const QQmlPropertyData *method = d()->methods;
+ if (d()->methodCount != 1) {
+ Q_ASSERT(d()->methodCount > 0);
+ method = resolveOverloaded(d()->methods, d()->methodCount, argv, argc, types);
+ }
- if (d()->constructorCount == 1) {
- object = CallPrecise(objectOrGadget, d()->constructors[0], v4, callData, QMetaObject::CreateInstance);
+ if (!method || method->isV4Function()) {
+ QV4::convertAndCall(
+ v4, thisObject, argv, types, argc,
+ [this](const Value *thisObject, const Value *argv, int argc) {
+ return callInternal(thisObject, argv, argc);
+ });
} else {
- object = CallOverloaded(objectOrGadget, d()->constructors, d()->constructorCount, v4, callData, QMetaObject::CreateInstance);
- }
- Scoped<QMetaObjectWrapper> metaObject(scope, this);
- object->defineDefaultProperty(v4->id_constructor(), metaObject);
- object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
- return object.asReturnedValue();
+ const QMetaMethod metaMethod = method->metaMethod();
+ QV4::coerceAndCall(
+ v4, &metaMethod, argv, types, argc,
+ [v4, object, valueWrapper, method](void **argv, int argc) {
+ Q_UNUSED(argc);
+
+ // If we call the method, we have to write back any value type references afterwards.
+ // The method might change the value.
+ object.metacall(QMetaObject::InvokeMetaMethod, method->coreIndex(), argv);
+ if (!method->isConstant()) {
+ if (valueWrapper && valueWrapper->isReference())
+ valueWrapper->writeBack();
+ }
-}
+ // If the method returns a QObject* we need to track it on the JS heap
+ // (if it's destructible).
+ QObject *qobjectPtr = nullptr;
+ const QMetaType resultType = method->propType();
+ if (argv[0]) {
+ if (resultType.flags() & QMetaType::PointerToQObject) {
+ qobjectPtr = *static_cast<QObject **>(argv[0]);
+ } else if (resultType == QMetaType::fromType<QVariant>()) {
+ const QVariant *result = static_cast<const QVariant *>(argv[0]);
+ const QMetaType variantType = result->metaType();
+ if (variantType.flags() & QMetaType::PointerToQObject)
+ qobjectPtr = *static_cast<QObject *const *>(result->data());
+ }
+ }
-bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
-{
- Q_ASSERT(a->as<QMetaObjectWrapper>());
- QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
- QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>();
- if (!bMetaObject)
- return true;
- return aMetaObject->metaObject() == bMetaObject->metaObject();
+ if (qobjectPtr) {
+ QQmlData *ddata = QQmlData::get(qobjectPtr, true);
+ if (!ddata->explicitIndestructibleSet) {
+ ddata->indestructible = false;
+ QObjectWrapper::ensureWrapper(v4, qobjectPtr);
+ }
+ }
+ });
+ }
}
-DEFINE_OBJECT_VTABLE(QMetaObjectWrapper);
-
-
-
+DEFINE_OBJECT_VTABLE(QObjectMethod);
void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
{
@@ -2348,56 +3093,59 @@ void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
DEFINE_OBJECT_VTABLE(QmlSignalHandler);
-void QmlSignalHandler::initProto(ExecutionEngine *engine)
+ReturnedValue QmlSignalHandler::call(const Value *thisObject, const Value *argv, int argc) const
{
- if (engine->signalHandlerPrototype()->d_unchecked())
- return;
+ const QString handlerName = QQmlSignalNames::signalNameToHandlerName(
+ object()->metaObject()->method(signalIndex()).name());
+ qCWarning(lcSignalHandler).noquote()
+ << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
+ "not call it directly. Make it a proper function and call "
+ "that or emit the signal.")
+ .arg(handlerName, thisObject->toQStringNoThrow());
- Scope scope(engine);
- ScopedObject o(scope, engine->newObject());
- QV4::ScopedString connect(scope, engine->newIdentifier(QStringLiteral("connect")));
- QV4::ScopedString disconnect(scope, engine->newIdentifier(QStringLiteral("disconnect")));
- o->put(connect, QV4::ScopedValue(scope, engine->functionPrototype()->get(connect)));
- o->put(disconnect, QV4::ScopedValue(scope, engine->functionPrototype()->get(disconnect)));
+ Scope scope(engine());
+ Scoped<QObjectMethod> method(
+ scope, QObjectMethod::create(
+ scope.engine->rootContext(),
+ static_cast<Heap::QObjectWrapper *>(nullptr),
+ signalIndex()));
- engine->jsObjects[QV4::ExecutionEngine::SignalHandlerProto] = o->d();
+ return method->call(thisObject, argv, argc);
}
-void MultiplyWrappedQObjectMap::insert(QObject *key, Heap::Object *value)
+void QmlSignalHandler::initProto(ExecutionEngine *engine)
{
- QHash<QObject*, QV4::WeakValue>::operator[](key).set(value->internalClass->engine, value);
- connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
-}
-
+ if (engine->signalHandlerPrototype()->d_unchecked())
+ return;
+ Scope scope(engine);
+ ScopedObject o(scope, engine->newObject());
+ ScopedString connect(scope, engine->newIdentifier(QStringLiteral("connect")));
+ ScopedString disconnect(scope, engine->newIdentifier(QStringLiteral("disconnect")));
+ o->put(connect, ScopedValue(scope, engine->functionPrototype()->get(connect)));
+ o->put(disconnect, ScopedValue(scope, engine->functionPrototype()->get(disconnect)));
-MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it)
-{
- disconnect(it.key(), SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
- return QHash<QObject*, QV4::WeakValue>::erase(it);
+ engine->jsObjects[ExecutionEngine::SignalHandlerProto] = o->d();
}
-void MultiplyWrappedQObjectMap::remove(QObject *key)
-{
- Iterator it = find(key);
- if (it == end())
- return;
- erase(it);
-}
-void MultiplyWrappedQObjectMap::mark(QObject *key, MarkStack *markStack)
+MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(
+ MultiplyWrappedQObjectMap::Iterator it)
{
- Iterator it = find(key);
- if (it == end())
- return;
- it->markOnce(markStack);
+ const QObjectBiPointer key = it.key();
+ const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
+ disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
+ return QHash<QObjectBiPointer, WeakValue>::erase(it);
}
void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
{
- QHash<QObject*, QV4::WeakValue>::remove(object);
+ QHash<QObjectBiPointer, WeakValue>::remove(object);
+ QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object));
}
+} // namespace QV4
+
QT_END_NAMESPACE
#include "moc_qv4qobjectwrapper_p.cpp"