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.cpp1016
1 files changed, 683 insertions, 333 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 694b7828b6..7c05923d66 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -14,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>
@@ -33,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>
@@ -47,6 +52,7 @@
#endif
#include <QtCore/qloggingcategory.h>
#include <QtCore/qqueue.h>
+#include <QtCore/qtypes.h>
#include <vector>
QT_BEGIN_NAMESPACE
@@ -55,6 +61,7 @@ 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
@@ -130,37 +137,133 @@ static ReturnedValue loadProperty(
if (property.isQList() && propMetaType.flags().testFlag(QMetaType::IsQmlList))
return QmlListWrapper::create(v4, object, property.coreIndex(), propMetaType);
- // TODO: Check all the builtin types here. See getGadgetProperty() in qqmlvaluetypewrapper.cpp
- switch (property.isEnum() ? QMetaType::Int : propMetaType.id()) {
- case QMetaType::Int: {
- int v = 0;
+ const auto encodeSimple = [&](auto v) {
property.readProperty(object, &v);
return Encode(v);
+ };
+
+ const auto encodeInt = [&](auto v) {
+ property.readProperty(object, &v);
+ return Encode(int(v));
+ };
+
+ const auto encodeDouble = [&](auto v) {
+ property.readProperty(object, &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();
+ };
+
+ 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 v4->newArrayBuffer(v)->asReturnedValue();
}
- case QMetaType::Bool: {
- bool v = false;
+ 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 Encode(v);
+ return Encode(v4->newRegExpObject(v));
}
- case QMetaType::QString: {
- QString v;
+#endif
+ case QMetaType::QVariantMap: {
+ QVariantMap v;
property.readProperty(object, &v);
- return v4->newString(v)->asReturnedValue();
+ return scope.engine->fromData(
+ propMetaType, &v, wrapper, property.coreIndex(), referenceFlags(v4, property));
}
- case QMetaType::UInt: {
- uint v = 0;
+ case QMetaType::QJsonValue: {
+ QJsonValue v;
property.readProperty(object, &v);
- return Encode(v);
+ return QV4::JsonObject::fromJsonValue(v4, v);
}
- case QMetaType::Float: {
- float v = 0;
+ case QMetaType::QJsonObject: {
+ QJsonObject v;
property.readProperty(object, &v);
- return Encode(v);
+ return QV4::JsonObject::fromJsonObject(v4, v);
}
- case QMetaType::Double: {
- double v = 0;
+ case QMetaType::QJsonArray:
+ return encodeSequence(QMetaSequence::fromContainer<QJsonArray>());
+ 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(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;
@@ -204,13 +307,9 @@ static ReturnedValue loadProperty(
}
// See if it's a sequence type.
- // Pass nullptr as data. It's lazy-loaded.
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(
- v4, propMetaType, nullptr,
- wrapper, property.coreIndex(),
- referenceFlags(scope.engine, property)));
- if (!retn->isUndefined())
- return retn->asReturnedValue();
+ const QQmlType qmlType = QQmlMetaType::qmlListType(propMetaType);
+ if (qmlType.isSequentialContainer())
+ return encodeSequence(qmlType.listMetaSequence());
QVariant v(propMetaType);
property.readProperty(object, v.data());
@@ -258,20 +357,15 @@ ReturnedValue QObjectWrapper::getProperty(
Q_ASSERT(vmemo);
return vmemo->vmeMethod(property->coreIndex());
} else if (property->isV4Function()) {
- Scope scope(engine);
- ScopedContext global(scope, engine->qmlContext());
- if (!global)
- global = engine->rootContext();
return QObjectMethod::create(
- global, (flags & AttachMethods) ? object : nullptr, property->coreIndex());
+ engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
} else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine);
return engine->memoryManager->allocate<QmlSignalHandler>(
object, property->coreIndex())->asReturnedValue();
} else {
- ExecutionContext *global = engine->rootContext();
return QObjectMethod::create(
- global, (flags & AttachMethods) ? object : nullptr, property->coreIndex());
+ engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
}
}
@@ -290,7 +384,8 @@ ReturnedValue QObjectWrapper::getProperty(
}
}
-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()))
@@ -302,8 +397,7 @@ static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, Str
if (hasProperty)
*hasProperty = true;
- ExecutionContext *global = v4->rootContext();
- return OptionalReturnedValue(QObjectMethod::create(global, qobj, index));
+ return OptionalReturnedValue(QObjectMethod::create(v4, qobj, index));
}
static OptionalReturnedValue getPropertyFromImports(
@@ -313,24 +407,29 @@ static OptionalReturnedValue getPropertyFromImports(
if (!qmlContext || !qmlContext->imports())
return OptionalReturnedValue();
- QQmlTypeNameCache::Result r = qmlContext->imports()->query(name);
-
if (hasProperty)
*hasProperty = true;
- if (!r.isValid())
- return OptionalReturnedValue();
+ 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));
+ 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();
}
- Q_UNREACHABLE_RETURN(OptionalReturnedValue());
}
ReturnedValue QObjectWrapper::getQmlProperty(
@@ -347,7 +446,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
ExecutionEngine *v4 = engine();
- if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty))
+ if (auto methodValue = getDestroyOrToStringMethod(v4, name, d(), hasProperty))
return *methodValue;
QQmlPropertyData local;
@@ -390,7 +489,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
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);
@@ -465,6 +564,28 @@ bool QObjectWrapper::setQmlProperty(
return true;
}
+/*!
+ \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)
@@ -478,7 +599,9 @@ void QObjectWrapper::setProperty(
Scope scope(engine);
if (ScopedFunctionObject f(scope, value); f) {
- if (!f->isBinding()) {
+ if (f->as<QQmlTypeWrapper>()) {
+ // Ignore. It's probably a singleton or an attached type.
+ } else if (!f->isBinding()) {
const bool isAliasToAllowed = [&]() {
if (property->isAlias()) {
const QQmlPropertyIndex originalIndex(property->coreIndex(), -1);
@@ -511,7 +634,7 @@ void QObjectWrapper::setProperty(
QQmlRefPointer<QQmlContextData> callingQmlContext = scope.engine->callingQmlContext();
Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
- ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
+ Scoped<JavaScriptFunctionObject> f(scope, bindingFunction->bindingFunction());
ScopedContext ctx(scope, f->scope());
// binding assignment.
@@ -551,14 +674,26 @@ void QObjectWrapper::setProperty(
if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) {
- Q_ASSERT(binding->kind() == QQmlAbstractBinding::QmlBinding);
- 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()));
@@ -580,7 +715,9 @@ void QObjectWrapper::setProperty(
const QMetaType propType = property->propType();
// functions are already handled, except for the QJSValue case
- Q_ASSERT(!value.as<FunctionObject>() || propType == QMetaType::fromType<QJSValue>());
+ Q_ASSERT(!value.as<FunctionObject>()
+ || value.as<QV4::QQmlTypeWrapper>()
+ || propType == QMetaType::fromType<QJSValue>());
if (value.isNull() && property->isQObject()) {
PROPERTY_STORE(QObject*, nullptr);
@@ -602,7 +739,7 @@ void QObjectWrapper::setProperty(
scope.engine->throwError(error);
return;
} else if (propType == QMetaType::fromType<int>() && value.isNumber()) {
- PROPERTY_STORE(int, value.asDouble());
+ PROPERTY_STORE(int, value.toInt32());
} else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) {
PROPERTY_STORE(qreal, qreal(value.asDouble()));
} else if (propType == QMetaType::fromType<float>() && value.isNumber()) {
@@ -724,15 +861,6 @@ ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObjec
return constWrapper.asReturnedValue();
}
-Heap::QObjectMethod *QObjectWrapper::cloneMethod(
- ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
- Heap::Object *wrapper, QObject *object)
-{
- Scope scope(engine);
- Scoped<QObjectMethod> method(scope, QObjectMethod::create(engine, cloneFrom, wrapper, object));
- return method ? method->d() : nullptr;
-}
-
void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
{
if (QQmlData::wasDeleted(object))
@@ -972,7 +1100,7 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
return Encode::undefined();
QQmlData *ddata = QQmlData::get(qobj, false);
- if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) {
+ 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());
@@ -1004,7 +1132,8 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
&& !property->isVarProperty()
&& !property->isVMEFunction() // Handled by QObjectLookup
&& !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too
- setupQObjectMethodLookup(lookup, ddata, property, This, nullptr);
+ QV4::Heap::QObjectMethod *method = nullptr;
+ setupQObjectMethodLookup(lookup, ddata, property, This, method);
lookup->getter = Lookup::getterQObjectMethod;
return lookup->getter(lookup, engine, *object);
}
@@ -1081,13 +1210,15 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
{
- Q_UNUSED(receiver);
switch (which) {
case Destroy: {
delete static_cast<QObjectSlotDispatcher*>(this_);
}
break;
case Call: {
+ if (QQmlData::wasDeleted(receiver))
+ break;
+
QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
ExecutionEngine *v4 = This->function.engine();
// Might be that we're still connected to a signal that's emitted long
@@ -1096,7 +1227,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
if (!v4)
break;
- QQmlMetaObject::ArgTypeStorage storage;
+ QQmlMetaObject::ArgTypeStorage<9> storage;
QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr);
int argCount = storage.size();
@@ -1236,7 +1367,16 @@ ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Valu
}
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,
@@ -1294,7 +1434,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 {
@@ -1324,9 +1473,37 @@ 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
@@ -1350,13 +1527,22 @@ void QObjectWrapper::destroyObject(bool lastCall)
if (!o->parent() && !ddata->indestructible) {
if (ddata && ddata->ownContext) {
Q_ASSERT(ddata->ownContext.data() == ddata->context);
- ddata->ownContext->emitDestruction();
+ 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();
+ ddata->disconnectNotifiers(QQmlData::DeleteNotifyList::No);
+ ddata->compilationUnit.reset();
+
+ qDeleteAll(ddata->deferredData);
+ ddata->deferredData.clear();
+
if (lastCall)
delete o;
else
@@ -1476,7 +1662,7 @@ static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMe
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 "
@@ -1512,6 +1698,26 @@ static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMe
}
}
+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.
@@ -1625,22 +1831,48 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
}
}
} else if (const Object *obj = actual.as<Object>()) {
- if (obj->as<VariantObject>()) {
- if (conversionType == qMetaTypeId<QVariant>())
- return 0;
- if (ExecutionEngine::toVariant(actual, QMetaType {}).metaType() == conversionMetaType)
- return 0;
- else
- return 10;
+ 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 (const Sequence *sequence = obj->as<Sequence>()) {
@@ -1650,13 +1882,12 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
return 10;
}
- if (obj->as<QQmlValueTypeWrapper>()) {
- const QVariant v = ExecutionEngine::toVariant(actual, QMetaType {});
- if (v.userType() == conversionType)
- return 0;
- else if (v.canConvert(conversionMetaType))
- return 5;
- return 10;
+ if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
+ return MatchVariant(conversionMetaType, [wrapper]() {
+ return wrapper->d()->isVariant()
+ ? wrapper->toVariant().metaType()
+ : wrapper->type();
+ });
}
if (conversionType == QMetaType::QJsonObject)
@@ -1680,9 +1911,17 @@ static int numDefinedArguments(CallData *callArgs)
return numDefinedArguments;
}
-static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
- ExecutionEngine *engine, 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;
@@ -1694,11 +1933,7 @@ static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPro
}
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;
}
@@ -1709,7 +1944,7 @@ static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPro
<< "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());
@@ -1724,7 +1959,7 @@ static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPro
if (data.hasArguments()) {
- QQmlMetaObject::ArgTypeStorage storage;
+ QQmlMetaObject::ArgTypeStorage<9> storage;
bool ok = false;
if (data.isConstructor())
@@ -1771,7 +2006,7 @@ Resolve the overloaded method to call. The algorithm works conceptually like th
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 const QQmlPropertyData *ResolveOverloaded(
+const QQmlPropertyData *QObjectMethod::resolveOverloaded(
const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount,
ExecutionEngine *engine, CallData *callArgs)
{
@@ -1789,12 +2024,12 @@ static const QQmlPropertyData *ResolveOverloaded(
for (int i = 0; i < methodCount; ++i) {
const QQmlPropertyData *attempt = methods + i;
- if (lcOverloadResolution().isInfoEnabled()) {
+ if (lcOverloadResolution().isDebugEnabled()) {
const QQmlPropertyData &candidate = methods[i];
const QMetaMethod m = candidate.isConstructor()
? object.metaObject()->constructor(candidate.coreIndex())
: object.metaObject()->method(candidate.coreIndex());
- qCInfo(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
+ qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
}
// QQmlV4Function overrides anything that doesn't provide the exact number of arguments
@@ -1805,17 +2040,17 @@ static const QQmlPropertyData *ResolveOverloaded(
int sumMethodMatchScore = bestSumMatchScore;
if (!attempt->isV4Function()) {
- QQmlMetaObject::ArgTypeStorage storage;
+ QQmlMetaObject::ArgTypeStorage<9> storage;
int methodArgumentCount = 0;
if (attempt->hasArguments()) {
if (attempt->isConstructor()) {
if (!object.constructorParameterTypes(attempt->coreIndex(), &storage, nullptr)) {
- qCInfo(lcOverloadResolution, "rejected, could not get ctor argument types");
+ qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
continue;
}
} else {
if (!object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr)) {
- qCInfo(lcOverloadResolution, "rejected, could not get ctor argument types");
+ qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
continue;
}
}
@@ -1823,7 +2058,7 @@ static const QQmlPropertyData *ResolveOverloaded(
}
if (methodArgumentCount > argumentCount) {
- qCInfo(lcOverloadResolution, "rejected, insufficient arguments");
+ qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
continue; // We don't have sufficient arguments to call this method
}
@@ -1831,7 +2066,7 @@ static const QQmlPropertyData *ResolveOverloaded(
? 0
: (definedArgumentCount - methodArgumentCount + 1);
if (methodParameterScore > bestParameterScore) {
- qCInfo(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
+ qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
continue; // We already have a better option
}
@@ -1853,21 +2088,21 @@ static const QQmlPropertyData *ResolveOverloaded(
bestParameterScore = methodParameterScore;
bestMaxMatchScore = maxMethodMatchScore;
bestSumMatchScore = sumMethodMatchScore;
- qCInfo(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
- << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
- << "bestSumMatchScore" << bestSumMatchScore << "\n";
+ qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
+ << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
+ << "bestSumMatchScore" << bestSumMatchScore << "\n";
} else {
- qCInfo(lcOverloadResolution) << "did not update best\n"
- << "bestParameterScore" << bestParameterScore << "\t"
- << "methodParameterScore" << methodParameterScore << "\n"
- << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
- << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
- << "bestSumMatchScore" << bestSumMatchScore << "\t"
- << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
+ 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 && bestMaxMatchScore == 0) {
- qCInfo(lcOverloadResolution, "perfect match");
+ qCDebug(lcOverloadResolution, "perfect match");
break; // We can't get better than that
}
@@ -1878,13 +2113,11 @@ static const QQmlPropertyData *ResolveOverloaded(
} 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());
}
engine->throwError(error);
@@ -1892,7 +2125,69 @@ static const QQmlPropertyData *ResolveOverloaded(
}
}
+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;
+}
+
+const QQmlPropertyData *QObjectMethod::resolveOverloaded(
+ const QQmlPropertyData *methods, int methodCount,
+ void **argv, int argc, const QMetaType *types)
+{
+ // 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()
{
@@ -2057,6 +2352,10 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
else
qstringPtr = new (&allocData) QString(value.toQStringNoThrow());
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();
@@ -2087,8 +2386,8 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
return true;
case QMetaType::QJsonArray: {
Scope scope(engine);
- ScopedArrayObject a(scope, value);
- jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(a));
+ ScopedObject o(scope, value);
+ jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(o));
return true;
}
case QMetaType::QJsonObject: {
@@ -2135,6 +2434,11 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
return true;
}
+ if (const QmlListWrapper *listWrapper = value.as<QmlListWrapper>()) {
+ *qlistPtr = listWrapper->d()->property()->toList<QList<QObject *>>();
+ return true;
+ }
+
qlistPtr->append(nullptr);
return value.isNullOrUndefined();
}
@@ -2175,37 +2479,14 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const
}
// Convert via QVariant through the QML engine.
- qvariantPtr = new (&allocData) QVariant();
+ qvariantPtr = new (&allocData) QVariant(metaType);
type = QVariantWrappedType;
- QVariant v = ExecutionEngine::toVariant(value, metaType);
-
- if (v.metaType() == metaType) {
- *qvariantPtr = std::move(v);
+ if (ExecutionEngine::metaTypeFromJS(value, metaType, qvariantPtr->data()))
return true;
- }
-
- if (v.canConvert(metaType)) {
- *qvariantPtr = std::move(v);
- qvariantPtr->convert(metaType);
- return true;
- }
- const QQmlMetaObject mo = QQmlMetaType::rawMetaObjectForType(metaType);
- if (!mo.isNull() && v.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
- QObject *obj = QQmlMetaType::toQObject(v);
-
- if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) {
- *qvariantPtr = QVariant(metaType, nullptr);
- return false;
- }
-
- *qvariantPtr = QVariant(metaType, &obj);
- return true;
- }
-
- *qvariantPtr = QVariant(metaType, (void *)nullptr);
- return false;
+ const QVariant v = ExecutionEngine::toVariant(value, metaType);
+ return QMetaType::convert(v.metaType(), v.constData(), metaType, qvariantPtr->data());
}
ReturnedValue CallArgument::toValue(ExecutionEngine *engine)
@@ -2273,34 +2554,34 @@ ReturnedValue CallArgument::toValue(ExecutionEngine *engine)
return Encode::undefined();
}
-ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
+ReturnedValue QObjectMethod::create(ExecutionEngine *engine, 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;
+ Scope valueScope(engine);
+ Scoped<QObjectMethod> method(
+ valueScope,
+ engine->memoryManager->allocate<QObjectMethod>(engine, wrapper, index));
return method.asReturnedValue();
}
-ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
+ReturnedValue QObjectMethod::create(
+ ExecutionEngine *engine, 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);
+ Scope valueScope(engine);
+ Scoped<QObjectMethod> method(
+ valueScope,
+ engine->memoryManager->allocate<QObjectMethod>(engine, valueType, index));
return method.asReturnedValue();
}
ReturnedValue QObjectMethod::create(
ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
- Heap::Object *wrapper, QObject *object)
+ Heap::Object *wrapper, Heap::Object *object)
{
Scope valueScope(engine);
Scoped<QQmlValueTypeWrapper> valueTypeWrapper(valueScope);
- if (cloneFrom->valueTypeWrapper) {
- Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->valueTypeWrapper);
+ if (cloneFrom->wrapper) {
+ Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->wrapper);
if (ref) {
valueTypeWrapper = QQmlValueTypeWrapper::create(engine, ref->d(), wrapper);
} else {
@@ -2311,73 +2592,97 @@ ReturnedValue QObjectMethod::create(
}
}
- Scoped<ExecutionContext> context(valueScope, cloneFrom->scope.get());
Scoped<QObjectMethod> method(
- valueScope, engine->memoryManager->allocate<QV4::QObjectMethod>(context));
+ valueScope,
+ engine->memoryManager->allocate<QV4::QObjectMethod>(
+ engine, valueTypeWrapper ? valueTypeWrapper->d() : object, cloneFrom->index));
- method->d()->index = cloneFrom->index;
method->d()->methodCount = cloneFrom->methodCount;
- if (valueTypeWrapper)
- method->d()->valueTypeWrapper.set(engine, valueTypeWrapper->d());
- else
- method->d()->setObject(object);
-
- if (cloneFrom->methodCount == 1) {
+ 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;
- } else {
+ 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::ExecutionEngine *engine, Object *object, int methodIndex)
{
- Heap::FunctionObject::init(scope);
+ Heap::FunctionObject::init(engine);
+ wrapper.set(engine, object);
+ index = methodIndex;
}
const QMetaObject *Heap::QObjectMethod::metaObject() const
{
- if (valueTypeWrapper)
- return valueTypeWrapper->metaObject();
+ 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 (qObj.isValid())
- return false;
+ if (!wrapper)
+ return true;
- if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get())
- return wrapper->object() == nullptr;
+ QV4::Scope scope(internalClass->engine);
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
+ return valueWrapper->d()->object() == nullptr;
- return true;
+ return false;
}
bool Heap::QObjectMethod::isAttachedTo(QObject *o) const
{
- if (qObj.isValid() && qObj != o)
- return false;
+ 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 (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) {
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper) {
QV4::Scope scope(wrapper->internalClass->engine);
- QV4::Scoped<QV4::QObjectWrapper> qobject(scope, wrapper->object());
- if (qobject && qobject->object() == o)
- return true;
- QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, wrapper->object());
- if (type && type->object() == o)
- return true;
+ 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 true;
+ return false;
}
Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
@@ -2387,7 +2692,7 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
if (!thisMeta) {
// You can only get a detached method via a lookup, and then you have a thisObject.
- Q_ASSERT(valueTypeWrapper || qObj);
+ Q_ASSERT(wrapper);
return Included;
}
@@ -2405,6 +2710,10 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
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) {
@@ -2414,19 +2723,16 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
if (methodOffset <= index)
return thisMeta->inherits(included) ? Explicit : Invalid;
- methodOffset -= QMetaObjectPrivate::get(included)->methodCount;
included = included->superClass();
Q_ASSERT(included);
+ methodOffset -= QMetaObjectPrivate::get(included)->methodCount;
};
Q_UNREACHABLE_RETURN(Invalid);
};
- if (QObject *o = object())
- return check(o->metaObject());
-
- if (valueTypeWrapper)
- return check(valueTypeWrapper->metaObject());
+ 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.
@@ -2456,8 +2762,10 @@ QString Heap::QObjectMethod::name() const
void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
{
- if (methods)
+ if (methods) {
+ Q_ASSERT(methodCount > 0);
return;
+ }
const QMetaObject *mo = metaObject();
@@ -2475,6 +2783,7 @@ void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
QQmlPropertyData dummy;
QMetaMethod method = mo->method(index);
dummy.load(method);
+ dummy.setMetaObject(mo);
resolvedMethods.append(dummy);
// Look for overloaded methods
QByteArray methodName = method.name();
@@ -2494,15 +2803,8 @@ void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
*methods = resolvedMethods.at(0);
methodCount = 1;
}
-}
-static QObject *qObject(const Value *v)
-{
- if (const QObjectWrapper *o = v->as<QObjectWrapper>())
- return o->object();
- if (const QQmlTypeWrapper *t = v->as<QQmlTypeWrapper>())
- return t->object();
- return nullptr;
+ Q_ASSERT(methodCount > 0);
}
ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, QObject *o) const
@@ -2533,32 +2835,44 @@ ReturnedValue QObjectMethod::method_destroy(
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();
const QMetaObject *thisMeta = nullptr;
- QObject *o = qObject(thisObject);
- Heap::QQmlValueTypeWrapper *wrapper = nullptr;
- if (o) {
- thisMeta = o->metaObject();
- } else if (const QQmlValueTypeWrapper *value = thisObject->as<QQmlValueTypeWrapper>()) {
- wrapper = value->d();
- thisMeta = wrapper->metaObject();
+ 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();
}
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 (wrapper && wrapper == d()->valueTypeWrapper) {
+ } else if (valueWrapper && valueWrapper == d()->wrapper) {
mode = Heap::QObjectMethod::Explicit;
// Nothing to do; gadgets are the same. This should be somewhat common
} else {
@@ -2572,20 +2886,24 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
QQmlObjectOrGadget object = [&](){
if (mode == Heap::QObjectMethod::Included) {
- if (QObject *qObject = d()->qObj)
- return QQmlObjectOrGadget(qObject);
-
- wrapper = d()->valueTypeWrapper;
- Q_ASSERT(wrapper);
- return QQmlObjectOrGadget(wrapper->metaObject(), wrapper->gadgetPtr());
+ 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);
- Q_ASSERT(wrapper);
- if (!wrapper->enforcesLocation())
- QV4::ReferenceObject::readReference(wrapper);
- return QQmlObjectOrGadget(thisMeta, wrapper->gadgetPtr());
+ Q_ASSERT(valueWrapper);
+ if (!valueWrapper->enforcesLocation())
+ QV4::ReferenceObject::readReference(valueWrapper);
+ return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr());
}
}();
@@ -2609,9 +2927,9 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
// The method might change the value.
const auto doCall = [&](const auto &call) {
if (!method->isConstant()) {
- if (wrapper && wrapper->isReference()) {
+ if (valueWrapper && valueWrapper->isReference()) {
ScopedValue rv(scope, call());
- d()->valueTypeWrapper->writeBack();
+ valueWrapper->writeBack();
return rv->asReturnedValue();
}
}
@@ -2620,7 +2938,8 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
};
if (d()->methodCount != 1) {
- method = ResolveOverloaded(object, d()->methods, d()->methodCount, v4, callData);
+ Q_ASSERT(d()->methodCount > 0);
+ method = resolveOverloaded(object, d()->methods, d()->methodCount, v4, callData);
if (method == nullptr)
return Encode::undefined();
}
@@ -2629,7 +2948,7 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
return doCall([&]() {
ScopedValue rv(scope, Value::undefinedValue());
QQmlV4Function func(callData, rv, v4);
- QQmlV4Function *funcptr = &func;
+ QQmlV4FunctionPtr funcptr = &func;
void *args[] = { nullptr, &funcptr };
object.metacall(QMetaObject::InvokeMetaMethod, method->coreIndex(), args);
@@ -2638,116 +2957,127 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
});
}
- return doCall([&]() { return CallPrecise(object, *method, v4, callData); });
+ return doCall([&]() { return callPrecise(object, *method, v4, callData); });
}
-DEFINE_OBJECT_VTABLE(QObjectMethod);
-
-
-void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject)
+struct ToStringMetaMethod
{
- FunctionObject::init();
- this->metaObject = metaObject;
- constructors = nullptr;
- constructorCount = 0;
-}
+ constexpr int parameterCount() const { return 0; }
+ constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
+ constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
+};
-void Heap::QMetaObjectWrapper::destroy()
+void QObjectMethod::callInternalWithMetaTypes(
+ QObject *thisObject, void **argv, const QMetaType *types, int argc) const
{
- delete[] constructors;
-}
-
-void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
+ 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);
- Scope scope(engine);
- Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<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
-{
+ 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;
+ }
- d()->ensureConstructorsCache();
+ d()->ensureMethodsCache(thisMeta);
- ExecutionEngine *v4 = engine();
- const QMetaObject* mo = d()->metaObject;
- if (d()->constructorCount == 0) {
- return v4->throwTypeError(QLatin1String(mo->className())
- + QLatin1String(" has no invokable constructor"));
+ const QQmlPropertyData *method = d()->methods;
+ if (d()->methodCount != 1) {
+ Q_ASSERT(d()->methodCount > 0);
+ method = resolveOverloaded(d()->methods, d()->methodCount, argv, argc, types);
}
- Scope scope(v4);
- Scoped<QObjectWrapper> object(scope);
- JSCallData cData(nullptr, argv, argc);
- CallData *callData = cData.callData(scope);
+ 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 {
+ 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();
+ }
- const QQmlObjectOrGadget objectOrGadget(mo);
+ // 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());
+ }
+ }
- if (d()->constructorCount == 1) {
- object = CallPrecise(objectOrGadget, d()->constructors[0], v4, callData, QMetaObject::CreateInstance);
- } else if (const QQmlPropertyData *ctor = ResolveOverloaded(
- objectOrGadget, d()->constructors, d()->constructorCount, v4, callData)) {
- object = CallPrecise(objectOrGadget, *ctor, v4, callData, QMetaObject::CreateInstance);
+ if (qobjectPtr) {
+ QQmlData *ddata = QQmlData::get(qobjectPtr, true);
+ if (!ddata->explicitIndestructibleSet) {
+ ddata->indestructible = false;
+ QObjectWrapper::ensureWrapper(v4, qobjectPtr);
+ }
+ }
+ });
}
- Scoped<QMetaObjectWrapper> metaObject(scope, this);
- object->defineDefaultProperty(v4->id_constructor(), metaObject);
- object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
- return object.asReturnedValue();
-
-}
-
-bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
-{
- const QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
- Q_ASSERT(aMetaObject);
- const QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>();
- return bMetaObject && aMetaObject->metaObject() == bMetaObject->metaObject();
}
-DEFINE_OBJECT_VTABLE(QMetaObjectWrapper);
-
+DEFINE_OBJECT_VTABLE(QObjectMethod);
void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
{
@@ -2758,6 +3088,26 @@ void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
DEFINE_OBJECT_VTABLE(QmlSignalHandler);
+ReturnedValue QmlSignalHandler::call(const Value *thisObject, const Value *argv, int argc) const
+{
+ 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());
+ Scoped<QObjectMethod> method(
+ scope, QObjectMethod::create(
+ scope.engine,
+ static_cast<Heap::QObjectWrapper *>(nullptr),
+ signalIndex()));
+
+ return method->call(thisObject, argv, argc);
+}
+
void QmlSignalHandler::initProto(ExecutionEngine *engine)
{
if (engine->signalHandlerPrototype()->d_unchecked())