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);
1124 if (!ddata)
1125 ddata = QQmlData::get(qobj, true);
1131 return method.asReturnedValue();
1132 }
1133
1134 if (!ddata || !ddata->propertyCache) {
1141 return result->asReturnedValue();
1142 }
1144
1145 if (!property) {
1146 // Check for attached properties
1147 if (name->startsWithUpper()) {
1149 return *importProperty;
1150 }
1152 }
1153
1154 if (property->isFunction()
1155 && !property->isVarProperty()
1156 && !property->isVMEFunction() // Handled by QObjectLookup
1157 && !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too
1158 QV4::Heap::QObjectMethod *method = nullptr;
1161 return lookup->getter(engine, *object);
1162 }
1163
1165
1167 return lookup->getter(engine, *object);
1168}
1169
1175
1177{
1180
1181 if (QObject *qObject = wrapper->object())
1182 return QMetaObject::metacall(qObject, call, index, a);
1183
1184 return 0;
1185}
1186
1189{
1190 if (!metaObject)
1191 return QLatin1String("null");
1192
1193 if (!object)
1194 return QString::fromUtf8(metaObject->className()) + QLatin1String("(0x0)");
1195
1196 const int id = metaObject->indexOfMethod("toString()");
1197 if (id >= 0) {
1203 return result.toString();
1206 return value->toQString();
1207 }
1208
1211 QLatin1String("(0x") + QString::number(quintptr(object), 16);
1213 if (!objectName.isEmpty())
1214 result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
1215 result += QLatin1Char(')');
1216 return result;
1217}
1218
1220{
1225
1229
1230 static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
1231 {
1232 switch (which) {
1233 case Destroy: {
1234 delete static_cast<QObjectSlotDispatcher*>(this_);
1235 }
1236 break;
1237 case Call: {
1238 if (QQmlData::wasDeleted(receiver))
1239 break;
1240
1241 QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
1242 ExecutionEngine *v4 = This->function.engine();
1243 // Might be that we're still connected to a signal that's emitted long
1244 // after the engine died. We don't track connections in a global list, so
1245 // we need this safeguard.
1246 if (!v4)
1247 break;
1248
1249 QQmlMetaObject::ArgTypeStorage<9> storage;
1250 QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr);
1251
1252 const qsizetype argCount = std::min(storage.size(), This->maxNumArguments);
1253
1254 Scope scope(v4);
1255 ScopedFunctionObject f(scope, This->function.value());
1256
1257 JSCallArguments jsCallData(scope, argCount);
1258 *jsCallData.thisObject = This->thisObject.isUndefined()
1259 ? v4->globalObject->asReturnedValue()
1260 : This->thisObject.value();
1261 for (qsizetype ii = 0; ii < argCount; ++ii) {
1262 QMetaType type = storage[ii];
1263 if (type == QMetaType::fromType<QVariant>()) {
1264 jsCallData.args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1265 } else {
1266 jsCallData.args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
1267 }
1268 }
1269
1270 f->call(jsCallData);
1271 if (scope.hasException()) {
1272 QQmlError error = v4->catchExceptionAsQmlError();
1273 if (error.description().isEmpty()) {
1274 ScopedString name(scope, f->name());
1275 error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString()));
1276 }
1277 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
1278 QQmlEnginePrivate::get(qmlEngine)->warning(error);
1279 } else {
1280 QMessageLogger(error.url().toString().toLatin1().constData(),
1281 error.line(), nullptr).warning().noquote()
1282 << error.toString();
1283 }
1284 }
1285 }
1286 break;
1287 case Compare: {
1288 QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
1289 if (connection->function.isUndefined()) {
1290 *ret = false;
1291 return;
1292 }
1293
1294 // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
1295 // for the new-style QObject::connect. Here we use the engine pointer as sentinel
1296 // to distinguish those type of QSlotObjectBase connections from our QML connections.
1297 ExecutionEngine *v4 = reinterpret_cast<ExecutionEngine*>(metaArgs[0]);
1298 if (v4 != connection->function.engine()) {
1299 *ret = false;
1300 return;
1301 }
1302
1303 Scope scope(v4);
1304 ScopedValue function(scope, *reinterpret_cast<Value*>(metaArgs[1]));
1305 ScopedValue thisObject(scope, *reinterpret_cast<Value*>(metaArgs[2]));
1306 QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
1307 int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
1308
1309 if (slotIndexToDisconnect != -1) {
1310 // This is a QObject function wrapper
1311 if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1312 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1313
1314 ScopedFunctionObject f(scope, connection->function.value());
1315 std::pair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f);
1316 if (connectedFunctionData.first == receiverToDisconnect &&
1317 connectedFunctionData.second == slotIndexToDisconnect) {
1318 *ret = true;
1319 return;
1320 }
1321 }
1322 } else {
1323 // This is a normal JS function
1324 if (RuntimeHelpers::strictEqual(*connection->function.valueRef(), function) &&
1325 connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1326 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1327 *ret = true;
1328 return;
1329 }
1330 }
1331
1332 *ret = false;
1333 }
1334 break;
1335 case NumOperations:
1336 break;
1337 }
1338 };
1339};
1340
1342{
1343 Scope scope(b);
1344
1345 if (argc == 0)
1346 THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
1347
1350 int signalIndex = signalInfo.second; // in method range, not signal range!
1351
1352 if (signalIndex < 0)
1353 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1354
1355 if (!signalObject)
1356 THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1357
1360 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1361
1364
1365 if (argc == 1) {
1366 f = argv[0];
1367 } else if (argc >= 2) {
1368 object = argv[0];
1369 f = argv[1];
1370 }
1371
1372 if (!f)
1373 THROW_GENERIC_ERROR("Function.prototype.connect: target is not a function");
1374
1375 if (!object->isUndefined() && !object->isObject())
1376 THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object");
1377
1380
1383
1387 }
1388 }
1389
1390 std::pair<QObject *, int> functionData = QObjectMethod::extractQtMethod(f); // align with disconnect
1391 QObject *receiver = nullptr;
1392
1393 if (functionData.first)
1395 else if (auto qobjectWrapper = object->as<QV4::QObjectWrapper>())
1397 else if (auto typeWrapper = object->as<QV4::QQmlTypeWrapper>())
1399
1400 if (receiver) {
1401 if (functionData.second == -1) {
1403 } else {
1404 // This means we are connecting to QObjectMethod which complains about extra arguments.
1405 Heap::QObjectMethod *d = static_cast<Heap::QObjectMethod *>(f->d());
1408 [](int a, const QQmlPropertyData &b) {
1409 return std::max(a, b.metaMethod().parameterCount());
1410 });
1411 }
1412
1414 } else {
1417 "Could not find receiver of the connection, using sender as receiver. Disconnect "
1418 "explicitly (or delete the sender) to make sure the connection is removed.");
1420 }
1421
1423}
1424
1426{
1427 Scope scope(b);
1428
1429 if (argc == 0)
1430 THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
1431
1435
1436 if (signalIndex == -1)
1437 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1438
1439 if (!signalObject)
1440 THROW_GENERIC_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1441
1443 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1444
1447
1448 if (argc == 1) {
1449 functionValue = argv[0];
1450 } else if (argc >= 2) {
1452 functionValue = argv[1];
1453 }
1454
1455 if (!functionValue)
1456 THROW_GENERIC_ERROR("Function.prototype.disconnect: target is not a function");
1457
1459 THROW_GENERIC_ERROR("Function.prototype.disconnect: target this is not an object");
1460
1462
1463 void *a[] = {
1464 scope.engine,
1469 };
1470
1471 QObject *receiver = nullptr;
1472
1473 if (functionData.first)
1477 else if (auto typeWrapper = functionThisValue->as<QV4::QQmlTypeWrapper>())
1479
1480 if (receiver) {
1482 reinterpret_cast<void **>(&a));
1483 } else {
1485 reinterpret_cast<void **>(&a));
1486 }
1487
1489}
1490
1491static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack)
1492{
1493 QQueue<QObject *> queue;
1494 queue.append(parent->children());
1495
1496 while (!queue.isEmpty()) {
1497 QObject *child = queue.dequeue();
1498 if (!child)
1499 continue;
1500 QObjectWrapper::markWrapper(child, markStack);
1501 queue.append(child->children());
1502 }
1503}
1504
1506{
1507 QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
1508
1509 if (QObject *o = This->object()) {
1510 if (QQmlData *ddata = QQmlData::get(o)) {
1511 if (ddata->hasVMEMetaObject) {
1513 = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) {
1514 vme->mark(markStack);
1515 }
1516 }
1517
1518 // mark the const wrapper if our engine has interacted with it at some point
1521
1523 scope,
1525 static_cast<const QObject *>(o)));
1526
1527 if (constWrapper) {
1528 if (This == constWrapper->d()) {
1529 // We've got the const wrapper. Also mark the non-const one
1532 else
1534 } else {
1535 // We've got the non-const wrapper. Also mark the const one.
1537 }
1538 }
1539 }
1540 }
1541
1542 // Children usually don't need to be marked, the gc keeps them alive.
1543 // But in the rare case of a "floating" QObject without a parent that
1544 // _gets_ marked (we've been called here!) then we also need to
1545 // propagate the marking down to the children recursively.
1546 if (!o->parent())
1548 }
1549
1551}
1552
1554{
1555 Heap::QObjectWrapper *h = d();
1557
1558 if (QObject *o = h->object()) {
1559 QQmlData *ddata = QQmlData::get(o, false);
1560 if (ddata) {
1561 if (!o->parent() && !ddata->indestructible) {
1562 if (ddata && ddata->ownContext) {
1566 ddata->context = nullptr;
1567 }
1568
1569 // This object is notionally destroyed now. It might still live until the next
1570 // event loop iteration, but it won't need its connections, CU, or deferredData
1571 // anymore.
1572
1573 ddata->isQueuedForDeletion = true;
1576
1579
1580 if (lastCall)
1581 delete o;
1582 else
1583 o->deleteLater();
1584 } else {
1585 // If the object is C++-owned, we still have to release the weak reference we have
1586 // to it.
1590 }
1591 }
1592 }
1593
1594 h->destroy();
1595}
1596
1597
1599
1600namespace {
1601
1602template<typename... Types>
1603constexpr std::size_t MaxSizeOfN = (std::max)({sizeof(Types)...});
1604
1605struct CallArgument {
1606 Q_DISABLE_COPY_MOVE(CallArgument);
1607
1608 CallArgument() = default;
1609 ~CallArgument() { cleanup(); }
1610
1611 inline void *dataPtr();
1612
1613 inline void initAsType(QMetaType type);
1614 inline bool fromValue(QMetaType type, ExecutionEngine *, const Value &);
1615 inline ReturnedValue toValue(ExecutionEngine *);
1616
1617private:
1618 // QVariantWrappedType denotes that we're storing a QVariant, but we mean
1619 // the type inside the QVariant, not QVariant itself.
1620 enum { QVariantWrappedType = -1 };
1621
1622 inline void cleanup();
1623
1624 template <class T, class M>
1625 bool fromContainerValue(const Value &object, M CallArgument::*member);
1626
1627 union {
1628 float floatValue;
1629 double doubleValue;
1630 quint32 intValue;
1631 bool boolValue;
1632 QObject *qobjectPtr;
1633 std::vector<int> *stdVectorIntPtr;
1634 std::vector<qreal> *stdVectorRealPtr;
1635 std::vector<bool> *stdVectorBoolPtr;
1636 std::vector<QString> *stdVectorQStringPtr;
1637 std::vector<QUrl> *stdVectorQUrlPtr;
1638#if QT_CONFIG(qml_itemmodel)
1639 std::vector<QModelIndex> *stdVectorQModelIndexPtr;
1640#endif
1641
1642 char allocData[MaxSizeOfN<QVariant,
1643 QString,
1644 QList<QObject *>,
1645 QJSValue,
1646 QJSManagedValue,
1647 QJsonArray,
1648 QJsonObject,
1649 QJsonValue>];
1650 qint64 q_for_alignment;
1651 };
1652
1653 // Pointers to allocData
1654 union {
1655 QString *qstringPtr;
1656 QByteArray *qbyteArrayPtr;
1657 QVariant *qvariantPtr;
1658 QList<QObject *> *qlistPtr;
1659 QJSValue *qjsValuePtr;
1660 QJSManagedValue *qjsManagedValuePtr;
1661 QJsonArray *jsonArrayPtr;
1662 QJsonObject *jsonObjectPtr;
1663 QJsonValue *jsonValuePtr;
1664 };
1665
1666 int type = QMetaType::UnknownType;
1667};
1668}
1669
1670static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMetaType returnType, int argCount,
1671 const QMetaType *argTypes, ExecutionEngine *engine, CallData *callArgs,
1672 QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
1673{
1674 if (argCount > 0) {
1675 // Convert all arguments.
1676 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1677 args[0].initAsType(returnType);
1678 for (int ii = 0; ii < argCount; ++ii) {
1679 if (!args[ii + 1].fromValue(argTypes[ii], engine,
1680 callArgs->args[ii].asValue<Value>())) {
1681 qWarning() << QString::fromLatin1("Could not convert argument %1 from %2 to %3")
1682 .arg(ii).arg(callArgs->args[ii].asValue<Value>().toQStringNoThrow()).arg(argTypes[ii].name());
1683 const StackTrace stack = engine->stackTrace();
1684 for (const StackFrame &frame : stack) {
1685 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source
1686 + (frame.line > 0
1687 ? (QLatin1Char(':') + QString::number(frame.line))
1688 : QString());
1689
1690 }
1691
1692 const bool is_signal =
1693 object.metaObject()->method(index).methodType() == QMetaMethod::Signal;
1694 if (is_signal) {
1695 qWarning() << "Passing incompatible arguments to signals is not supported.";
1696 } else {
1697 return engine->throwTypeError(
1698 QLatin1String("Passing incompatible arguments to C++ functions from "
1699 "JavaScript is not allowed."));
1700 }
1701 }
1702 }
1703 QVarLengthArray<void *, 9> argData(args.size());
1704 for (int ii = 0; ii < args.size(); ++ii)
1705 argData[ii] = args[ii].dataPtr();
1706
1707 object.metacall(callType, index, argData.data());
1708
1709 return args[0].toValue(engine);
1710
1711 } else if (returnType != QMetaType::fromType<void>()) {
1712
1713 CallArgument arg;
1714 arg.initAsType(returnType);
1715
1716 void *args[] = { arg.dataPtr() };
1717
1718 object.metacall(callType, index, args);
1719
1720 return arg.toValue(engine);
1721
1722 } else {
1723
1724 void *args[] = { nullptr };
1725 object.metacall(callType, index, args);
1726 return Encode::undefined();
1727
1728 }
1729}
1730
1731template<typename Retrieve>
1732int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
1733 if (conversionMetaType == QMetaType::fromType<QVariant>())
1734 return 0;
1735
1736 const QMetaType type = retrieve();
1737 if (type == conversionMetaType)
1738 return 0;
1739
1740 if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
1741 if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject))
1742 return 1;
1743 }
1744
1745 if (QMetaType::canConvert(type, conversionMetaType)) {
1746 if (conversionMetaType == QMetaType::fromType<QJSValue>()
1747 || conversionMetaType == QMetaType::fromType<double>()
1748 || conversionMetaType == QMetaType::fromType<QString>()) {
1749 // Unspecific conversions receive lower score. You can convert anything
1750 // to QString or double via toString() and valueOf(), respectively.
1751 // And anything can be wrapped into QJSValue, but that's inefficient.
1752 return 6;
1753 }
1754
1755 // We have an explicitly defined conversion method to a non-boring type.
1756 return 5;
1757 }
1758
1759 return 10;
1760};
1761
1762/*
1763 Returns the match score for converting \a actual to be of type \a conversionType. A
1764 zero score means "perfect match" whereas a higher score is worse.
1765
1766 The conversion table is copied out of the \l QScript::callQtMethod() function.
1767*/
1768static int MatchScore(const Value &actual, QMetaType conversionMetaType)
1769{
1770 const int conversionType = conversionMetaType.id();
1771 const auto convertibleScore = [&](QMetaType actualType) {
1772 // There are a number of things we can do in JavaScript to subvert this, but
1773 // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
1774 if (!QMetaType::canConvert(actualType, conversionMetaType))
1775 return 10;
1776
1777 // You can convert anything to QJSValue, but that's inefficient.
1778 // If we have a better option, we should use it.
1779 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1780 return 9;
1781
1782 // You can also convert anything to QVariant, but that's also suboptimal.
1783 // You can convert anything to string or double via toString() and valueOf().
1784 // Those are also rather unspecific.
1785 switch (conversionType) {
1786 case QMetaType::QVariant:
1787 case QMetaType::Double:
1788 case QMetaType::QString:
1789 return 9;
1790 default:
1791 break;
1792 }
1793
1794 // We have an explicitly defined conversion method to a non-boring type.
1795 return 8;
1796 };
1797
1798 if (actual.isNumber()) {
1799 switch (conversionType) {
1800 case QMetaType::Double:
1801 return 0;
1802 case QMetaType::Float:
1803 return 1;
1804 case QMetaType::LongLong:
1805 case QMetaType::ULongLong:
1806 return 2;
1807 case QMetaType::Long:
1808 case QMetaType::ULong:
1809 return 3;
1810 case QMetaType::Int:
1811 case QMetaType::UInt:
1812 return 4;
1813 case QMetaType::Short:
1814 case QMetaType::UShort:
1815 return 5;
1816 break;
1817 case QMetaType::Char:
1818 case QMetaType::UChar:
1819 return 6;
1820 case QMetaType::QJsonValue:
1821 return 5;
1822 default:
1823 return convertibleScore(actual.isInteger()
1824 ? QMetaType::fromType<int>()
1825 : QMetaType::fromType<double>());
1826 }
1827 } else if (actual.isString()) {
1828 switch (conversionType) {
1829 case QMetaType::QString:
1830 return 0;
1831 case QMetaType::QJsonValue:
1832 return 5;
1833 case QMetaType::QUrl:
1834 return 6; // we like to convert strings to URLs in QML
1835 case QMetaType::Double:
1836 case QMetaType::Float:
1837 case QMetaType::LongLong:
1838 case QMetaType::ULongLong:
1839 case QMetaType::Int:
1840 case QMetaType::UInt:
1841 case QMetaType::Short:
1842 case QMetaType::UShort:
1843 case QMetaType::Char:
1844 case QMetaType::UChar:
1845 // QMetaType can natively convert strings to numbers.
1846 // However, in the general case it's of course extremely lossy.
1847 return 10;
1848 default:
1849 return convertibleScore(QMetaType::fromType<QString>());
1850 }
1851 } else if (actual.isBoolean()) {
1852 switch (conversionType) {
1853 case QMetaType::Bool:
1854 return 0;
1855 case QMetaType::QJsonValue:
1856 return 5;
1857 default:
1858 return convertibleScore(QMetaType::fromType<bool>());
1859 }
1860 } else if (actual.as<DateObject>()) {
1861 switch (conversionType) {
1862 case QMetaType::QDateTime:
1863 return 0;
1864 case QMetaType::QDate:
1865 return 1;
1866 case QMetaType::QTime:
1867 return 2;
1868 default:
1869 return convertibleScore(QMetaType::fromType<QDateTime>());
1870 }
1871 } else if (actual.as<RegExpObject>()) {
1872 switch (conversionType) {
1873#if QT_CONFIG(regularexpression)
1874 case QMetaType::QRegularExpression:
1875 return 0;
1876 default:
1877 return convertibleScore(QMetaType::fromType<QRegularExpression>());
1878#else
1879 default:
1880 return convertibleScore(QMetaType());
1881#endif
1882 }
1883 } else if (actual.as<ArrayBuffer>()) {
1884 switch (conversionType) {
1885 case QMetaType::QByteArray:
1886 return 0;
1887 default:
1888 return convertibleScore(QMetaType::fromType<QByteArray>());
1889 }
1890 } else if (actual.as<ArrayObject>()) {
1891 switch (conversionType) {
1892 case QMetaType::QJsonArray:
1893 return 3;
1894 case QMetaType::QStringList:
1895 case QMetaType::QVariantList:
1896 return 5;
1897 case QMetaType::QVector4D:
1898 case QMetaType::QMatrix4x4:
1899 return 6;
1900 case QMetaType::QVector3D:
1901 return 7;
1902 default:
1903 return convertibleScore(QMetaType());
1904 }
1905 } else if (actual.isNull()) {
1906 switch (conversionType) {
1907 case QMetaType::Nullptr:
1908 case QMetaType::VoidStar:
1909 case QMetaType::QObjectStar:
1910 case QMetaType::QJsonValue:
1911 return 0;
1912 default: {
1913 if (conversionMetaType.flags().testFlag(QMetaType::IsPointer))
1914 return 0;
1915 else
1916 return convertibleScore(QMetaType());
1917 }
1918 }
1919 } else if (const Object *obj = actual.as<Object>()) {
1920 if (const VariantObject *variantObject = obj->as<VariantObject>()) {
1921 return MatchVariant(conversionMetaType, [variantObject]() {
1922 return variantObject->d()->data().metaType();
1923 });
1924 }
1925
1926 if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
1927 switch (conversionType) {
1928 case QMetaType::QObjectStar:
1929 return 0;
1930 default:
1931 if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
1932 QObject *wrapped = wrapper->object();
1933 if (!wrapped)
1934 return 0;
1935 if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject()))
1936 return 0;
1937 }
1938 }
1939
1940 return convertibleScore(QMetaType::fromType<QObject *>());
1941 }
1942
1943 if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
1944 const QQmlType type = wrapper->d()->type();
1945 if (type.isSingleton()) {
1946 const QMetaType metaType = type.typeId();
1947 if (metaType == conversionMetaType)
1948 return 0;
1949
1950 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1951 && metaType.flags() & QMetaType::PointerToQObject
1952 && type.metaObject()->inherits(conversionMetaType.metaObject())) {
1953 return 0;
1954 }
1955 } else if (QObject *object = wrapper->object()) {
1956 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1957 && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) {
1958 return 0;
1959 }
1960 }
1961
1962 return convertibleScore(QMetaType());
1963 }
1964
1965 if (const Sequence *sequence = obj->as<Sequence>()) {
1966 const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(sequence);
1967 if (sequenceType == conversionMetaType)
1968 return 1;
1969
1970 return convertibleScore(sequenceType);
1971 }
1972
1973 if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
1974 return MatchVariant(conversionMetaType, [wrapper]() {
1975 return wrapper->d()->isVariant()
1976 ? wrapper->toVariant().metaType()
1977 : wrapper->type();
1978 });
1979 }
1980
1981 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1982 return 0;
1983
1984 switch (conversionType) {
1985 case QMetaType::QJsonObject:
1986 case QMetaType::QVariantMap:
1987 return 5;
1988 default:
1989 break;
1990 }
1991
1992 }
1993
1994 return convertibleScore(QMetaType());
1995}
1996
1997static int numDefinedArguments(CallData *callArgs)
1998{
1999 int numDefinedArguments = callArgs->argc();
2000 while (numDefinedArguments > 0
2001 && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) {
2002 --numDefinedArguments;
2003 }
2004 return numDefinedArguments;
2005}
2006
2007static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
2008{
2009 const QMetaObject *metaObject = object.metaObject();
2010 const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
2011 return indexOfClassInfo != -1
2012 && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
2013}
2014
2018{
2020
2022
2023 if (!returnType.isValid()) {
2024 return engine->throwError(QLatin1String("Unknown method return type: ")
2026 }
2027
2030 engine->throwError(QStringLiteral("Too many arguments"));
2031 return false;
2032 }
2033
2034 const auto stackTrace = engine->stackTrace();
2035 if (stackTrace.isEmpty()) {
2037 << "When matching arguments for "
2038 << object.className() << "::" << data.name(object.metaObject()) << "():";
2039 } else {
2040 const StackFrame frame = stackTrace.first();
2042 + (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
2043 : QString());
2044 }
2045
2046 qWarning().noquote() << QStringLiteral("Too many arguments, ignoring %1")
2048 return true;
2049 };
2050
2052
2053 if (data.hasArguments()) {
2054
2056
2057 bool ok = false;
2058 if (data.isConstructor())
2060 else
2062
2063 if (!ok) {
2064 return engine->throwError(QLatin1String("Unknown method parameter type: ")
2066 }
2067
2068 if (storage.size() > callArgs->argc()) {
2069 QString error = QLatin1String("Insufficient arguments");
2070 return engine->throwError(error);
2071 }
2072
2075 return Encode::undefined();
2076
2077 }
2078
2080
2081 } else {
2083 return Encode::undefined();
2084
2085 return CallMethod(object, data.coreIndex(), returnType, 0, nullptr, engine, callArgs, callType);
2086 }
2087}
2088
2089/*
2090Resolve the overloaded method to call. The algorithm works conceptually like this:
2091 1. Resolve the set of overloads it is *possible* to call.
2092 Impossible overloads include those that have too many parameters or have parameters
2093 of unknown type.
2094 2. Filter the set of overloads to only contain those with the closest number of
2095 parameters.
2096 For example, if we are called with 3 parameters and there are 2 overloads that
2097 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
2098 3. Find the best remaining overload based on its match score.
2099 If two or more overloads have the same match score, return the last one. The match
2100 score is constructed by adding the matchScore() result for each of the parameters.
2101*/
2105{
2106 const int argumentCount = callArgs->argc();
2108
2109 const QQmlPropertyData *best = nullptr;
2113
2116
2117 for (int i = 0; i < methodCount; ++i) {
2118 const QQmlPropertyData *attempt = methods + i;
2119
2125 qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
2126 }
2127
2128 // QQmlV4Function overrides anything that doesn't provide the exact number of arguments
2129 int methodParameterScore = 1;
2130 // QQmlV4Function overrides the "no idea" option, which is 10
2131 int maxMethodMatchScore = 9;
2132 // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments
2134
2135 if (!attempt->isV4Function()) {
2137 int methodArgumentCount = 0;
2138 if (attempt->hasArguments()) {
2139 if (attempt->isConstructor()) {
2141 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2142 continue;
2143 }
2144 } else {
2145 if (!object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr)) {
2146 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2147 continue;
2148 }
2149 }
2151 }
2152
2154 qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
2155 continue; // We don't have sufficient arguments to call this method
2156 }
2157
2159 ? 0
2162 qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
2163 continue; // We already have a better option
2164 }
2165
2168 for (int ii = 0; ii < methodArgumentCount; ++ii) {
2169 const int score = MatchScore((v = Value::fromStaticValue(callArgs->args[ii])),
2170 storage[ii]);
2173 }
2174 }
2175
2180 best = attempt;
2184 qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
2185 << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
2186 << "bestSumMatchScore" << bestSumMatchScore << "\n";
2187 } else {
2188 qCDebug(lcOverloadResolution) << "did not update best\n"
2189 << "bestParameterScore" << bestParameterScore << "\t"
2190 << "methodParameterScore" << methodParameterScore << "\n"
2191 << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
2192 << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
2193 << "bestSumMatchScore" << bestSumMatchScore << "\t"
2194 << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
2195 }
2196
2197 if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
2198 qCDebug(lcOverloadResolution, "perfect match");
2199 break; // We can't get better than that
2200 }
2201
2202 };
2203
2204 if (best && best->isValid()) {
2205 return best;
2206 } else {
2207 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
2208 for (int i = 0; i < methodCount; ++i) {
2213 error += u"\n " + QString::fromUtf8(m.methodSignature());
2214 }
2215
2217 return nullptr;
2218 }
2219}
2220
2221static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
2222{
2223 if (required == QMetaType::fromType<QVariant>()
2224 || required == QMetaType::fromType<QJSValue>()
2225 || required == QMetaType::fromType<QJSManagedValue>()) {
2226 return true;
2227 }
2228
2229 if (data) {
2230 if (passed == QMetaType::fromType<QVariant>())
2231 passed = static_cast<const QVariant *>(data)->metaType();
2232 else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
2233 passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
2234 }
2235
2236 if (passed == required)
2237 return true;
2238
2239 if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
2240 switch (passed.id()) {
2241 case QMetaType::UnknownType:
2242 case QMetaType::Nullptr:
2243 case QMetaType::Bool:
2244 case QMetaType::Int:
2245 case QMetaType::Double:
2246 case QMetaType::QString:
2247 return true;
2248 default:
2249 break;
2250 }
2251 }
2252
2253 return false;
2254}
2255
2257 const QMetaMethod &method, void **argv, int argc, const QMetaType *types)
2258{
2259 if (types[0].isValid() && !ExactMatch(method.returnMetaType(), types[0], nullptr))
2260 return false;
2261
2262 if (method.parameterCount() != argc)
2263 return false;
2264
2265 for (int i = 0; i < argc; ++i) {
2266 if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1]))
2267 return false;
2268 }
2269
2270 return true;
2271}
2272
2275 void **argv, int argc, const QMetaType *types)
2276{
2277 // We only accept exact matches here. Everything else goes through the JavaScript conversion.
2278 for (int i = 0; i < methodCount; ++i) {
2279 const QQmlPropertyData *attempt = methods + i;
2281 return attempt;
2282 }
2283
2284 return nullptr;
2285}
2286
2287void CallArgument::cleanup()
2288{
2289 switch (type) {
2290 case QMetaType::QString:
2291 qstringPtr->~QString();
2292 break;
2293 case QMetaType::QByteArray:
2294 qbyteArrayPtr->~QByteArray();
2295 break;
2296 case QMetaType::QVariant:
2297 case QVariantWrappedType:
2298 qvariantPtr->~QVariant();
2299 break;
2300 case QMetaType::QJsonArray:
2301 jsonArrayPtr->~QJsonArray();
2302 break;
2303 case QMetaType::QJsonObject:
2304 jsonObjectPtr->~QJsonObject();
2305 break;
2306 case QMetaType::QJsonValue:
2307 jsonValuePtr->~QJsonValue();
2308 break;
2309 default:
2310 if (type == qMetaTypeId<QJSValue>()) {
2311 qjsValuePtr->~QJSValue();
2312 break;
2313 }
2314
2315 if (type == qMetaTypeId<QJSManagedValue>()) {
2316 qjsManagedValuePtr->~QJSManagedValue();
2317 break;
2318 }
2319
2320 if (type == qMetaTypeId<QList<QObject *> >()) {
2321 qlistPtr->~QList<QObject *>();
2322 break;
2323 }
2324
2325 // The sequence types need no cleanup because we don't own them.
2326
2327 break;
2328 }
2329}
2330
2331void *CallArgument::dataPtr()
2332{
2333 switch (type) {
2334 case QMetaType::UnknownType:
2335 return nullptr;
2336 case QVariantWrappedType:
2337 return qvariantPtr->data();
2338 default:
2339 if (type == qMetaTypeId<std::vector<int>>())
2340 return stdVectorIntPtr;
2341 if (type == qMetaTypeId<std::vector<qreal>>())
2342 return stdVectorRealPtr;
2343 if (type == qMetaTypeId<std::vector<bool>>())
2344 return stdVectorBoolPtr;
2345 if (type == qMetaTypeId<std::vector<QString>>())
2346 return stdVectorQStringPtr;
2347 if (type == qMetaTypeId<std::vector<QUrl>>())
2348 return stdVectorQUrlPtr;
2349#if QT_CONFIG(qml_itemmodel)
2350 if (type == qMetaTypeId<std::vector<QModelIndex>>())
2351 return stdVectorQModelIndexPtr;
2352#endif
2353 break;
2354 }
2355
2356 return (void *)&allocData;
2357}
2358
2359void CallArgument::initAsType(QMetaType metaType)
2360{
2361 if (type != QMetaType::UnknownType)
2362 cleanup();
2363
2364 type = metaType.id();
2365 switch (type) {
2366 case QMetaType::Void:
2367 type = QMetaType::UnknownType;
2368 break;
2369 case QMetaType::UnknownType:
2370 case QMetaType::Int:
2371 case QMetaType::UInt:
2372 case QMetaType::Bool:
2373 case QMetaType::Double:
2374 case QMetaType::Float:
2375 break;
2376 case QMetaType::QObjectStar:
2377 qobjectPtr = nullptr;
2378 break;
2379 case QMetaType::QString:
2380 qstringPtr = new (&allocData) QString();
2381 break;
2382 case QMetaType::QVariant:
2383 qvariantPtr = new (&allocData) QVariant();
2384 break;
2385 case QMetaType::QJsonArray:
2386 jsonArrayPtr = new (&allocData) QJsonArray();
2387 break;
2388 case QMetaType::QJsonObject:
2389 jsonObjectPtr = new (&allocData) QJsonObject();
2390 break;
2391 case QMetaType::QJsonValue:
2392 jsonValuePtr = new (&allocData) QJsonValue();
2393 break;
2394 default: {
2395 if (metaType == QMetaType::fromType<QJSValue>()) {
2396 qjsValuePtr = new (&allocData) QJSValue();
2397 break;
2398 }
2399
2400 if (metaType == QMetaType::fromType<QJSManagedValue>()) {
2401 qjsManagedValuePtr = new (&allocData) QJSManagedValue();
2402 break;
2403 }
2404
2405 if (metaType == QMetaType::fromType<QList<QObject *>>()) {
2406 qlistPtr = new (&allocData) QList<QObject *>();
2407 break;
2408 }
2409
2410 type = QVariantWrappedType;
2411 qvariantPtr = new (&allocData) QVariant(metaType, (void *)nullptr);
2412 break;
2413 }
2414 }
2415}
2416
2417template <class T, class M>
2418bool CallArgument::fromContainerValue(const Value &value, M CallArgument::*member)
2419{
2420 if (T* ptr = static_cast<T *>(SequencePrototype::rawContainerPtr(
2421 value.as<Sequence>(), QMetaType(type)))) {
2422 (this->*member) = ptr;
2423 return true;
2424 }
2425 (this->*member) = nullptr;
2426 return false;
2427}
2428
2429bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const Value &value)
2430{
2431 if (type != QMetaType::UnknownType)
2432 cleanup();
2433
2434 type = metaType.id();
2435
2436 switch (type) {
2437 case QMetaType::Int:
2438 intValue = quint32(value.toInt32());
2439 return true;
2440 case QMetaType::UInt:
2441 intValue = quint32(value.toUInt32());
2442 return true;
2443 case QMetaType::Bool:
2444 boolValue = value.toBoolean();
2445 return true;
2446 case QMetaType::Double:
2447 doubleValue = double(value.toNumber());
2448 return true;
2449 case QMetaType::Float:
2450 floatValue = float(value.toNumber());
2451 return true;
2452 case QMetaType::QString:
2453 if (value.isNullOrUndefined())
2454 qstringPtr = new (&allocData) QString();
2455 else
2456 qstringPtr = new (&allocData) QString(value.toQStringNoThrow());
2457 return true;
2458 case QMetaType::QByteArray:
2459 qbyteArrayPtr = new (&allocData) QByteArray();
2460 ExecutionEngine::metaTypeFromJS(value, metaType, qbyteArrayPtr);
2461 return true;
2462 case QMetaType::QObjectStar:
2463 if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) {
2464 qobjectPtr = qobjectWrapper->object();
2465 return true;
2466 }
2467
2468 if (const QQmlTypeWrapper *qmlTypeWrapper = value.as<QQmlTypeWrapper>()) {
2469 if (qmlTypeWrapper->isSingleton()) {
2470 // Convert via QVariant below.
2471 // TODO: Can't we just do qobjectPtr = qmlTypeWrapper->object() instead?
2472 break;
2473 } else if (QObject *obj = qmlTypeWrapper->object()) {
2474 // attached object case
2475 qobjectPtr = obj;
2476 return true;
2477 }
2478
2479 // If this is a plain type wrapper without an instance,
2480 // then we got a namespace, and that's a type error
2481 type = QMetaType::UnknownType;
2482 return false;
2483 }
2484
2485 qobjectPtr = nullptr;
2486 return value.isNullOrUndefined(); // null and undefined are nullptr
2487 case QMetaType::QVariant:
2488 qvariantPtr = new (&allocData) QVariant(ExecutionEngine::toVariant(value, QMetaType {}));
2489 return true;
2490 case QMetaType::QJsonArray: {
2491 Scope scope(engine);
2492 ScopedObject o(scope, value);
2493 jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(o));
2494 return true;
2495 }
2496 case QMetaType::QJsonObject: {
2497 Scope scope(engine);
2498 ScopedObject o(scope, value);
2499 jsonObjectPtr = new (&allocData) QJsonObject(JsonObject::toJsonObject(o));
2500 return true;
2501 }
2502 case QMetaType::QJsonValue:
2503 jsonValuePtr = new (&allocData) QJsonValue(JsonObject::toJsonValue(value));
2504 return true;
2505 case QMetaType::Void:
2506 type = QMetaType::UnknownType;
2507 // TODO: This only doesn't leak because a default constructed QVariant doesn't allocate.
2508 *qvariantPtr = QVariant();
2509 return true;
2510 default:
2511 if (type == qMetaTypeId<QJSValue>()) {
2512 qjsValuePtr = new (&allocData) QJSValue;
2513 Scope scope(engine);
2514 ScopedValue v(scope, value);
2515 QJSValuePrivate::setValue(qjsValuePtr, v);
2516 return true;
2517 }
2518
2519 if (type == qMetaTypeId<QJSManagedValue>()) {
2520 Scope scope(engine);
2521 ScopedValue v(scope, value);
2522 qjsManagedValuePtr = new (&allocData) QJSManagedValue;
2523 // This points to a JS heap object that cannot be immutable. const_cast-ing is fine here.
2524 *QJSManagedValuePrivate::memberPtr(qjsManagedValuePtr) = const_cast<Value *>(&value);
2525 return true;
2526 }
2527
2528 if (type == qMetaTypeId<QList<QObject*> >()) {
2529 qlistPtr = new (&allocData) QList<QObject *>();
2530 Scope scope(engine);
2531 ScopedArrayObject array(scope, value);
2532 if (array) {
2533 Scoped<QObjectWrapper> qobjectWrapper(scope);
2534
2535 uint length = array->getLength();
2536 qlistPtr->reserve(length);
2537 for (uint ii = 0; ii < length; ++ii) {
2538 QObject *o = nullptr;
2539 qobjectWrapper = array->get(ii);
2540 if (!!qobjectWrapper)
2541 o = qobjectWrapper->object();
2542 qlistPtr->append(o);
2543 }
2544 return true;
2545 }
2546
2547 if (const auto sequence = value.as<QV4::Sequence>()) {
2548
2549 // Does readReference(). Don't move past getRawContainer()
2550 const qint64 length = sequence->getLength();
2551
2552 switch (QV4::SequencePrototype::getRawContainer(
2553 sequence, qlistPtr, QMetaType::fromType<QList<QObject *>>())) {
2554 case SequencePrototype::Copied:
2555 case SequencePrototype::WasEqual:
2556 break;
2557 case SequencePrototype::TypeMismatch: {
2558 if (!qIsAtMostSizetypeLimit(length) || !qIsAtMostUintLimit(length))
2559 return false;
2560
2561 qlistPtr->reserve(length);
2562 Scoped<QObjectWrapper> qobjectWrapper(scope);
2563 for (uint ii = 0; ii < length; ++ii) {
2564 QObject *o = nullptr;
2565 qobjectWrapper = sequence->get(ii);
2566 if (!!qobjectWrapper)
2567 o = qobjectWrapper->object();
2568 qlistPtr->append(o);
2569 }
2570 break;
2571 }
2572 }
2573 return true;
2574 }
2575
2576 if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) {
2577 qlistPtr->append(qobjectWrapper->object());
2578 return true;
2579 }
2580
2581 if (const QmlListWrapper *listWrapper = value.as<QmlListWrapper>()) {
2582 *qlistPtr = listWrapper->d()->property()->toList<QList<QObject *>>();
2583 return true;
2584 }
2585
2586 qlistPtr->append(nullptr);
2587 return value.isNullOrUndefined();
2588 }
2589
2590 if (metaType.flags() & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) {
2591 // You can assign null or undefined to any pointer. The result is a nullptr.
2592 if (value.isNullOrUndefined()) {
2593 qvariantPtr = new (&allocData) QVariant(metaType, nullptr);
2594 return true;
2595 }
2596 break;
2597 }
2598
2599 if (type == qMetaTypeId<std::vector<int>>()) {
2600 if (fromContainerValue<std::vector<int>>(value, &CallArgument::stdVectorIntPtr))
2601 return true;
2602 } else if (type == qMetaTypeId<std::vector<qreal>>()) {
2603 if (fromContainerValue<std::vector<qreal>>(value, &CallArgument::stdVectorRealPtr))
2604 return true;
2605 } else if (type == qMetaTypeId<std::vector<bool>>()) {
2606 if (fromContainerValue<std::vector<bool>>(value, &CallArgument::stdVectorBoolPtr))
2607 return true;
2608 } else if (type == qMetaTypeId<std::vector<QString>>()) {
2609 if (fromContainerValue<std::vector<QString>>(value, &CallArgument::stdVectorQStringPtr))
2610 return true;
2611 } else if (type == qMetaTypeId<std::vector<QUrl>>()) {
2612 if (fromContainerValue<std::vector<QUrl>>(value, &CallArgument::stdVectorQUrlPtr))
2613 return true;
2614#if QT_CONFIG(qml_itemmodel)
2615 } else if (type == qMetaTypeId<std::vector<QModelIndex>>()) {
2616 if (fromContainerValue<std::vector<QModelIndex>>(
2617 value, &CallArgument::stdVectorQModelIndexPtr)) {
2618 return true;
2619 }
2620#endif
2621 }
2622 break;
2623 }
2624
2625 // Convert via QVariant through the QML engine.
2626 qvariantPtr = new (&allocData) QVariant(metaType);
2627 type = QVariantWrappedType;
2628
2629 if (ExecutionEngine::metaTypeFromJS(value, metaType, qvariantPtr->data()))
2630 return true;
2631
2632 const QVariant v = ExecutionEngine::toVariant(value, metaType);
2633 return QMetaType::convert(v.metaType(), v.constData(), metaType, qvariantPtr->data());
2634}
2635
2636ReturnedValue CallArgument::toValue(ExecutionEngine *engine)
2637{
2638 switch (type) {
2639 case QMetaType::Int:
2640 return Encode(int(intValue));
2641 case QMetaType::UInt:
2642 return Encode((uint)intValue);
2643 case QMetaType::Bool:
2644 return Encode(boolValue);
2645 case QMetaType::Double:
2646 return Encode(doubleValue);
2647 case QMetaType::Float:
2648 return Encode(floatValue);
2649 case QMetaType::QString:
2650 return Encode(engine->newString(*qstringPtr));
2651 case QMetaType::QByteArray:
2652 return Encode(engine->newArrayBuffer(*qbyteArrayPtr));
2653 case QMetaType::QObjectStar:
2654 if (qobjectPtr)
2655 QQmlData::get(qobjectPtr, true)->setImplicitDestructible();
2656 return QObjectWrapper::wrap(engine, qobjectPtr);
2657 case QMetaType::QJsonArray:
2658 return JsonObject::fromJsonArray(engine, *jsonArrayPtr);
2659 case QMetaType::QJsonObject:
2660 return JsonObject::fromJsonObject(engine, *jsonObjectPtr);
2661 case QMetaType::QJsonValue:
2662 return JsonObject::fromJsonValue(engine, *jsonValuePtr);
2663 case QMetaType::QVariant:
2664 case QVariantWrappedType: {
2665 Scope scope(engine);
2666 ScopedValue rv(scope, scope.engine->fromVariant(*qvariantPtr));
2667 Scoped<QObjectWrapper> qobjectWrapper(scope, rv);
2668 if (!!qobjectWrapper) {
2669 if (QObject *object = qobjectWrapper->object())
2670 QQmlData::get(object, true)->setImplicitDestructible();
2671 }
2672 return rv->asReturnedValue();
2673 }
2674 default:
2675 break;
2676 }
2677
2678 if (type == qMetaTypeId<QJSValue>()) {
2679 // The QJSValue can be passed around via dataPtr()
2680 QJSValuePrivate::manageStringOnV4Heap(engine, qjsValuePtr);
2681 return QJSValuePrivate::asReturnedValue(qjsValuePtr);
2682 }
2683
2684 if (type == qMetaTypeId<QJSManagedValue>())
2685 return QJSManagedValuePrivate::member(qjsManagedValuePtr)->asReturnedValue();
2686
2687 if (type == qMetaTypeId<QList<QObject *> >()) {
2688 // XXX Can this be made more by using Array as a prototype and implementing
2689 // directly against QList<QObject*>?
2690 QList<QObject *> &list = *qlistPtr;
2691 Scope scope(engine);
2692 ScopedArrayObject array(scope, engine->newArrayObject());
2693 array->arrayReserve(list.size());
2694 ScopedValue v(scope);
2695 for (int ii = 0; ii < list.size(); ++ii)
2696 array->arrayPut(ii, (v = QObjectWrapper::wrap(engine, list.at(ii))));
2697 array->setArrayLengthUnchecked(list.size());
2698 return array.asReturnedValue();
2699 }
2700
2701 return Encode::undefined();
2702}
2703
2712
2722
2726{
2728
2730 if (cloneFrom->wrapper) {
2732 if (ref) {
2734 } else {
2735 // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what
2736 // value we should operate on. Without knowledge of the property the value
2737 // was read from, we cannot load the value from the given object.
2738 return Encode::undefined();
2739 }
2740 }
2741
2743 valueScope,
2746
2748
2749 Q_ASSERT(method->d()->methods == nullptr);
2750 switch (cloneFrom->methodCount) {
2751 case 0:
2752 Q_ASSERT(cloneFrom->methods == nullptr);
2753 break;
2754 case 1:
2756 == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
2757 method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
2758 *method->d()->methods = *cloneFrom->methods;
2759 break;
2760 default:
2761 Q_ASSERT(cloneFrom->methods != nullptr);
2765 break;
2766 }
2767
2768 return method.asReturnedValue();
2769}
2770
2772{
2776}
2777
2779{
2781
2783 return valueWrapper->metaObject();
2784 if (QObject *self = object())
2785 return self->metaObject();
2786
2787 return nullptr;
2788}
2789
2791{
2793
2795 return objectWrapper->object();
2797 return typeWrapper->object();
2798 return nullptr;
2799}
2800
2801bool Heap::QObjectMethod::isDetached() const
2802{
2803 if (!wrapper)
2804 return true;
2805
2808 return valueWrapper->d()->object() == nullptr;
2809
2810 return false;
2811}
2812
2814{
2817 return objectWrapper->object() == o;
2819 return typeWrapper->object() == o;
2820
2824 return qobject->object() == o;
2826 return type->object() == o;
2827
2828 // Attached to some nested value type or sequence object
2829 return false;
2830 }
2831
2832 return false;
2833}
2834
2836 const QMetaObject *thisMeta) const
2837{
2838 // Check that the metaobject matches.
2839
2840 if (!thisMeta) {
2841 // You can only get a detached method via a lookup, and then you have a thisObject.
2843 return Included;
2844 }
2845
2846 const auto check = [&](const QMetaObject *included) {
2851 "%s:%d: Calling C++ methods with 'this' objects different from the one "
2852 "they were retrieved from is broken, due to historical reasons. The "
2853 "original object is used as 'this' object. You can allow the given "
2854 "'this' object to be used by setting "
2855 "'pragma NativeMethodBehavior: AcceptThisObject'",
2857 return Included;
2858 }
2859
2860 // destroy() and toString() can be called on all QObjects, but not on gadgets.
2861 if (index < 0)
2863
2864 // Find the base type the method belongs to.
2866 while (true) {
2867 if (included == thisMeta)
2868 return Explicit;
2869
2870 if (methodOffset <= index)
2872
2876 };
2877
2879 };
2880
2881 if (const QMetaObject *meta = metaObject())
2882 return check(meta);
2883
2884 // If the QObjectMethod is detached, we can only have gotten here via a lookup.
2885 // The lookup checks that the QQmlPropertyCache matches.
2886 return Explicit;
2887}
2888
2890{
2892 return QStringLiteral("destroy");
2893 else if (index == QV4::QObjectMethod::ToStringMethod)
2894 return QStringLiteral("toString");
2895
2896 const QMetaObject *mo = metaObject();
2897 if (!mo)
2898 return QString();
2899
2900 int methodOffset = mo->methodOffset();
2901 while (methodOffset > index) {
2902 mo = mo->superClass();
2904 }
2905
2906 return "%1::%2"_L1.arg(QLatin1StringView{mo->className()},
2908}
2909
2911{
2912 if (methods) {
2913 Q_ASSERT(methodCount > 0);
2914 return;
2915 }
2916
2917 const QMetaObject *mo = metaObject();
2918
2919 if (!mo)
2920 mo = thisMeta;
2921
2922 Q_ASSERT(mo);
2923
2924 int methodOffset = mo->methodOffset();
2925 while (methodOffset > index) {
2926 mo = mo->superClass();
2928 }
2932 dummy.load(method);
2935 // Look for overloaded methods
2937 for (int ii = index - 1; ii >= methodOffset; --ii) {
2938 if (methodName == mo->method(ii).name()) {
2939 method = mo->method(ii);
2940 dummy.load(method);
2942 }
2943 }
2944 if (resolvedMethods.size() > 1) {
2948 } else {
2949 methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod);
2951 methodCount = 1;
2952 }
2953
2954 Q_ASSERT(methodCount > 0);
2955}
2956
2963
2965 ExecutionEngine *engine, QObject *o, const Value *args, int argc) const
2966{
2967 method_destroy(engine, o, argc > 0 ? args[0].toInt32() : 0);
2968 return Encode::undefined();
2969}
2970
2972{
2973 if (!o)
2974 return true;
2975
2977 engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2978 return false;
2979 }
2980
2981 if (delay > 0)
2983 else
2984 o->deleteLater();
2985
2986 return true;
2987}
2988
2990 const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2991{
2992 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2994}
2995
2997 const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
2998{
2999 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
3001}
3002
3004{
3006
3007 const QMetaObject *thisMeta = nullptr;
3008
3009 QObject *o = nullptr;
3011 if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
3012 thisMeta = w->metaObject();
3013 o = w->object();
3014 } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
3015 thisMeta = w->metaObject();
3016 o = w->object();
3017 } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
3018 thisMeta = w->metaObject();
3019 valueWrapper = w->d();
3020 }
3021
3023 if (o && o == d()->object()) {
3025 // Nothing to do; objects are the same. This should be common
3026 } else if (valueWrapper && valueWrapper == d()->wrapper) {
3028 // Nothing to do; gadgets are the same. This should be somewhat common
3029 } else {
3031 if (mode == Heap::QObjectMethod::Invalid) {
3032 v4->throwError(QLatin1String("Cannot call method %1 on %2").arg(
3033 d()->name(), thisObject->toQStringNoThrow()));
3034 return Encode::undefined();
3035 }
3036 }
3037
3038 QQmlObjectOrGadget object = [&](){
3039 if (mode == Heap::QObjectMethod::Included) {
3040 QV4::Scope scope(v4);
3044 return QQmlObjectOrGadget(type->object());
3046 valueWrapper = value->d();
3048 }
3049 Q_UNREACHABLE();
3050 } else {
3051 if (o)
3052 return QQmlObjectOrGadget(o);
3053
3058 }
3059 }();
3060
3061 if (object.isNull())
3062 return Encode::undefined();
3063
3064 if (d()->index == DestroyMethod)
3065 return method_destroy(v4, object.qObject(), argv, argc);
3066 else if (d()->index == ToStringMethod)
3067 return method_toString(v4, object.qObject());
3068
3070
3071 Scope scope(v4);
3074
3075 const QQmlPropertyData *method = d()->methods;
3076
3077 // If we call the method, we have to write back any value type references afterwards.
3078 // The method might change the value.
3079 const auto doCall = [&](const auto &call) {
3080 if (!method->isConstant()) {
3084 return rv->asReturnedValue();
3085 }
3086 }
3087
3088 return call();
3089 };
3090
3091 if (d()->methodCount != 1) {
3092 Q_ASSERT(d()->methodCount > 0);
3094 if (method == nullptr)
3095 return Encode::undefined();
3096 }
3097
3098 if (method->isV4Function()) {
3099 return doCall([&]() {
3103
3104 void *args[] = { nullptr, &funcptr };
3106
3107 return rv->asReturnedValue();
3108 });
3109 }
3110
3111 return doCall([&]() { return callPrecise(object, *method, v4, callData); });
3112}
3113
3115{
3116 constexpr int parameterCount() const { return 0; }
3117 constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
3118 constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
3119};
3120
3122 QObject *thisObject, void **argv, const QMetaType *types, int argc) const
3123{
3125
3126 const QMetaObject *thisMeta = nullptr;
3128
3129 if (thisObject) {
3131 } else {
3135 }
3136
3137 QQmlObjectOrGadget object = [&](){
3138 if (thisObject)
3140
3141 Scope scope(v4);
3144
3149 }();
3150
3151 if (object.isNull())
3152 return;
3153
3154 if (d()->index == DestroyMethod) {
3155 // method_destroy will use at most one argument
3157 v4, thisObject, argv, types, std::min(argc, 1),
3158 [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
3160 return method_destroy(v4, object.qObject(), argv, argc);
3161 });
3162 return;
3163 }
3164
3165 if (d()->index == ToStringMethod) {
3169 [v4, thisMeta, object](void **argv, int) {
3170 *static_cast<QString *>(argv[0])
3172 });
3173 return;
3174 }
3175
3177
3178 const QQmlPropertyData *method = d()->methods;
3179 if (d()->methodCount != 1) {
3180 Q_ASSERT(d()->methodCount > 0);
3182 }
3183
3184 if (!method || method->isV4Function()) {
3187 [this](const Value *thisObject, const Value *argv, int argc) {
3188 return callInternal(thisObject, argv, argc);
3189 });
3190 } else {
3194 [v4, object, valueWrapper, method](void **argv, int argc) {
3195 Q_UNUSED(argc);
3196
3197 // If we call the method, we have to write back any value type references afterwards.
3198 // The method might change the value.
3200 if (!method->isConstant()) {
3203 }
3204
3205 // If the method returns a QObject* we need to track it on the JS heap
3206 // (if it's destructible).
3207 QObject *qobjectPtr = nullptr;
3209 if (argv[0]) {
3211 qobjectPtr = *static_cast<QObject **>(argv[0]);
3212 } else if (resultType == QMetaType::fromType<QVariant>()) {
3213 const QVariant *result = static_cast<const QVariant *>(argv[0]);
3216 qobjectPtr = *static_cast<QObject *const *>(result->data());
3217 }
3218 }
3219
3220 if (qobjectPtr) {
3223 ddata->indestructible = false;
3225 }
3226 }
3227 });
3228 }
3229}
3230
3232
3233void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
3234{
3235 Object::init();
3236 this->signalIndex = signalIndex;
3237 setObject(object);
3238}
3239
3241
3243{
3247 << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
3248 "not call it directly. Make it a proper function and call "
3249 "that or emit the signal.")
3251
3252 Scope scope(engine());
3255 scope.engine,
3256 static_cast<Heap::QObjectWrapper *>(nullptr),
3257 signalIndex()));
3258
3259 return method->call(thisObject, argv, argc);
3260}
3261
3276
3277
3280{
3281 const QObjectBiPointer key = it.key();
3282 const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
3283 disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
3284 return QHash<QObjectBiPointer, WeakValue>::erase(it);
3285}
3286
3287void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
3288{
3289 QHash<QObjectBiPointer, WeakValue>::remove(object);
3290 QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object));
3291}
3292
3293} // namespace QV4
3294
3295QT_END_NAMESPACE
3296
3297#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