Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4qobjectwrapper.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6
7#include <private/qjsvalue_p.h>
8#include <private/qjsmanagedvalue_p.h>
9
10#include <private/qqmlbinding_p.h>
11#include <private/qqmlbuiltinfunctions_p.h>
12#include <private/qqmlengine_p.h>
13#include <private/qqmlobjectorgadget_p.h>
14#include <private/qqmlpropertybinding_p.h>
15#include <private/qqmlscriptstring_p.h>
16#include <private/qqmlsignalnames_p.h>
17#include <private/qqmltypewrapper_p.h>
18#include <private/qqmlvaluetypewrapper_p.h>
19#include <private/qqmlvmemetaobject_p.h>
20
21#include <private/qv4arraybuffer_p.h>
22#include <private/qv4arrayobject_p.h>
23#include <private/qv4compileddata_p.h>
24#include <private/qv4dateobject_p.h>
25#include <private/qv4functionobject_p.h>
26#include <private/qv4identifiertable_p.h>
27#include <private/qv4jscall_p.h>
28#include <private/qv4jsonobject_p.h>
29#include <private/qv4lookup_p.h>
30#include <private/qv4mm_p.h>
31#include <private/qv4regexpobject_p.h>
32#include <private/qv4runtime_p.h>
33#include <private/qv4scopedvalue_p.h>
34#include <private/qv4sequenceobject_p.h>
35#include <private/qv4variantobject_p.h>
36
37#include <QtCore/qjsonarray.h>
38#include <QtCore/qjsonobject.h>
39#include <QtCore/qjsonvalue.h>
40#include <QtCore/qloggingcategory.h>
41#include <QtCore/qmetaobject.h>
42#include <QtCore/qqueue.h>
43#include <QtCore/qtimer.h>
44#include <QtCore/qtypes.h>
45#include <QtCore/qvarlengtharray.h>
46
47#include <vector>
48
49#if QT_CONFIG(qml_itemmodel)
50#include <QtCore/qabstractitemmodel.h>
51#endif
52
54
55Q_LOGGING_CATEGORY(lcBuiltinsBindingRemoval, "qt.qml.binding.removal", QtWarningMsg)
56Q_STATIC_LOGGING_CATEGORY(lcObjectConnect, "qt.qml.object.connect", QtWarningMsg)
57Q_STATIC_LOGGING_CATEGORY(lcOverloadResolution, "qt.qml.overloadresolution", QtWarningMsg)
58Q_STATIC_LOGGING_CATEGORY(lcMethodBehavior, "qt.qml.method.behavior")
59Q_STATIC_LOGGING_CATEGORY(lcSignalHandler, "qt.qml.signalhandler")
60
61// The code in this file does not violate strict aliasing, but GCC thinks it does
62// so turn off the warnings for us to have a clean build
63QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
64
65using namespace Qt::StringLiterals;
66
67namespace QV4 {
68
70{
72 if (v4) {
73 Scope scope(v4);
75 if (method)
77 }
78
79 return std::make_pair((QObject *)nullptr, -1);
80}
81
82static std::pair<QObject *, int> extractQtSignal(const Value &value)
83{
84 if (value.isObject()) {
85 ExecutionEngine *v4 = value.as<Object>()->engine();
86 Scope scope(v4);
87 ScopedFunctionObject function(scope, value);
88 if (function)
89 return QObjectMethod::extractQtMethod(function);
90
91 Scoped<QmlSignalHandler> handler(scope, value);
92 if (handler)
93 return std::make_pair(handler->object(), handler->signalIndex());
94 }
95
96 return std::make_pair((QObject *)nullptr, -1);
97}
98
100 ExecutionEngine *v4,
101 const QQmlPropertyData &property)
102{
103 Heap::ReferenceObject::Flags flags = Heap::ReferenceObject::NoFlag;
104 if (CppStackFrame *stackFrame = v4->currentStackFrame) {
105 if (stackFrame->v4Function->executableCompilationUnit()->valueTypesAreCopied())
106 flags |= Heap::ReferenceObject::EnforcesLocation;
107 }
108
109 if (property.isWritable())
110 flags |= Heap::ReferenceObject::CanWriteBack;
111
112 return flags;
113}
114
118{
120 Scope scope(v4);
121
123 if (property.isQObject()) {
124 QObject *rv = nullptr;
127 return QObjectWrapper::wrapConst(v4, rv);
128 else
129 return QObjectWrapper::wrap(v4, rv);
130 }
131
134
135 const auto encodeSimple = [&](auto v) {
137 return Encode(v);
138 };
139
140 const auto encodeInt = [&](auto v) {
142 return Encode(int(v));
143 };
144
145 const auto encodeDouble = [&](auto v) {
147 return Encode(double(v));
148 };
149
150 const auto encodeDate = [&](auto v) {
152 return Encode(v4->newDateObject(
154 };
155
156 const auto encodeString = [&](auto v) {
158 return v4->newString(v)->asReturnedValue();
159 };
160
161 const auto encodeSequence = [&](QMetaSequence metaSequence) {
162 // Pass nullptr as data. It's lazy-loaded.
164 v4, propMetaType, metaSequence, nullptr,
166 };
167
168
171 case QMetaType::Void:
172 return Encode::undefined();
173 case QMetaType::Nullptr:
174 case QMetaType::VoidStar:
175 return Encode::null();
176 case QMetaType::Int:
177 return encodeSimple(int());
178 case QMetaType::Bool:
179 return encodeSimple(bool());
180 case QMetaType::QString:
181 return encodeString(QString());
182 case QMetaType::QByteArray: {
186 }
187 case QMetaType::QChar:
188 return encodeString(QChar());
189 case QMetaType::Char16:
190 return encodeString(char16_t());
191 case QMetaType::UInt:
192 return encodeSimple(uint());
193 case QMetaType::Float:
194 return encodeSimple(float());
195 case QMetaType::Double:
196 return encodeSimple(double());
197 case QMetaType::Short:
198 return encodeInt(short());
199 case QMetaType::UShort:
200 return encodeInt(ushort());
201 case QMetaType::Char:
202 return encodeInt(char());
203 case QMetaType::UChar:
204 return encodeInt(uchar());
205 case QMetaType::SChar:
206 return encodeInt(qint8());
207 case QMetaType::Long:
208 return encodeDouble(long());
209 case QMetaType::ULong:
210 return encodeDouble(ulong());
211 case QMetaType::LongLong:
212 return encodeDouble(qlonglong());
213 case QMetaType::ULongLong:
214 return encodeDouble(qulonglong());
215 case QMetaType::QDateTime:
216 return encodeDate(QDateTime());
217 case QMetaType::QDate:
218 return encodeDate(QDate());
219 case QMetaType::QTime:
220 return encodeDate(QTime());
221#if QT_CONFIG(regularexpression)
225 return Encode(v4->newRegExpObject(v));
226 }
227#endif
228 case QMetaType::QVariantMap: {
231 return scope.engine->fromData(
233 }
234 case QMetaType::QVariantHash: {
237 return scope.engine->fromData(
239 }
240 case QMetaType::QJsonValue: {
243 return QV4::JsonObject::fromJsonValue(v4, v);
244 }
245 case QMetaType::QJsonObject: {
248 return QV4::JsonObject::fromJsonObject(v4, v);
249 }
250 case QMetaType::QJsonArray:
256 case QMetaType::QUrl: {
257 // ### Qt7: We really want this to be a JS URL object, but that would break things.
258 QUrl v;
261 }
262 case QMetaType::QPixmap:
263 case QMetaType::QImage: {
264 // Scarce value types
268 }
269 default:
270 break;
271 }
272
274 QJSValue v;
277 }
278
279 if (property.isQVariant()) {
280 // We have to read the property even if it's a lazy-loaded reference object.
281 // Without reading it, we wouldn't know its inner type.
282 QVariant v;
284 return scope.engine->fromVariant(
287 }
288
289 if (!propMetaType.isValid()) {
291 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
292 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
293 return Encode::undefined();
294 }
295
296 // TODO: For historical reasons we don't enforce locations for reference objects here.
297 // Once we do, we can eager load and use the fromVariant() below.
298 // Then the extra checks for value types and sequences can be dropped.
299
303 // Lazy loaded value type reference. Pass nullptr as data.
307 }
308 }
309
310 // See if it's a sequence type.
314
317 return scope.engine->fromVariant(
319}
320
326
333
348
352{
354
356 if (property->isVMEFunction()) {
359 return vmemo->vmeMethod(property->coreIndex());
360 } else if (property->isV4Function()) {
361 return QObjectMethod::create(
362 engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
363 } else if (property->isSignalHandler()) {
367 } else {
368 return QObjectMethod::create(
369 engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
370 }
371 }
372
374
375 if (ep && ep->propertyCapture && !property->isConstant()) {
380 }
381 }
382
383 if (property->isVarProperty()) {
387 } else {
389 }
390}
391
403
405 ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
406{
407 int index = 0;
408 if (name->equals(v4->id_destroy()))
410 else if (name->equals(v4->id_toString()))
412 else
413 return OptionalReturnedValue();
414
415 if (hasProperty)
416 *hasProperty = true;
418}
419
421 ExecutionEngine *v4, String *name, const QQmlRefPointer<QQmlContextData> &qmlContext,
422 QObject *qobj, bool *hasProperty = nullptr)
423{
424 if (!qmlContext || !qmlContext->imports())
425 return OptionalReturnedValue();
426
427 if (hasProperty)
428 *hasProperty = true;
429
430 if (QQmlTypeLoader *typeLoader = v4->typeLoader()) {
431 QQmlTypeNameCache::Result r = qmlContext->imports()->query(name, typeLoader);
432
433 if (!r.isValid())
434 return OptionalReturnedValue();
435
436 if (r.scriptIndex != -1) {
437 return OptionalReturnedValue(Encode::undefined());
438 } else if (r.type.isValid()) {
439 return OptionalReturnedValue(
440 QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
441 } else if (r.importNamespace) {
442 return OptionalReturnedValue(QQmlTypeWrapper::create(
443 v4, qobj, qmlContext->imports(), r.importNamespace,
444 Heap::QQmlTypeWrapper::ExcludeEnums));
445 }
446 Q_UNREACHABLE_RETURN(OptionalReturnedValue());
447 } else {
448 return OptionalReturnedValue();
449 }
450}
451
455{
456 // Keep this code in sync with ::virtualResolveLookupGetter
457
458 if (QQmlData::wasDeleted(d()->object())) {
459 if (hasProperty)
460 *hasProperty = false;
461 return Encode::undefined();
462 }
463
465
467 return *methodValue;
468
471
472 if (!result) {
473 // Check for attached properties
477 return *importProperty;
478 }
479 return Object::virtualGet(this, name->propertyKey(), this, hasProperty);
480 }
481
482 QQmlData *ddata = QQmlData::get(d()->object(), false);
483
484 if ((flags & CheckRevision) && result->hasRevision()) {
486 if (hasProperty)
487 *hasProperty = false;
488 return Encode::undefined();
489 }
490 }
491
492 if (hasProperty)
493 *hasProperty = true;
494
495 return getProperty(v4, d(), d()->object(), result, flags);
496}
497
502{
503 if (QQmlData::wasDeleted(object)) {
504 if (hasProperty)
505 *hasProperty = false;
506 return Encode::null();
507 }
508
510 return *methodValue;
511
512 QQmlData *ddata = QQmlData::get(object, false);
515
516 if (result) {
520 if (hasProperty)
521 *hasProperty = false;
522 return Encode::undefined();
523 }
524 }
525
526 if (hasProperty)
527 *hasProperty = true;
528
529 if (property && result != &local)
530 *property = result;
531
533 } else {
534 // Check if this object is already wrapped.
535 if (!ddata || (ddata->jsWrapper.isUndefined() &&
536 (ddata->jsEngineId == 0 || // Nobody owns the QObject
537 !ddata->hasTaintedV4Object))) { // Someone else has used the QObject, but it isn't tainted
538
539 // Not wrapped. Last chance: try query QObjectWrapper's prototype.
540 // If it can't handle this, then there is no point
541 // to wrap the QObject just to look at an empty set of JS props.
543 return proto->get(name, hasProperty);
544 }
545 }
546
547 // If we get here, we must already be wrapped (which implies a ddata).
548 // There's no point wrapping again, as there wouldn't be any new props.
550
553 if (!rewrapped) {
554 if (hasProperty)
555 *hasProperty = false;
556 return Encode::null();
557 }
559}
560
561
583
584/*!
585 \internal
586 If an QObjectWrapper is created via wrap, then it needs to be stored somewhere.
587 Otherwise, the garbage collector will immediately collect it if it is already
588 past the "mark QObjectWrapper's" phase (note that QObjectWrapper are marked
589 by iterating over a list of all QObjectWrapper, and then checking if the
590 wrapper fulfills some conditions).
591 However, sometimes we don't really want to keep a reference to the wrapper,
592 but just want to make sure that it exists (and we know that the wrapper
593 already fulfills the conditions to be kept alive). Then ensureWrapper
594 can be used, which creates the wrapper and ensures that it is also
595 marked.
596 */
605
608 const QQmlPropertyData *property, const Value &value)
609{
610 if (!property->isWritable() && !property->isQList()) {
611 QString error = QLatin1String("Cannot assign to read-only property \"") +
614 return;
615 }
616
619 if (f->as<QQmlTypeWrapper>()) {
620 // Ignore. It's probably a singleton or an attached type.
621 } else if (!f->isBinding()) {
622 const bool isAliasToAllowed = [&]() {
623 if (property->isAlias()) {
635 } else {
636 return false;
637 }
638 }();
641 // assigning a JS function to a non var or QJSValue property or is not allowed.
642 QString error = QLatin1String("Cannot assign JavaScript function to ");
643 if (!QMetaType(property->propType()).name())
644 error += QLatin1String("[unknown property type]");
645 else
648 return;
649 }
650 } else {
651
656
657 // binding assignment.
658 if (property->acceptsQBinding()) {
659 const QQmlPropertyIndex idx(property->coreIndex(), /*not a value type*/-1);
662 if (f->isBoundFunction()) {
663 auto boundFunction = static_cast<BoundFunction *>(f.getPointer());
666 } else {
669
670 }
672 void *argv = {&bindable};
673 // indirect metacall in case interceptors are installed
676 if (!ok) {
677 auto error = QStringLiteral("Failed to set binding on %1::%2.").
680 }
681 } else {
684 if (f->isBoundFunction())
686 newBinding->setTarget(object, *property, nullptr);
688 }
689 return;
690 }
691 }
692
696 binding && !binding->isSticky()) {
697 const auto stackFrame = engine->currentStackFrame;
698 switch (binding->kind()) {
700 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
702 "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
706 break;
707 }
711 "Overwriting binding on %s::%s at %s:%d",
714 break;
715 }
716 }
717 }
718 }
721
722 if (property->isVarProperty()) {
723 // allow assignment of "special" values (null, undefined, function) to var properties
727 return;
728 }
729
730#define PROPERTY_STORE(cpptype, value)
731 cpptype o = value;
732 int status = -1;
733 int flags = 0;
734 void *argv[] = { &o, 0, &status, &flags };
735 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
736
738 // functions are already handled, except for the QJSValue case
742
743 if (value.isNull() && property->isQObject()) {
744 PROPERTY_STORE(QObject*, nullptr);
745 } else if (value.isUndefined() && property->isResettable()) {
746 void *a[] = { nullptr };
748 } else if (value.isUndefined() && propType == QMetaType::fromType<QVariant>()) {
750 } else if (value.isUndefined() && propType == QMetaType::fromType<QJsonValue>()) {
752 } else if (propType == QMetaType::fromType<QJSValue>()) {
755 QString error = QLatin1String("Cannot assign [undefined] to ");
756 if (!propType.name())
757 error += QLatin1String("[unknown property type]");
758 else
761 return;
762 } else if (propType == QMetaType::fromType<int>() && value.isNumber()) {
764 } else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) {
766 } else if (propType == QMetaType::fromType<float>() && value.isNumber()) {
767 PROPERTY_STORE(float, float(value.asDouble()));
768 } else if (propType == QMetaType::fromType<double>() && value.isNumber()) {
769 PROPERTY_STORE(double, double(value.asDouble()));
770 } else if (propType == QMetaType::fromType<QString>() && value.isString()) {
772 } else if (property->isVarProperty()) {
777 && (value.isUndefined() || value.isPrimitive())) {
778 QQmlScriptString ss(value.toQStringNoThrow(), nullptr /* context */, object);
779 if (value.isNumber()) {
781 ss.d->isNumberLiteral = true;
782 } else if (value.isString()) {
784 ss.d->isStringLiteral = true;
785 }
787 } else {
788 QVariant v;
791 else
793
796 const char *valueType = (v.userType() == QMetaType::UnknownType)
797 ? "an unknown type"
798 : QMetaType(v.userType()).name();
799
800 const char *targetTypeName = propType.name();
801 if (!targetTypeName)
802 targetTypeName = "an unregistered type";
803
804 QString error = QLatin1String("Cannot assign ") +
806 QLatin1String(" to ") +
809 return;
810 }
811 }
812}
813
815{
817
818 QQmlData *ddata = QQmlData::get(object, true);
819 if (!ddata)
820 return Encode::undefined();
821
823
824 if (ddata->jsWrapper.isUndefined() &&
825 (ddata->jsEngineId == engine->m_engineId || // We own the QObject
826 ddata->jsEngineId == 0 || // No one owns the QObject
827 !ddata->hasTaintedV4Object)) { // Someone else has used the QObject, but it isn't tainted
828
832 return rv->asReturnedValue();
833
834 } else {
835 // If this object is tainted, we have to check to see if it is in our
836 // tainted object list
840
841 // If our tainted handle doesn't exist or has been collected, and there isn't
842 // a handle in the ddata, we can assume ownership of the ddata->jsWrapper
847 return result->asReturnedValue();
848 }
849
850 if (!alternateWrapper) {
856 }
857
859 }
860}
861
863{
864 const QObject *constObject = object;
865
866 QQmlData *ddata = QQmlData::get(object, true);
867
872
873 if (!constWrapper) {
879 ddata->hasConstWrapper = true;
880 }
881
883}
884
902
907
909{
910 Q_ASSERT(propertyIndex < 0xffff);
912
914 return;
915 QQmlData *ddata = QQmlData::get(object, /*create*/false);
916 if (!ddata)
917 return;
918
921 Q_ASSERT(property); // We resolved this property earlier, so it better exist!
923}
924
926{
928 const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a);
931
932 // We can have a const and a non-const wrapper for the same object.
935}
936
947
959
961{
962 if (!id.isString())
963 return Object::virtualPut(m, id, value, receiver);
964
965 Scope scope(m);
966 QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
968
969 if (that->internalClass()->isFrozen()) {
970 QString error = QLatin1String("Cannot assign to property \"") +
971 name->toQString() + QLatin1String("\" of read-only object");
973 return false;
974 }
975
977 return false;
978
982 // Types created by QML are not extensible at run-time, but for other QObjects we can store them
983 // as regular JavaScript properties, like on JavaScript objects.
984 if (ddata && ddata->context) {
985 QString error = QLatin1String("Cannot assign to non-existent property \"") +
986 name->toQString() + QLatin1Char('\"');
988 return false;
989 } else {
990 return Object::virtualPut(m, id, value, receiver);
991 }
992 }
993
994 return true;
995}
996
998{
999 if (id.isString()) {
1000 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
1001 const QObject *thatObject = that->d()->object();
1003 Scope scope(m);
1009 if (p) {
1010 // ### probably not the fastest implementation
1011 bool hasProperty;
1014 }
1015 return Attr_Data;
1016 }
1017 }
1018 }
1019
1020 return Object::virtualGetOwnProperty(m, id, p);
1021}
1022
1024{
1027 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
1028
1029private:
1030 QSet<QByteArray> m_alreadySeen;
1031};
1032
1033PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
1034{
1035 // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
1036 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
1037 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
1038 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
1039
1040 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(o);
1041
1042 QObject *thatObject = that->d()->object();
1043 if (thatObject && !QQmlData::wasDeleted(thatObject)) {
1044 const QMetaObject *mo = thatObject->metaObject();
1045 // These indices don't apply to gadgets, so don't block them.
1046 const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
1047 const int propertyCount = mo->propertyCount();
1048 if (propertyIndex < propertyCount) {
1049 ExecutionEngine *thatEngine = that->engine();
1050 Scope scope(thatEngine);
1051 const QMetaProperty property = mo->property(propertyIndex);
1052 ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name())));
1053 ++propertyIndex;
1054 if (attrs)
1055 *attrs= Attr_Data;
1056 if (pd) {
1057 QQmlPropertyData local;
1058 local.load(property);
1059 pd->value = that->getProperty(
1060 thatEngine, that->d(), thatObject, &local,
1061 QObjectWrapper::AttachMethods);
1062 }
1063 return propName->toPropertyKey();
1064 }
1065 const int methodCount = mo->methodCount();
1066 while (propertyIndex < propertyCount + methodCount) {
1067 Q_ASSERT(propertyIndex >= propertyCount);
1068 int index = propertyIndex - propertyCount;
1069 const QMetaMethod method = mo->method(index);
1070 ++propertyIndex;
1071 if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
1072 continue;
1073 // filter out duplicates due to overloads:
1074 if (m_alreadySeen.contains(method.name()))
1075 continue;
1076 else
1077 m_alreadySeen.insert(method.name());
1078 ExecutionEngine *thatEngine = that->engine();
1079 Scope scope(thatEngine);
1080 ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name())));
1081 if (attrs)
1082 *attrs = Attr_Data;
1083 if (pd) {
1084 QQmlPropertyData local;
1085 local.load(method);
1086 pd->value = that->getProperty(
1087 thatEngine, that->d(), thatObject, &local,
1088 QObjectWrapper::AttachMethods);
1089 }
1090 return methodName->toPropertyKey();
1091 }
1092 }
1093
1094 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1095}
1096
1102
1104{
1105 // Keep this code in sync with ::getQmlProperty
1108 if (!id.isString())
1111
1112 const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
1115
1116 QObject * const qobj = This->d()->object();
1117
1118 if (QQmlData::wasDeleted(qobj))
1119 return Encode::undefined();
1120
1121 QQmlData *ddata = QQmlData::get(qobj, false);
1125 lookup, ddata ? ddata : QQmlData::get(qobj, true), nullptr, This, method->d());
1127 return method.asReturnedValue();
1128 }
1129
1130 if (!ddata || !ddata->propertyCache) {
1137 return result->asReturnedValue();
1138 }
1140
1141 if (!property) {
1142 // Check for attached properties
1143 if (name->startsWithUpper()) {
1145 return *importProperty;
1146 }
1148 }
1149
1150 if (property->isFunction()
1151 && !property->isVarProperty()
1152 && !property->isVMEFunction() // Handled by QObjectLookup
1153 && !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too
1154 QV4::Heap::QObjectMethod *method = nullptr;
1157 return lookup->getter(engine, *object);
1158 }
1159
1161
1163 return lookup->getter(engine, *object);
1164}
1165
1171
1173{
1176
1177 if (QObject *qObject = wrapper->object())
1178 return QMetaObject::metacall(qObject, call, index, a);
1179
1180 return 0;
1181}
1182
1185{
1186 if (!metaObject)
1187 return QLatin1String("null");
1188
1189 if (!object)
1190 return QString::fromUtf8(metaObject->className()) + QLatin1String("(0x0)");
1191
1192 const int id = metaObject->indexOfMethod("toString()");
1193 if (id >= 0) {
1199 return result.toString();
1202 return value->toQString();
1203 }
1204
1207 QLatin1String("(0x") + QString::number(quintptr(object), 16);
1209 if (!objectName.isEmpty())
1210 result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
1211 result += QLatin1Char(')');
1212 return result;
1213}
1214
1216{
1221
1225
1226 static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
1227 {
1228 switch (which) {
1229 case Destroy: {
1230 delete static_cast<QObjectSlotDispatcher*>(this_);
1231 }
1232 break;
1233 case Call: {
1234 if (QQmlData::wasDeleted(receiver))
1235 break;
1236
1237 QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
1238 ExecutionEngine *v4 = This->function.engine();
1239 // Might be that we're still connected to a signal that's emitted long
1240 // after the engine died. We don't track connections in a global list, so
1241 // we need this safeguard.
1242 if (!v4)
1243 break;
1244
1245 QQmlMetaObject::ArgTypeStorage<9> storage;
1246 QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr);
1247
1248 const qsizetype argCount = std::min(storage.size(), This->maxNumArguments);
1249
1250 Scope scope(v4);
1251 ScopedFunctionObject f(scope, This->function.value());
1252
1253 JSCallArguments jsCallData(scope, argCount);
1254 *jsCallData.thisObject = This->thisObject.isUndefined()
1255 ? v4->globalObject->asReturnedValue()
1256 : This->thisObject.value();
1257 for (qsizetype ii = 0; ii < argCount; ++ii) {
1258 QMetaType type = storage[ii];
1259 if (type == QMetaType::fromType<QVariant>()) {
1260 jsCallData.args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1261 } else {
1262 jsCallData.args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
1263 }
1264 }
1265
1266 f->call(jsCallData);
1267 if (scope.hasException()) {
1268 QQmlError error = v4->catchExceptionAsQmlError();
1269 if (error.description().isEmpty()) {
1270 ScopedString name(scope, f->name());
1271 error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString()));
1272 }
1273 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
1274 QQmlEnginePrivate::get(qmlEngine)->warning(error);
1275 } else {
1276 QMessageLogger(error.url().toString().toLatin1().constData(),
1277 error.line(), nullptr).warning().noquote()
1278 << error.toString();
1279 }
1280 }
1281 }
1282 break;
1283 case Compare: {
1284 QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
1285 if (connection->function.isUndefined()) {
1286 *ret = false;
1287 return;
1288 }
1289
1290 // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
1291 // for the new-style QObject::connect. Here we use the engine pointer as sentinel
1292 // to distinguish those type of QSlotObjectBase connections from our QML connections.
1293 ExecutionEngine *v4 = reinterpret_cast<ExecutionEngine*>(metaArgs[0]);
1294 if (v4 != connection->function.engine()) {
1295 *ret = false;
1296 return;
1297 }
1298
1299 Scope scope(v4);
1300 ScopedValue function(scope, *reinterpret_cast<Value*>(metaArgs[1]));
1301 ScopedValue thisObject(scope, *reinterpret_cast<Value*>(metaArgs[2]));
1302 QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
1303 int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
1304
1305 if (slotIndexToDisconnect != -1) {
1306 // This is a QObject function wrapper
1307 if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1308 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1309
1310 ScopedFunctionObject f(scope, connection->function.value());
1311 std::pair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f);
1312 if (connectedFunctionData.first == receiverToDisconnect &&
1313 connectedFunctionData.second == slotIndexToDisconnect) {
1314 *ret = true;
1315 return;
1316 }
1317 }
1318 } else {
1319 // This is a normal JS function
1320 if (RuntimeHelpers::strictEqual(*connection->function.valueRef(), function) &&
1321 connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1322 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1323 *ret = true;
1324 return;
1325 }
1326 }
1327
1328 *ret = false;
1329 }
1330 break;
1331 case NumOperations:
1332 break;
1333 }
1334 };
1335};
1336
1338{
1339 Scope scope(b);
1340
1341 if (argc == 0)
1342 THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
1343
1346 int signalIndex = signalInfo.second; // in method range, not signal range!
1347
1348 if (signalIndex < 0)
1349 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1350
1351 if (!signalObject)
1352 THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1353
1356 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1357
1360
1361 if (argc == 1) {
1362 f = argv[0];
1363 } else if (argc >= 2) {
1364 object = argv[0];
1365 f = argv[1];
1366 }
1367
1368 if (!f)
1369 THROW_GENERIC_ERROR("Function.prototype.connect: target is not a function");
1370
1371 if (!object->isUndefined() && !object->isObject())
1372 THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object");
1373
1376
1379
1383 }
1384 }
1385
1386 std::pair<QObject *, int> functionData = QObjectMethod::extractQtMethod(f); // align with disconnect
1387 QObject *receiver = nullptr;
1388
1389 if (functionData.first)
1391 else if (auto qobjectWrapper = object->as<QV4::QObjectWrapper>())
1393 else if (auto typeWrapper = object->as<QV4::QQmlTypeWrapper>())
1395
1396 if (receiver) {
1397 if (functionData.second == -1) {
1399 } else {
1400 // This means we are connecting to QObjectMethod which complains about extra arguments.
1401 Heap::QObjectMethod *d = static_cast<Heap::QObjectMethod *>(f->d());
1404 [](int a, const QQmlPropertyData &b) {
1405 return std::max(a, b.metaMethod().parameterCount());
1406 });
1407 }
1408
1410 } else {
1413 "Could not find receiver of the connection, using sender as receiver. Disconnect "
1414 "explicitly (or delete the sender) to make sure the connection is removed.");
1416 }
1417
1419}
1420
1422{
1423 Scope scope(b);
1424
1425 if (argc == 0)
1426 THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
1427
1431
1432 if (signalIndex == -1)
1433 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1434
1435 if (!signalObject)
1436 THROW_GENERIC_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1437
1439 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1440
1443
1444 if (argc == 1) {
1445 functionValue = argv[0];
1446 } else if (argc >= 2) {
1448 functionValue = argv[1];
1449 }
1450
1451 if (!functionValue)
1452 THROW_GENERIC_ERROR("Function.prototype.disconnect: target is not a function");
1453
1455 THROW_GENERIC_ERROR("Function.prototype.disconnect: target this is not an object");
1456
1458
1459 void *a[] = {
1460 scope.engine,
1465 };
1466
1467 QObject *receiver = nullptr;
1468
1469 if (functionData.first)
1473 else if (auto typeWrapper = functionThisValue->as<QV4::QQmlTypeWrapper>())
1475
1476 if (receiver) {
1478 reinterpret_cast<void **>(&a));
1479 } else {
1481 reinterpret_cast<void **>(&a));
1482 }
1483
1485}
1486
1487static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack)
1488{
1489 QQueue<QObject *> queue;
1490 queue.append(parent->children());
1491
1492 while (!queue.isEmpty()) {
1493 QObject *child = queue.dequeue();
1494 if (!child)
1495 continue;
1496 QObjectWrapper::markWrapper(child, markStack);
1497 queue.append(child->children());
1498 }
1499}
1500
1502{
1503 QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
1504
1505 if (QObject *o = This->object()) {
1506 if (QQmlData *ddata = QQmlData::get(o)) {
1507 if (ddata->hasVMEMetaObject) {
1509 = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) {
1510 vme->mark(markStack);
1511 }
1512 }
1513
1514 // mark the const wrapper if our engine has interacted with it at some point
1517
1519 scope,
1521 static_cast<const QObject *>(o)));
1522
1523 if (constWrapper) {
1524 if (This == constWrapper->d()) {
1525 // We've got the const wrapper. Also mark the non-const one
1528 else
1530 } else {
1531 // We've got the non-const wrapper. Also mark the const one.
1533 }
1534 }
1535 }
1536 }
1537
1538 // Children usually don't need to be marked, the gc keeps them alive.
1539 // But in the rare case of a "floating" QObject without a parent that
1540 // _gets_ marked (we've been called here!) then we also need to
1541 // propagate the marking down to the children recursively.
1542 if (!o->parent())
1544 }
1545
1547}
1548
1550{
1551 Heap::QObjectWrapper *h = d();
1553
1554 if (QObject *o = h->object()) {
1555 QQmlData *ddata = QQmlData::get(o, false);
1556 if (ddata) {
1557 if (!o->parent() && !ddata->indestructible) {
1558 if (ddata && ddata->ownContext) {
1562 ddata->context = nullptr;
1563 }
1564
1565 // This object is notionally destroyed now. It might still live until the next
1566 // event loop iteration, but it won't need its connections, CU, or deferredData
1567 // anymore.
1568
1569 ddata->isQueuedForDeletion = true;
1572
1575
1576 if (lastCall)
1577 delete o;
1578 else
1579 o->deleteLater();
1580 } else {
1581 // If the object is C++-owned, we still have to release the weak reference we have
1582 // to it.
1586 }
1587 }
1588 }
1589
1590 h->destroy();
1591}
1592
1593
1595
1596namespace {
1597
1598template<typename... Types>
1599constexpr std::size_t MaxSizeOfN = (std::max)({sizeof(Types)...});
1600
1601struct CallArgument {
1602 Q_DISABLE_COPY_MOVE(CallArgument);
1603
1604 CallArgument() = default;
1605 ~CallArgument() { cleanup(); }
1606
1607 inline void *dataPtr();
1608
1609 inline void initAsType(QMetaType type);
1610 inline bool fromValue(QMetaType type, ExecutionEngine *, const Value &);
1611 inline ReturnedValue toValue(ExecutionEngine *);
1612
1613private:
1614 // QVariantWrappedType denotes that we're storing a QVariant, but we mean
1615 // the type inside the QVariant, not QVariant itself.
1616 enum { QVariantWrappedType = -1 };
1617
1618 inline void cleanup();
1619
1620 template <class T, class M>
1621 bool fromContainerValue(const Value &object, M CallArgument::*member);
1622
1623 union {
1624 float floatValue;
1625 double doubleValue;
1626 quint32 intValue;
1627 bool boolValue;
1628 QObject *qobjectPtr;
1629 std::vector<int> *stdVectorIntPtr;
1630 std::vector<qreal> *stdVectorRealPtr;
1631 std::vector<bool> *stdVectorBoolPtr;
1632 std::vector<QString> *stdVectorQStringPtr;
1633 std::vector<QUrl> *stdVectorQUrlPtr;
1634#if QT_CONFIG(qml_itemmodel)
1635 std::vector<QModelIndex> *stdVectorQModelIndexPtr;
1636#endif
1637
1638 char allocData[MaxSizeOfN<QVariant,
1639 QString,
1640 QList<QObject *>,
1641 QJSValue,
1642 QJSManagedValue,
1643 QJsonArray,
1644 QJsonObject,
1645 QJsonValue>];
1646 qint64 q_for_alignment;
1647 };
1648
1649 // Pointers to allocData
1650 union {
1651 QString *qstringPtr;
1652 QByteArray *qbyteArrayPtr;
1653 QVariant *qvariantPtr;
1654 QList<QObject *> *qlistPtr;
1655 QJSValue *qjsValuePtr;
1656 QJSManagedValue *qjsManagedValuePtr;
1657 QJsonArray *jsonArrayPtr;
1658 QJsonObject *jsonObjectPtr;
1659 QJsonValue *jsonValuePtr;
1660 };
1661
1662 int type = QMetaType::UnknownType;
1663};
1664}
1665
1666static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMetaType returnType, int argCount,
1667 const QMetaType *argTypes, ExecutionEngine *engine, CallData *callArgs,
1668 QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
1669{
1670 if (argCount > 0) {
1671 // Convert all arguments.
1672 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1673 args[0].initAsType(returnType);
1674 for (int ii = 0; ii < argCount; ++ii) {
1675 if (!args[ii + 1].fromValue(argTypes[ii], engine,
1676 callArgs->args[ii].asValue<Value>())) {
1677 qWarning() << QString::fromLatin1("Could not convert argument %1 from %2 to %3")
1678 .arg(ii).arg(callArgs->args[ii].asValue<Value>().toQStringNoThrow()).arg(argTypes[ii].name());
1679 const StackTrace stack = engine->stackTrace();
1680 for (const StackFrame &frame : stack) {
1681 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source
1682 + (frame.line > 0
1683 ? (QLatin1Char(':') + QString::number(frame.line))
1684 : QString());
1685
1686 }
1687
1688 const bool is_signal =
1689 object.metaObject()->method(index).methodType() == QMetaMethod::Signal;
1690 if (is_signal) {
1691 qWarning() << "Passing incompatible arguments to signals is not supported.";
1692 } else {
1693 return engine->throwTypeError(
1694 QLatin1String("Passing incompatible arguments to C++ functions from "
1695 "JavaScript is not allowed."));
1696 }
1697 }
1698 }
1699 QVarLengthArray<void *, 9> argData(args.size());
1700 for (int ii = 0; ii < args.size(); ++ii)
1701 argData[ii] = args[ii].dataPtr();
1702
1703 object.metacall(callType, index, argData.data());
1704
1705 return args[0].toValue(engine);
1706
1707 } else if (returnType != QMetaType::fromType<void>()) {
1708
1709 CallArgument arg;
1710 arg.initAsType(returnType);
1711
1712 void *args[] = { arg.dataPtr() };
1713
1714 object.metacall(callType, index, args);
1715
1716 return arg.toValue(engine);
1717
1718 } else {
1719
1720 void *args[] = { nullptr };
1721 object.metacall(callType, index, args);
1722 return Encode::undefined();
1723
1724 }
1725}
1726
1727template<typename Retrieve>
1728int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
1729 if (conversionMetaType == QMetaType::fromType<QVariant>())
1730 return 0;
1731
1732 const QMetaType type = retrieve();
1733 if (type == conversionMetaType)
1734 return 0;
1735
1736 if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
1737 if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject))
1738 return 1;
1739 }
1740
1741 if (QMetaType::canConvert(type, conversionMetaType)) {
1742 if (conversionMetaType == QMetaType::fromType<QJSValue>()
1743 || conversionMetaType == QMetaType::fromType<double>()
1744 || conversionMetaType == QMetaType::fromType<QString>()) {
1745 // Unspecific conversions receive lower score. You can convert anything
1746 // to QString or double via toString() and valueOf(), respectively.
1747 // And anything can be wrapped into QJSValue, but that's inefficient.
1748 return 6;
1749 }
1750
1751 // We have an explicitly defined conversion method to a non-boring type.
1752 return 5;
1753 }
1754
1755 return 10;
1756};
1757
1758/*
1759 Returns the match score for converting \a actual to be of type \a conversionType. A
1760 zero score means "perfect match" whereas a higher score is worse.
1761
1762 The conversion table is copied out of the \l QScript::callQtMethod() function.
1763*/
1764static int MatchScore(const Value &actual, QMetaType conversionMetaType)
1765{
1766 const int conversionType = conversionMetaType.id();
1767 const auto convertibleScore = [&](QMetaType actualType) {
1768 // There are a number of things we can do in JavaScript to subvert this, but
1769 // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
1770 if (!QMetaType::canConvert(actualType, conversionMetaType))
1771 return 10;
1772
1773 // You can convert anything to QJSValue, but that's inefficient.
1774 // If we have a better option, we should use it.
1775 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1776 return 9;
1777
1778 // You can also convert anything to QVariant, but that's also suboptimal.
1779 // You can convert anything to string or double via toString() and valueOf().
1780 // Those are also rather unspecific.
1781 switch (conversionType) {
1782 case QMetaType::QVariant:
1783 case QMetaType::Double:
1784 case QMetaType::QString:
1785 return 9;
1786 default:
1787 break;
1788 }
1789
1790 // We have an explicitly defined conversion method to a non-boring type.
1791 return 8;
1792 };
1793
1794 if (actual.isNumber()) {
1795 switch (conversionType) {
1796 case QMetaType::Double:
1797 return 0;
1798 case QMetaType::Float:
1799 return 1;
1800 case QMetaType::LongLong:
1801 case QMetaType::ULongLong:
1802 return 2;
1803 case QMetaType::Long:
1804 case QMetaType::ULong:
1805 return 3;
1806 case QMetaType::Int:
1807 case QMetaType::UInt:
1808 return 4;
1809 case QMetaType::Short:
1810 case QMetaType::UShort:
1811 return 5;
1812 break;
1813 case QMetaType::Char:
1814 case QMetaType::UChar:
1815 return 6;
1816 case QMetaType::QJsonValue:
1817 return 5;
1818 default:
1819 return convertibleScore(actual.isInteger()
1820 ? QMetaType::fromType<int>()
1821 : QMetaType::fromType<double>());
1822 }
1823 } else if (actual.isString()) {
1824 switch (conversionType) {
1825 case QMetaType::QString:
1826 return 0;
1827 case QMetaType::QJsonValue:
1828 return 5;
1829 case QMetaType::QUrl:
1830 return 6; // we like to convert strings to URLs in QML
1831 case QMetaType::Double:
1832 case QMetaType::Float:
1833 case QMetaType::LongLong:
1834 case QMetaType::ULongLong:
1835 case QMetaType::Int:
1836 case QMetaType::UInt:
1837 case QMetaType::Short:
1838 case QMetaType::UShort:
1839 case QMetaType::Char:
1840 case QMetaType::UChar:
1841 // QMetaType can natively convert strings to numbers.
1842 // However, in the general case it's of course extremely lossy.
1843 return 10;
1844 default:
1845 return convertibleScore(QMetaType::fromType<QString>());
1846 }
1847 } else if (actual.isBoolean()) {
1848 switch (conversionType) {
1849 case QMetaType::Bool:
1850 return 0;
1851 case QMetaType::QJsonValue:
1852 return 5;
1853 default:
1854 return convertibleScore(QMetaType::fromType<bool>());
1855 }
1856 } else if (actual.as<DateObject>()) {
1857 switch (conversionType) {
1858 case QMetaType::QDateTime:
1859 return 0;
1860 case QMetaType::QDate:
1861 return 1;
1862 case QMetaType::QTime:
1863 return 2;
1864 default:
1865 return convertibleScore(QMetaType::fromType<QDateTime>());
1866 }
1867 } else if (actual.as<RegExpObject>()) {
1868 switch (conversionType) {
1869#if QT_CONFIG(regularexpression)
1870 case QMetaType::QRegularExpression:
1871 return 0;
1872 default:
1873 return convertibleScore(QMetaType::fromType<QRegularExpression>());
1874#else
1875 default:
1876 return convertibleScore(QMetaType());
1877#endif
1878 }
1879 } else if (actual.as<ArrayBuffer>()) {
1880 switch (conversionType) {
1881 case QMetaType::QByteArray:
1882 return 0;
1883 default:
1884 return convertibleScore(QMetaType::fromType<QByteArray>());
1885 }
1886 } else if (actual.as<ArrayObject>()) {
1887 switch (conversionType) {
1888 case QMetaType::QJsonArray:
1889 return 3;
1890 case QMetaType::QStringList:
1891 case QMetaType::QVariantList:
1892 return 5;
1893 case QMetaType::QVector4D:
1894 case QMetaType::QMatrix4x4:
1895 return 6;
1896 case QMetaType::QVector3D:
1897 return 7;
1898 default:
1899 return convertibleScore(QMetaType());
1900 }
1901 } else if (actual.isNull()) {
1902 switch (conversionType) {
1903 case QMetaType::Nullptr:
1904 case QMetaType::VoidStar:
1905 case QMetaType::QObjectStar:
1906 case QMetaType::QJsonValue:
1907 return 0;
1908 default: {
1909 if (conversionMetaType.flags().testFlag(QMetaType::IsPointer))
1910 return 0;
1911 else
1912 return convertibleScore(QMetaType());
1913 }
1914 }
1915 } else if (const Object *obj = actual.as<Object>()) {
1916 if (const VariantObject *variantObject = obj->as<VariantObject>()) {
1917 return MatchVariant(conversionMetaType, [variantObject]() {
1918 return variantObject->d()->data().metaType();
1919 });
1920 }
1921
1922 if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
1923 switch (conversionType) {
1924 case QMetaType::QObjectStar:
1925 return 0;
1926 default:
1927 if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
1928 QObject *wrapped = wrapper->object();
1929 if (!wrapped)
1930 return 0;
1931 if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject()))
1932 return 0;
1933 }
1934 }
1935
1936 return convertibleScore(QMetaType::fromType<QObject *>());
1937 }
1938
1939 if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
1940 const QQmlType type = wrapper->d()->type();
1941 if (type.isSingleton()) {
1942 const QMetaType metaType = type.typeId();
1943 if (metaType == conversionMetaType)
1944 return 0;
1945
1946 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1947 && metaType.flags() & QMetaType::PointerToQObject
1948 && type.metaObject()->inherits(conversionMetaType.metaObject())) {
1949 return 0;
1950 }
1951 } else if (QObject *object = wrapper->object()) {
1952 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1953 && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) {
1954 return 0;
1955 }
1956 }
1957
1958 return convertibleScore(QMetaType());
1959 }
1960
1961 if (const Sequence *sequence = obj->as<Sequence>()) {
1962 const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(sequence);
1963 if (sequenceType == conversionMetaType)
1964 return 1;
1965
1966 return convertibleScore(sequenceType);
1967 }
1968
1969 if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
1970 return MatchVariant(conversionMetaType, [wrapper]() {
1971 return wrapper->d()->isVariant()
1972 ? wrapper->toVariant().metaType()
1973 : wrapper->type();
1974 });
1975 }
1976
1977 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1978 return 0;
1979
1980 switch (conversionType) {
1981 case QMetaType::QJsonObject:
1982 case QMetaType::QVariantMap:
1983 return 5;
1984 default:
1985 break;
1986 }
1987
1988 }
1989
1990 return convertibleScore(QMetaType());
1991}
1992
1993static int numDefinedArguments(CallData *callArgs)
1994{
1995 int numDefinedArguments = callArgs->argc();
1996 while (numDefinedArguments > 0
1997 && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) {
1998 --numDefinedArguments;
1999 }
2000 return numDefinedArguments;
2001}
2002
2003static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
2004{
2005 const QMetaObject *metaObject = object.metaObject();
2006 const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
2007 return indexOfClassInfo != -1
2008 && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
2009}
2010
2014{
2016
2018
2019 if (!returnType.isValid()) {
2020 return engine->throwError(QLatin1String("Unknown method return type: ")
2022 }
2023
2026 engine->throwError(QStringLiteral("Too many arguments"));
2027 return false;
2028 }
2029
2030 const auto stackTrace = engine->stackTrace();
2031 if (stackTrace.isEmpty()) {
2033 << "When matching arguments for "
2034 << object.className() << "::" << data.name(object.metaObject()) << "():";
2035 } else {
2036 const StackFrame frame = stackTrace.first();
2038 + (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
2039 : QString());
2040 }
2041
2042 qWarning().noquote() << QStringLiteral("Too many arguments, ignoring %1")
2044 return true;
2045 };
2046
2048
2049 if (data.hasArguments()) {
2050
2052
2053 bool ok = false;
2054 if (data.isConstructor())
2056 else
2058
2059 if (!ok) {
2060 return engine->throwError(QLatin1String("Unknown method parameter type: ")
2062 }
2063
2064 if (storage.size() > callArgs->argc()) {
2065 QString error = QLatin1String("Insufficient arguments");
2066 return engine->throwError(error);
2067 }
2068
2071 return Encode::undefined();
2072
2073 }
2074
2076
2077 } else {
2079 return Encode::undefined();
2080
2081 return CallMethod(object, data.coreIndex(), returnType, 0, nullptr, engine, callArgs, callType);
2082 }
2083}
2084
2085/*
2086Resolve the overloaded method to call. The algorithm works conceptually like this:
2087 1. Resolve the set of overloads it is *possible* to call.
2088 Impossible overloads include those that have too many parameters or have parameters
2089 of unknown type.
2090 2. Filter the set of overloads to only contain those with the closest number of
2091 parameters.
2092 For example, if we are called with 3 parameters and there are 2 overloads that
2093 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
2094 3. Find the best remaining overload based on its match score.
2095 If two or more overloads have the same match score, return the last one. The match
2096 score is constructed by adding the matchScore() result for each of the parameters.
2097*/
2101{
2102 const int argumentCount = callArgs->argc();
2104
2105 const QQmlPropertyData *best = nullptr;
2109
2112
2113 for (int i = 0; i < methodCount; ++i) {
2114 const QQmlPropertyData *attempt = methods + i;
2115
2121 qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
2122 }
2123
2124 // QQmlV4Function overrides anything that doesn't provide the exact number of arguments
2125 int methodParameterScore = 1;
2126 // QQmlV4Function overrides the "no idea" option, which is 10
2127 int maxMethodMatchScore = 9;
2128 // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments
2130
2131 if (!attempt->isV4Function()) {
2133 int methodArgumentCount = 0;
2134 if (attempt->hasArguments()) {
2135 if (attempt->isConstructor()) {
2137 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2138 continue;
2139 }
2140 } else {
2141 if (!object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr)) {
2142 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2143 continue;
2144 }
2145 }
2147 }
2148
2150 qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
2151 continue; // We don't have sufficient arguments to call this method
2152 }
2153
2155 ? 0
2158 qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
2159 continue; // We already have a better option
2160 }
2161
2164 for (int ii = 0; ii < methodArgumentCount; ++ii) {
2165 const int score = MatchScore((v = Value::fromStaticValue(callArgs->args[ii])),
2166 storage[ii]);
2169 }
2170 }
2171
2176 best = attempt;
2180 qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
2181 << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
2182 << "bestSumMatchScore" << bestSumMatchScore << "\n";
2183 } else {
2184 qCDebug(lcOverloadResolution) << "did not update best\n"
2185 << "bestParameterScore" << bestParameterScore << "\t"
2186 << "methodParameterScore" << methodParameterScore << "\n"
2187 << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
2188 << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
2189 << "bestSumMatchScore" << bestSumMatchScore << "\t"
2190 << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
2191 }
2192
2193 if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
2194 qCDebug(lcOverloadResolution, "perfect match");
2195 break; // We can't get better than that
2196 }
2197
2198 };
2199
2200 if (best && best->isValid()) {
2201 return best;
2202 } else {
2203 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
2204 for (int i = 0; i < methodCount; ++i) {
2209 error += u"\n " + QString::fromUtf8(m.methodSignature());
2210 }
2211
2213 return nullptr;
2214 }
2215}
2216
2217static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
2218{
2219 if (required == QMetaType::fromType<QVariant>()
2220 || required == QMetaType::fromType<QJSValue>()
2221 || required == QMetaType::fromType<QJSManagedValue>()) {
2222 return true;
2223 }
2224
2225 if (data) {
2226 if (passed == QMetaType::fromType<QVariant>())
2227 passed = static_cast<const QVariant *>(data)->metaType();
2228 else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
2229 passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
2230 }
2231
2232 if (passed == required)
2233 return true;
2234
2235 if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
2236 switch (passed.id()) {
2237 case QMetaType::UnknownType:
2238 case QMetaType::Nullptr:
2239 case QMetaType::Bool:
2240 case QMetaType::Int:
2241 case QMetaType::Double:
2242 case QMetaType::QString:
2243 return true;
2244 default:
2245 break;
2246 }
2247 }
2248
2249 return false;
2250}
2251
2253 const QMetaMethod &method, void **argv, int argc, const QMetaType *types)
2254{
2255 if (types[0].isValid() && !ExactMatch(method.returnMetaType(), types[0], nullptr))
2256 return false;
2257
2258 if (method.parameterCount() != argc)
2259 return false;
2260
2261 for (int i = 0; i < argc; ++i) {
2262 if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1]))
2263 return false;
2264 }
2265
2266 return true;
2267}
2268
2271 void **argv, int argc, const QMetaType *types)
2272{
2273 // We only accept exact matches here. Everything else goes through the JavaScript conversion.
2274 for (int i = 0; i < methodCount; ++i) {
2275 const QQmlPropertyData *attempt = methods + i;
2277 return attempt;
2278 }
2279
2280 return nullptr;
2281}
2282
2283void CallArgument::cleanup()
2284{
2285 switch (type) {
2286 case QMetaType::QString:
2287 qstringPtr->~QString();
2288 break;
2289 case QMetaType::QByteArray:
2290 qbyteArrayPtr->~QByteArray();
2291 break;
2292 case QMetaType::QVariant:
2293 case QVariantWrappedType:
2294 qvariantPtr->~QVariant();
2295 break;
2296 case QMetaType::QJsonArray:
2297 jsonArrayPtr->~QJsonArray();
2298 break;
2299 case QMetaType::QJsonObject:
2300 jsonObjectPtr->~QJsonObject();
2301 break;
2302 case QMetaType::QJsonValue:
2303 jsonValuePtr->~QJsonValue();
2304 break;
2305 default:
2306 if (type == qMetaTypeId<QJSValue>()) {
2307 qjsValuePtr->~QJSValue();
2308 break;
2309 }
2310
2311 if (type == qMetaTypeId<QJSManagedValue>()) {
2312 qjsManagedValuePtr->~QJSManagedValue();
2313 break;
2314 }
2315
2316 if (type == qMetaTypeId<QList<QObject *> >()) {
2317 qlistPtr->~QList<QObject *>();
2318 break;
2319 }
2320
2321 // The sequence types need no cleanup because we don't own them.
2322
2323 break;
2324 }
2325}
2326
2327void *CallArgument::dataPtr()
2328{
2329 switch (type) {
2330 case QMetaType::UnknownType:
2331 return nullptr;
2332 case QVariantWrappedType:
2333 return qvariantPtr->data();
2334 default:
2335 if (type == qMetaTypeId<std::vector<int>>())
2336 return stdVectorIntPtr;
2337 if (type == qMetaTypeId<std::vector<qreal>>())
2338 return stdVectorRealPtr;
2339 if (type == qMetaTypeId<std::vector<bool>>())
2340 return stdVectorBoolPtr;
2341 if (type == qMetaTypeId<std::vector<QString>>())
2342 return stdVectorQStringPtr;
2343 if (type == qMetaTypeId<std::vector<QUrl>>())
2344 return stdVectorQUrlPtr;
2345#if QT_CONFIG(qml_itemmodel)
2346 if (type == qMetaTypeId<std::vector<QModelIndex>>())
2347 return stdVectorQModelIndexPtr;
2348#endif
2349 break;
2350 }
2351
2352 return (void *)&allocData;
2353}
2354
2355void CallArgument::initAsType(QMetaType metaType)
2356{
2357 if (type != QMetaType::UnknownType)
2358 cleanup();
2359
2360 type = metaType.id();
2361 switch (type) {
2362 case QMetaType::Void:
2363 type = QMetaType::UnknownType;
2364 break;
2365 case QMetaType::UnknownType:
2366 case QMetaType::Int:
2367 case QMetaType::UInt:
2368 case QMetaType::Bool:
2369 case QMetaType::Double:
2370 case QMetaType::Float:
2371 break;
2372 case QMetaType::QObjectStar:
2373 qobjectPtr = nullptr;
2374 break;
2375 case QMetaType::QString:
2376 qstringPtr = new (&allocData) QString();
2377 break;
2378 case QMetaType::QVariant:
2379 qvariantPtr = new (&allocData) QVariant();
2380 break;
2381 case QMetaType::QJsonArray:
2382 jsonArrayPtr = new (&allocData) QJsonArray();
2383 break;
2384 case QMetaType::QJsonObject:
2385 jsonObjectPtr = new (&allocData) QJsonObject();
2386 break;
2387 case QMetaType::QJsonValue:
2388 jsonValuePtr = new (&allocData) QJsonValue();
2389 break;
2390 default: {
2391 if (metaType == QMetaType::fromType<QJSValue>()) {
2392 qjsValuePtr = new (&allocData) QJSValue();
2393 break;
2394 }
2395
2396 if (metaType == QMetaType::fromType<QJSManagedValue>()) {
2397 qjsManagedValuePtr = new (&allocData) QJSManagedValue();
2398 break;
2399 }
2400
2401 if (metaType == QMetaType::fromType<QList<QObject *>>()) {
2402 qlistPtr = new (&allocData) QList<QObject *>();
2403 break;
2404 }
2405
2406 type = QVariantWrappedType;
2407 qvariantPtr = new (&allocData) QVariant(metaType, (void *)nullptr);
2408 break;
2409 }
2410 }
2411}
2412
2413template <class T, class M>
2414bool CallArgument::fromContainerValue(const Value &value, M CallArgument::*member)
2415{
2416 if (T* ptr = static_cast<T *>(SequencePrototype::rawContainerPtr(
2417 value.as<Sequence>(), QMetaType(type)))) {
2418 (this->*member) = ptr;
2419 return true;
2420 }
2421 (this->*member) = nullptr;
2422 return false;
2423}
2424
2425bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const Value &value)
2426{
2427 if (type != QMetaType::UnknownType)
2428 cleanup();
2429
2430 type = metaType.id();
2431
2432 switch (type) {
2433 case QMetaType::Int:
2434 intValue = quint32(value.toInt32());
2435 return true;
2436 case QMetaType::UInt:
2437 intValue = quint32(value.toUInt32());
2438 return true;
2439 case QMetaType::Bool:
2440 boolValue = value.toBoolean();
2441 return true;
2442 case QMetaType::Double:
2443 doubleValue = double(value.toNumber());
2444 return true;
2445 case QMetaType::Float:
2446 floatValue = float(value.toNumber());
2447 return true;
2448 case QMetaType::QString:
2449 if (value.isNullOrUndefined())
2450 qstringPtr = new (&allocData) QString();
2451 else
2452 qstringPtr = new (&allocData) QString(value.toQStringNoThrow());
2453 return true;
2454 case QMetaType::QByteArray:
2455 qbyteArrayPtr = new (&allocData) QByteArray();
2456 ExecutionEngine::metaTypeFromJS(value, metaType, qbyteArrayPtr);
2457 return true;
2458 case QMetaType::QObjectStar:
2459 if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) {
2460 qobjectPtr = qobjectWrapper->object();
2461 return true;
2462 }
2463
2464 if (const QQmlTypeWrapper *qmlTypeWrapper = value.as<QQmlTypeWrapper>()) {
2465 if (qmlTypeWrapper->isSingleton()) {
2466 // Convert via QVariant below.
2467 // TODO: Can't we just do qobjectPtr = qmlTypeWrapper->object() instead?
2468 break;
2469 } else if (QObject *obj = qmlTypeWrapper->object()) {
2470 // attached object case
2471 qobjectPtr = obj;
2472 return true;
2473 }
2474
2475 // If this is a plain type wrapper without an instance,
2476 // then we got a namespace, and that's a type error
2477 type = QMetaType::UnknownType;
2478 return false;
2479 }
2480
2481 qobjectPtr = nullptr;
2482 return value.isNullOrUndefined(); // null and undefined are nullptr
2483 case QMetaType::QVariant:
2484 qvariantPtr = new (&allocData) QVariant(ExecutionEngine::toVariant(value, QMetaType {}));
2485 return true;
2486 case QMetaType::QJsonArray: {
2487 Scope scope(engine);
2488 ScopedObject o(scope, value);
2489 jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(o));
2490 return true;
2491 }
2492 case QMetaType::QJsonObject: {
2493 Scope scope(engine);
2494 ScopedObject o(scope, value);
2495 jsonObjectPtr = new (&allocData) QJsonObject(JsonObject::toJsonObject(o));
2496 return true;
2497 }
2498 case QMetaType::QJsonValue:
2499 jsonValuePtr = new (&allocData) QJsonValue(JsonObject::toJsonValue(value));
2500 return true;
2501 case QMetaType::Void:
2502 type = QMetaType::UnknownType;
2503 // TODO: This only doesn't leak because a default constructed QVariant doesn't allocate.
2504 *qvariantPtr = QVariant();
2505 return true;
2506 default:
2507 if (type == qMetaTypeId<QJSValue>()) {
2508 qjsValuePtr = new (&allocData) QJSValue;
2509 Scope scope(engine);
2510 ScopedValue v(scope, value);
2511 QJSValuePrivate::setValue(qjsValuePtr, v);
2512 return true;
2513 }
2514
2515 if (type == qMetaTypeId<QJSManagedValue>()) {
2516 Scope scope(engine);
2517 ScopedValue v(scope, value);
2518 qjsManagedValuePtr = new (&allocData) QJSManagedValue;
2519 // This points to a JS heap object that cannot be immutable. const_cast-ing is fine here.
2520 *QJSManagedValuePrivate::memberPtr(qjsManagedValuePtr) = const_cast<Value *>(&value);
2521 return true;
2522 }
2523
2524 if (type == qMetaTypeId<QList<QObject*> >()) {
2525 qlistPtr = new (&allocData) QList<QObject *>();
2526 Scope scope(engine);
2527 ScopedArrayObject array(scope, value);
2528 if (array) {
2529 Scoped<QObjectWrapper> qobjectWrapper(scope);
2530
2531 uint length = array->getLength();
2532 qlistPtr->reserve(length);
2533 for (uint ii = 0; ii < length; ++ii) {
2534 QObject *o = nullptr;
2535 qobjectWrapper = array->get(ii);
2536 if (!!qobjectWrapper)
2537 o = qobjectWrapper->object();
2538 qlistPtr->append(o);
2539 }
2540 return true;
2541 }
2542
2543 if (const auto sequence = value.as<QV4::Sequence>()) {
2544
2545 // Does readReference(). Don't move past getRawContainer()
2546 const qint64 length = sequence->getLength();
2547
2548 switch (QV4::SequencePrototype::getRawContainer(
2549 sequence, qlistPtr, QMetaType::fromType<QList<QObject *>>())) {
2550 case SequencePrototype::Copied:
2551 case SequencePrototype::WasEqual:
2552 break;
2553 case SequencePrototype::TypeMismatch: {
2554 if (!qIsAtMostSizetypeLimit(length) || !qIsAtMostUintLimit(length))
2555 return false;
2556
2557 qlistPtr->reserve(length);
2558 Scoped<QObjectWrapper> qobjectWrapper(scope);
2559 for (uint ii = 0; ii < length; ++ii) {
2560 QObject *o = nullptr;
2561 qobjectWrapper = sequence->get(ii);
2562 if (!!qobjectWrapper)
2563 o = qobjectWrapper->object();
2564 qlistPtr->append(o);
2565 }
2566 break;
2567 }
2568 }
2569 return true;
2570 }
2571
2572 if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) {
2573 qlistPtr->append(qobjectWrapper->object());
2574 return true;
2575 }
2576
2577 if (const QmlListWrapper *listWrapper = value.as<QmlListWrapper>()) {
2578 *qlistPtr = listWrapper->d()->property()->toList<QList<QObject *>>();
2579 return true;
2580 }
2581
2582 qlistPtr->append(nullptr);
2583 return value.isNullOrUndefined();
2584 }
2585
2586 if (metaType.flags() & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) {
2587 // You can assign null or undefined to any pointer. The result is a nullptr.
2588 if (value.isNullOrUndefined()) {
2589 qvariantPtr = new (&allocData) QVariant(metaType, nullptr);
2590 return true;
2591 }
2592 break;
2593 }
2594
2595 if (type == qMetaTypeId<std::vector<int>>()) {
2596 if (fromContainerValue<std::vector<int>>(value, &CallArgument::stdVectorIntPtr))
2597 return true;
2598 } else if (type == qMetaTypeId<std::vector<qreal>>()) {
2599 if (fromContainerValue<std::vector<qreal>>(value, &CallArgument::stdVectorRealPtr))
2600 return true;
2601 } else if (type == qMetaTypeId<std::vector<bool>>()) {
2602 if (fromContainerValue<std::vector<bool>>(value, &CallArgument::stdVectorBoolPtr))
2603 return true;
2604 } else if (type == qMetaTypeId<std::vector<QString>>()) {
2605 if (fromContainerValue<std::vector<QString>>(value, &CallArgument::stdVectorQStringPtr))
2606 return true;
2607 } else if (type == qMetaTypeId<std::vector<QUrl>>()) {
2608 if (fromContainerValue<std::vector<QUrl>>(value, &CallArgument::stdVectorQUrlPtr))
2609 return true;
2610#if QT_CONFIG(qml_itemmodel)
2611 } else if (type == qMetaTypeId<std::vector<QModelIndex>>()) {
2612 if (fromContainerValue<std::vector<QModelIndex>>(
2613 value, &CallArgument::stdVectorQModelIndexPtr)) {
2614 return true;
2615 }
2616#endif
2617 }
2618 break;
2619 }
2620
2621 // Convert via QVariant through the QML engine.
2622 qvariantPtr = new (&allocData) QVariant(metaType);
2623 type = QVariantWrappedType;
2624
2625 if (ExecutionEngine::metaTypeFromJS(value, metaType, qvariantPtr->data()))
2626 return true;
2627
2628 const QVariant v = ExecutionEngine::toVariant(value, metaType);
2629 return QMetaType::convert(v.metaType(), v.constData(), metaType, qvariantPtr->data());
2630}
2631
2632ReturnedValue CallArgument::toValue(ExecutionEngine *engine)
2633{
2634 switch (type) {
2635 case QMetaType::Int:
2636 return Encode(int(intValue));
2637 case QMetaType::UInt:
2638 return Encode((uint)intValue);
2639 case QMetaType::Bool:
2640 return Encode(boolValue);
2641 case QMetaType::Double:
2642 return Encode(doubleValue);
2643 case QMetaType::Float:
2644 return Encode(floatValue);
2645 case QMetaType::QString:
2646 return Encode(engine->newString(*qstringPtr));
2647 case QMetaType::QByteArray:
2648 return Encode(engine->newArrayBuffer(*qbyteArrayPtr));
2649 case QMetaType::QObjectStar:
2650 if (qobjectPtr)
2651 QQmlData::get(qobjectPtr, true)->setImplicitDestructible();
2652 return QObjectWrapper::wrap(engine, qobjectPtr);
2653 case QMetaType::QJsonArray:
2654 return JsonObject::fromJsonArray(engine, *jsonArrayPtr);
2655 case QMetaType::QJsonObject:
2656 return JsonObject::fromJsonObject(engine, *jsonObjectPtr);
2657 case QMetaType::QJsonValue:
2658 return JsonObject::fromJsonValue(engine, *jsonValuePtr);
2659 case QMetaType::QVariant:
2660 case QVariantWrappedType: {
2661 Scope scope(engine);
2662 ScopedValue rv(scope, scope.engine->fromVariant(*qvariantPtr));
2663 Scoped<QObjectWrapper> qobjectWrapper(scope, rv);
2664 if (!!qobjectWrapper) {
2665 if (QObject *object = qobjectWrapper->object())
2666 QQmlData::get(object, true)->setImplicitDestructible();
2667 }
2668 return rv->asReturnedValue();
2669 }
2670 default:
2671 break;
2672 }
2673
2674 if (type == qMetaTypeId<QJSValue>()) {
2675 // The QJSValue can be passed around via dataPtr()
2676 QJSValuePrivate::manageStringOnV4Heap(engine, qjsValuePtr);
2677 return QJSValuePrivate::asReturnedValue(qjsValuePtr);
2678 }
2679
2680 if (type == qMetaTypeId<QJSManagedValue>())
2681 return QJSManagedValuePrivate::member(qjsManagedValuePtr)->asReturnedValue();
2682
2683 if (type == qMetaTypeId<QList<QObject *> >()) {
2684 // XXX Can this be made more by using Array as a prototype and implementing
2685 // directly against QList<QObject*>?
2686 QList<QObject *> &list = *qlistPtr;
2687 Scope scope(engine);
2688 ScopedArrayObject array(scope, engine->newArrayObject());
2689 array->arrayReserve(list.size());
2690 ScopedValue v(scope);
2691 for (int ii = 0; ii < list.size(); ++ii)
2692 array->arrayPut(ii, (v = QObjectWrapper::wrap(engine, list.at(ii))));
2693 array->setArrayLengthUnchecked(list.size());
2694 return array.asReturnedValue();
2695 }
2696
2697 return Encode::undefined();
2698}
2699
2708
2718
2722{
2724
2726 if (cloneFrom->wrapper) {
2728 if (ref) {
2730 } else {
2731 // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what
2732 // value we should operate on. Without knowledge of the property the value
2733 // was read from, we cannot load the value from the given object.
2734 return Encode::undefined();
2735 }
2736 }
2737
2739 valueScope,
2742
2744
2745 Q_ASSERT(method->d()->methods == nullptr);
2746 switch (cloneFrom->methodCount) {
2747 case 0:
2748 Q_ASSERT(cloneFrom->methods == nullptr);
2749 break;
2750 case 1:
2752 == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
2753 method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
2754 *method->d()->methods = *cloneFrom->methods;
2755 break;
2756 default:
2757 Q_ASSERT(cloneFrom->methods != nullptr);
2761 break;
2762 }
2763
2764 return method.asReturnedValue();
2765}
2766
2768{
2772}
2773
2775{
2777
2779 return valueWrapper->metaObject();
2780 if (QObject *self = object())
2781 return self->metaObject();
2782
2783 return nullptr;
2784}
2785
2787{
2789
2791 return objectWrapper->object();
2793 return typeWrapper->object();
2794 return nullptr;
2795}
2796
2797bool Heap::QObjectMethod::isDetached() const
2798{
2799 if (!wrapper)
2800 return true;
2801
2804 return valueWrapper->d()->object() == nullptr;
2805
2806 return false;
2807}
2808
2810{
2813 return objectWrapper->object() == o;
2815 return typeWrapper->object() == o;
2816
2820 return qobject->object() == o;
2822 return type->object() == o;
2823
2824 // Attached to some nested value type or sequence object
2825 return false;
2826 }
2827
2828 return false;
2829}
2830
2832 const QMetaObject *thisMeta) const
2833{
2834 // Check that the metaobject matches.
2835
2836 if (!thisMeta) {
2837 // You can only get a detached method via a lookup, and then you have a thisObject.
2839 return Included;
2840 }
2841
2842 const auto check = [&](const QMetaObject *included) {
2847 "%s:%d: Calling C++ methods with 'this' objects different from the one "
2848 "they were retrieved from is broken, due to historical reasons. The "
2849 "original object is used as 'this' object. You can allow the given "
2850 "'this' object to be used by setting "
2851 "'pragma NativeMethodBehavior: AcceptThisObject'",
2853 return Included;
2854 }
2855
2856 // destroy() and toString() can be called on all QObjects, but not on gadgets.
2857 if (index < 0)
2859
2860 // Find the base type the method belongs to.
2862 while (true) {
2863 if (included == thisMeta)
2864 return Explicit;
2865
2866 if (methodOffset <= index)
2868
2872 };
2873
2875 };
2876
2877 if (const QMetaObject *meta = metaObject())
2878 return check(meta);
2879
2880 // If the QObjectMethod is detached, we can only have gotten here via a lookup.
2881 // The lookup checks that the QQmlPropertyCache matches.
2882 return Explicit;
2883}
2884
2886{
2888 return QStringLiteral("destroy");
2889 else if (index == QV4::QObjectMethod::ToStringMethod)
2890 return QStringLiteral("toString");
2891
2892 const QMetaObject *mo = metaObject();
2893 if (!mo)
2894 return QString();
2895
2896 int methodOffset = mo->methodOffset();
2897 while (methodOffset > index) {
2898 mo = mo->superClass();
2900 }
2901
2902 return "%1::%2"_L1.arg(QLatin1StringView{mo->className()},
2904}
2905
2907{
2908 if (methods) {
2909 Q_ASSERT(methodCount > 0);
2910 return;
2911 }
2912
2913 const QMetaObject *mo = metaObject();
2914
2915 if (!mo)
2916 mo = thisMeta;
2917
2918 Q_ASSERT(mo);
2919
2920 int methodOffset = mo->methodOffset();
2921 while (methodOffset > index) {
2922 mo = mo->superClass();
2924 }
2928 dummy.load(method);
2931 // Look for overloaded methods
2933 for (int ii = index - 1; ii >= methodOffset; --ii) {
2934 if (methodName == mo->method(ii).name()) {
2935 method = mo->method(ii);
2936 dummy.load(method);
2938 }
2939 }
2940 if (resolvedMethods.size() > 1) {
2944 } else {
2945 methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod);
2947 methodCount = 1;
2948 }
2949
2950 Q_ASSERT(methodCount > 0);
2951}
2952
2959
2961 ExecutionEngine *engine, QObject *o, const Value *args, int argc) const
2962{
2963 method_destroy(engine, o, argc > 0 ? args[0].toInt32() : 0);
2964 return Encode::undefined();
2965}
2966
2968{
2969 if (!o)
2970 return true;
2971
2973 engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2974 return false;
2975 }
2976
2977 if (delay > 0)
2979 else
2980 o->deleteLater();
2981
2982 return true;
2983}
2984
2986 const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2987{
2988 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2990}
2991
2993 const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
2994{
2995 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2997}
2998
3000{
3002
3003 const QMetaObject *thisMeta = nullptr;
3004
3005 QObject *o = nullptr;
3007 if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
3008 thisMeta = w->metaObject();
3009 o = w->object();
3010 } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
3011 thisMeta = w->metaObject();
3012 o = w->object();
3013 } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
3014 thisMeta = w->metaObject();
3015 valueWrapper = w->d();
3016 }
3017
3019 if (o && o == d()->object()) {
3021 // Nothing to do; objects are the same. This should be common
3022 } else if (valueWrapper && valueWrapper == d()->wrapper) {
3024 // Nothing to do; gadgets are the same. This should be somewhat common
3025 } else {
3027 if (mode == Heap::QObjectMethod::Invalid) {
3028 v4->throwError(QLatin1String("Cannot call method %1 on %2").arg(
3029 d()->name(), thisObject->toQStringNoThrow()));
3030 return Encode::undefined();
3031 }
3032 }
3033
3034 QQmlObjectOrGadget object = [&](){
3035 if (mode == Heap::QObjectMethod::Included) {
3036 QV4::Scope scope(v4);
3040 return QQmlObjectOrGadget(type->object());
3042 valueWrapper = value->d();
3044 }
3045 Q_UNREACHABLE();
3046 } else {
3047 if (o)
3048 return QQmlObjectOrGadget(o);
3049
3054 }
3055 }();
3056
3057 if (object.isNull())
3058 return Encode::undefined();
3059
3060 if (d()->index == DestroyMethod)
3061 return method_destroy(v4, object.qObject(), argv, argc);
3062 else if (d()->index == ToStringMethod)
3063 return method_toString(v4, object.qObject());
3064
3066
3067 Scope scope(v4);
3070
3071 const QQmlPropertyData *method = d()->methods;
3072
3073 // If we call the method, we have to write back any value type references afterwards.
3074 // The method might change the value.
3075 const auto doCall = [&](const auto &call) {
3076 if (!method->isConstant()) {
3080 return rv->asReturnedValue();
3081 }
3082 }
3083
3084 return call();
3085 };
3086
3087 if (d()->methodCount != 1) {
3088 Q_ASSERT(d()->methodCount > 0);
3090 if (method == nullptr)
3091 return Encode::undefined();
3092 }
3093
3094 if (method->isV4Function()) {
3095 return doCall([&]() {
3099
3100 void *args[] = { nullptr, &funcptr };
3102
3103 return rv->asReturnedValue();
3104 });
3105 }
3106
3107 return doCall([&]() { return callPrecise(object, *method, v4, callData); });
3108}
3109
3111{
3112 constexpr int parameterCount() const { return 0; }
3113 constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
3114 constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
3115};
3116
3118 QObject *thisObject, void **argv, const QMetaType *types, int argc) const
3119{
3121
3122 const QMetaObject *thisMeta = nullptr;
3124
3125 if (thisObject) {
3127 } else {
3131 }
3132
3133 QQmlObjectOrGadget object = [&](){
3134 if (thisObject)
3136
3137 Scope scope(v4);
3140
3145 }();
3146
3147 if (object.isNull())
3148 return;
3149
3150 if (d()->index == DestroyMethod) {
3151 // method_destroy will use at most one argument
3153 v4, thisObject, argv, types, std::min(argc, 1),
3154 [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
3156 return method_destroy(v4, object.qObject(), argv, argc);
3157 });
3158 return;
3159 }
3160
3161 if (d()->index == ToStringMethod) {
3165 [v4, thisMeta, object](void **argv, int) {
3166 *static_cast<QString *>(argv[0])
3168 });
3169 return;
3170 }
3171
3173
3174 const QQmlPropertyData *method = d()->methods;
3175 if (d()->methodCount != 1) {
3176 Q_ASSERT(d()->methodCount > 0);
3178 }
3179
3180 if (!method || method->isV4Function()) {
3183 [this](const Value *thisObject, const Value *argv, int argc) {
3184 return callInternal(thisObject, argv, argc);
3185 });
3186 } else {
3190 [v4, object, valueWrapper, method](void **argv, int argc) {
3191 Q_UNUSED(argc);
3192
3193 // If we call the method, we have to write back any value type references afterwards.
3194 // The method might change the value.
3196 if (!method->isConstant()) {
3199 }
3200
3201 // If the method returns a QObject* we need to track it on the JS heap
3202 // (if it's destructible).
3203 QObject *qobjectPtr = nullptr;
3205 if (argv[0]) {
3207 qobjectPtr = *static_cast<QObject **>(argv[0]);
3208 } else if (resultType == QMetaType::fromType<QVariant>()) {
3209 const QVariant *result = static_cast<const QVariant *>(argv[0]);
3212 qobjectPtr = *static_cast<QObject *const *>(result->data());
3213 }
3214 }
3215
3216 if (qobjectPtr) {
3219 ddata->indestructible = false;
3221 }
3222 }
3223 });
3224 }
3225}
3226
3228
3229void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
3230{
3231 Object::init();
3232 this->signalIndex = signalIndex;
3233 setObject(object);
3234}
3235
3237
3239{
3243 << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
3244 "not call it directly. Make it a proper function and call "
3245 "that or emit the signal.")
3247
3248 Scope scope(engine());
3251 scope.engine,
3252 static_cast<Heap::QObjectWrapper *>(nullptr),
3253 signalIndex()));
3254
3255 return method->call(thisObject, argv, argc);
3256}
3257
3272
3273
3276{
3277 const QObjectBiPointer key = it.key();
3278 const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
3279 disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
3280 return QHash<QObjectBiPointer, WeakValue>::erase(it);
3281}
3282
3283void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
3284{
3285 QHash<QObjectBiPointer, WeakValue>::remove(object);
3286 QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object));
3287}
3288
3289} // namespace QV4
3290
3291QT_END_NAMESPACE
3292
3293#include "moc_qv4qobjectwrapper_p.cpp"
QHash< QObjectBiPointer, QV4::WeakValue >::Iterator Iterator
Definition qjsvalue.h:23
static int MatchScore(const Value &actual, QMetaType conversionMetaType)
static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMetaType returnType, int argCount, const QMetaType *argTypes, ExecutionEngine *engine, CallData *callArgs, QMetaObject::Call callType=QMetaObject::InvokeMetaMethod)
static std::pair< QObject *, int > extractQtSignal(const Value &value)
static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
static ReturnedValue loadProperty(ExecutionEngine *v4, Heap::Object *wrapper, QObject *object, const QQmlPropertyData &property)
static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack)
int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve)
static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty=nullptr)
static Heap::ReferenceObject::Flags referenceFlags(ExecutionEngine *v4, const QQmlPropertyData &property)
static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, const QQmlRefPointer< QQmlContextData > &qmlContext, QObject *qobj, bool *hasProperty=nullptr)
static int numDefinedArguments(CallData *callArgs)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define PROPERTY_STORE(cpptype, value)
void init(QObject *object, int signalIndex)
static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
PropertyKey next(const Object *o, Property *pd=nullptr, PropertyAttributes *attrs=nullptr) override
~QObjectWrapperOwnPropertyKeyIterator() override=default
constexpr QMetaType returnMetaType() const
constexpr QMetaType parameterMetaType(int) const
constexpr int parameterCount() const