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
53extern "C" {
54
56
57/*! \internal
58 Called right before a QML-to-C++ method call is dispatched, but only
59 when qt_v4NativeCallHookEnabled is set. An attached debugger can set
60 a breakpoint here. \a receiverMeta is the meta object of the call
61 receiver.
62*/
63Q_QML_EXPORT Q_DECL_COLD_FUNCTION void qt_v4AboutToCallNativeMethodHook(const QMetaObject *receiverMeta)
64{
65 Q_UNUSED(receiverMeta);
66}
67
68} // extern "C"
69
71
72Q_LOGGING_CATEGORY(lcBuiltinsBindingRemoval, "qt.qml.binding.removal", QtWarningMsg)
73Q_STATIC_LOGGING_CATEGORY(lcObjectConnect, "qt.qml.object.connect", QtWarningMsg)
74Q_STATIC_LOGGING_CATEGORY(lcOverloadResolution, "qt.qml.overloadresolution", QtWarningMsg)
75Q_STATIC_LOGGING_CATEGORY(lcMethodBehavior, "qt.qml.method.behavior")
76Q_STATIC_LOGGING_CATEGORY(lcSignalHandler, "qt.qml.signalhandler")
77
78// The code in this file does not violate strict aliasing, but GCC thinks it does
79// so turn off the warnings for us to have a clean build
80QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
81
82using namespace Qt::StringLiterals;
83
84namespace QV4 {
85
87{
89 if (v4) {
90 Scope scope(v4);
92 if (method)
94 }
95
96 return std::make_pair((QObject *)nullptr, -1);
97}
98
99static std::pair<QObject *, int> extractQtSignal(const Value &value)
100{
101 if (value.isObject()) {
102 ExecutionEngine *v4 = value.as<Object>()->engine();
103 Scope scope(v4);
104 ScopedFunctionObject function(scope, value);
105 if (function)
106 return QObjectMethod::extractQtMethod(function);
107
108 Scoped<QmlSignalHandler> handler(scope, value);
109 if (handler)
110 return std::make_pair(handler->object(), handler->signalIndex());
111 }
112
113 return std::make_pair((QObject *)nullptr, -1);
114}
115
117 ExecutionEngine *v4,
118 const QQmlPropertyData &property)
119{
120 Heap::ReferenceObject::Flags flags = Heap::ReferenceObject::NoFlag;
121 if (CppStackFrame *stackFrame = v4->currentStackFrame) {
122 if (stackFrame->v4Function->executableCompilationUnit()->valueTypesAreCopied())
123 flags |= Heap::ReferenceObject::EnforcesLocation;
124 }
125
126 if (property.isWritable())
127 flags |= Heap::ReferenceObject::CanWriteBack;
128
129 return flags;
130}
131
135{
137 Scope scope(v4);
138
140 if (property.isQObject()) {
141 QObject *rv = nullptr;
144 return QObjectWrapper::wrapConst(v4, rv);
145 else
146 return QObjectWrapper::wrap(v4, rv);
147 }
148
151
152 const auto encodeSimple = [&](auto v) {
154 return Encode(v);
155 };
156
157 const auto encodeInt = [&](auto v) {
159 return Encode(int(v));
160 };
161
162 const auto encodeDouble = [&](auto v) {
164 return Encode(double(v));
165 };
166
167 const auto encodeDate = [&](auto v) {
169 return Encode(v4->newDateObject(
171 };
172
173 const auto encodeString = [&](auto v) {
175 return v4->newString(v)->asReturnedValue();
176 };
177
178 const auto encodeSequence = [&](QMetaSequence metaSequence) {
179 // Pass nullptr as data. It's lazy-loaded.
181 v4, propMetaType, metaSequence, nullptr,
183 };
184
185
188 case QMetaType::Void:
189 return Encode::undefined();
190 case QMetaType::Nullptr:
191 case QMetaType::VoidStar:
192 return Encode::null();
193 case QMetaType::Int:
194 return encodeSimple(int());
195 case QMetaType::Bool:
196 return encodeSimple(bool());
197 case QMetaType::QString:
198 return encodeString(QString());
199 case QMetaType::QByteArray: {
203 }
204 case QMetaType::QChar:
205 return encodeString(QChar());
206 case QMetaType::Char16:
207 return encodeString(char16_t());
208 case QMetaType::UInt:
209 return encodeSimple(uint());
210 case QMetaType::Float:
211 return encodeSimple(float());
212 case QMetaType::Double:
213 return encodeSimple(double());
214 case QMetaType::Short:
215 return encodeInt(short());
216 case QMetaType::UShort:
217 return encodeInt(ushort());
218 case QMetaType::Char:
219 return encodeInt(char());
220 case QMetaType::UChar:
221 return encodeInt(uchar());
222 case QMetaType::SChar:
223 return encodeInt(qint8());
224 case QMetaType::Long:
225 return encodeDouble(long());
226 case QMetaType::ULong:
227 return encodeDouble(ulong());
228 case QMetaType::LongLong:
229 return encodeDouble(qlonglong());
230 case QMetaType::ULongLong:
231 return encodeDouble(qulonglong());
232 case QMetaType::QDateTime:
233 return encodeDate(QDateTime());
234 case QMetaType::QDate:
235 return encodeDate(QDate());
236 case QMetaType::QTime:
237 return encodeDate(QTime());
238#if QT_CONFIG(regularexpression)
242 return Encode(v4->newRegExpObject(v));
243 }
244#endif
245 case QMetaType::QVariantMap: {
248 return scope.engine->fromData(
250 }
251 case QMetaType::QVariantHash: {
254 return scope.engine->fromData(
256 }
257 case QMetaType::QJsonValue: {
260 return QV4::JsonObject::fromJsonValue(v4, v);
261 }
262 case QMetaType::QJsonObject: {
265 return QV4::JsonObject::fromJsonObject(v4, v);
266 }
267 case QMetaType::QJsonArray:
273 case QMetaType::QUrl: {
274 // ### Qt7: We really want this to be a JS URL object, but that would break things.
275 QUrl v;
278 }
279 case QMetaType::QPixmap:
280 case QMetaType::QImage: {
281 // Scarce value types
285 }
286 default:
287 break;
288 }
289
291 QJSValue v;
294 }
295
297 // We have to read the property even if it's a lazy-loaded reference object.
298 // Without reading it, we wouldn't know its inner type.
299 QVariant v;
301 return scope.engine->fromVariant(
304 }
305
306 if (!propMetaType.isValid()) {
308 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
309 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
310 return Encode::undefined();
311 }
312
313 // TODO: For historical reasons we don't enforce locations for reference objects here.
314 // Once we do, we can eager load and use the fromVariant() below.
315 // Then the extra checks for value types and sequences can be dropped.
316
320 // Lazy loaded value type reference. Pass nullptr as data.
324 }
325 }
326
327 // See if it's a sequence type.
331
334 return scope.engine->fromVariant(
336}
337
343
350
365
369{
371
373 if (property->isVMEFunction()) {
375 return vmemo->vmeMethod(property->coreIndex());
376
377 return QObjectMethod::create(
378 engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
379 } else if (property->isV4Function()) {
380 return QObjectMethod::create(
381 engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
382 } else if (property->isSignalHandler()) {
386 } else {
387 return QObjectMethod::create(
388 engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
389 }
390 }
391
393
394 if (ep && ep->propertyCapture && !property->isConstant()) {
399 }
400 }
401
402 if (property->isVarProperty()) {
405 }
406
408}
409
411 ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
412{
413 int index = 0;
414 if (name->equals(v4->id_destroy()))
416 else if (name->equals(v4->id_toString()))
418 else
419 return OptionalReturnedValue();
420
421 if (hasProperty)
422 *hasProperty = true;
424}
425
427 ExecutionEngine *v4, String *name, const QQmlRefPointer<QQmlContextData> &qmlContext,
428 QObject *qobj, bool *hasProperty = nullptr)
429{
430 if (!qmlContext || !qmlContext->imports())
431 return OptionalReturnedValue();
432
433 if (hasProperty)
434 *hasProperty = true;
435
436 if (QQmlTypeLoader *typeLoader = v4->typeLoader()) {
437 QQmlTypeNameCache::Result r = qmlContext->imports()->query(name, typeLoader);
438
439 if (!r.isValid())
440 return OptionalReturnedValue();
441
442 if (r.scriptIndex != -1) {
443 return OptionalReturnedValue(Encode::undefined());
444 } else if (r.type.isValid()) {
445 return OptionalReturnedValue(
446 QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
447 } else if (r.importNamespace) {
448 return OptionalReturnedValue(QQmlTypeWrapper::create(
449 v4, qobj, qmlContext->imports(), r.importNamespace,
450 Heap::QQmlTypeWrapper::ExcludeEnums));
451 }
452 Q_UNREACHABLE_RETURN(OptionalReturnedValue());
453 } else {
454 return OptionalReturnedValue();
455 }
456}
457
461{
462 // Keep this code in sync with ::virtualResolveLookupGetter
463
464 if (QQmlData::wasDeleted(d()->object())) {
465 if (hasProperty)
466 *hasProperty = false;
467 return Encode::undefined();
468 }
469
471
473 return *methodValue;
474
477
478 if (!result) {
479 // Check for attached properties
483 return *importProperty;
484 }
485 return Object::virtualGet(this, name->propertyKey(), this, hasProperty);
486 }
487
488 QQmlData *ddata = QQmlData::get(d()->object(), false);
489
490 if ((flags & CheckRevision) && result->hasRevision()) {
492 if (hasProperty)
493 *hasProperty = false;
494 return Encode::undefined();
495 }
496 }
497
498 if (hasProperty)
499 *hasProperty = true;
500
501 return getProperty(v4, d(), d()->object(), result, flags);
502}
503
508{
509 if (QQmlData::wasDeleted(object)) {
510 if (hasProperty)
511 *hasProperty = false;
512 return Encode::null();
513 }
514
516 return *methodValue;
517
518 QQmlData *ddata = QQmlData::get(object, false);
521
522 if (result) {
526 if (hasProperty)
527 *hasProperty = false;
528 return Encode::undefined();
529 }
530 }
531
532 if (hasProperty)
533 *hasProperty = true;
534
535 if (property && result != &local)
536 *property = result;
537
539 } else {
540 // Check if this object is already wrapped.
541 if (!ddata || (ddata->jsWrapper.isUndefined() &&
542 (ddata->jsEngineId == 0 || // Nobody owns the QObject
543 !ddata->hasTaintedV4Object))) { // Someone else has used the QObject, but it isn't tainted
544
545 // Not wrapped. Last chance: try query QObjectWrapper's prototype.
546 // If it can't handle this, then there is no point
547 // to wrap the QObject just to look at an empty set of JS props.
549 return proto->get(name, hasProperty);
550 }
551 }
552
553 // If we get here, we must already be wrapped (which implies a ddata).
554 // There's no point wrapping again, as there wouldn't be any new props.
556
559 if (!rewrapped) {
560 if (hasProperty)
561 *hasProperty = false;
562 return Encode::null();
563 }
565}
566
567
589
590/*!
591 \internal
592 If an QObjectWrapper is created via wrap, then it needs to be stored somewhere.
593 Otherwise, the garbage collector will immediately collect it if it is already
594 past the "mark QObjectWrapper's" phase (note that QObjectWrapper are marked
595 by iterating over a list of all QObjectWrapper, and then checking if the
596 wrapper fulfills some conditions).
597 However, sometimes we don't really want to keep a reference to the wrapper,
598 but just want to make sure that it exists (and we know that the wrapper
599 already fulfills the conditions to be kept alive). Then ensureWrapper
600 can be used, which creates the wrapper and ensures that it is also
601 marked.
602 */
612
615 const QQmlPropertyData *property, const Value &value)
616{
617 if (!property->isWritable() && !property->isQList()) {
618 QString error = QLatin1String("Cannot assign to read-only property \"") +
621 return;
622 }
623
626 if (f->as<QQmlTypeWrapper>()) {
627 // Ignore. It's probably a singleton or an attached type.
628 } else if (!f->isBinding()) {
629 const bool isAliasToAllowed = [&]() {
630 if (property->isAlias()) {
642 } else {
643 return false;
644 }
645 }();
648 // assigning a JS function to a non var or QJSValue property or is not allowed.
649 QString error = QLatin1String("Cannot assign JavaScript function to ");
650 if (!QMetaType(property->propType()).name())
651 error += QLatin1String("[unknown property type]");
652 else
655 return;
656 }
657 } else {
658
663
664 // binding assignment.
665 if (property->acceptsQBinding()) {
666 const QQmlPropertyIndex idx(property->coreIndex(), /*not a value type*/-1);
669 if (f->isBoundFunction()) {
670 auto boundFunction = static_cast<BoundFunction *>(f.getPointer());
673 } else {
676
677 }
679 void *argv = {&bindable};
680 // indirect metacall in case interceptors are installed
683 if (!ok) {
684 auto error = QStringLiteral("Failed to set binding on %1::%2.").
687 }
688 } else {
691 if (f->isBoundFunction())
693 newBinding->setTarget(object, *property, nullptr);
695 }
696 return;
697 }
698 }
699
703 binding && !binding->isSticky()) {
704 const auto stackFrame = engine->currentStackFrame;
705 switch (binding->kind()) {
707 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
709 "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
713 break;
714 }
718 "Overwriting binding on %s::%s at %s:%d",
721 break;
722 }
723 }
724 }
725 }
728
729 if (property->isVarProperty()) {
730 // allow assignment of "special" values (null, undefined, function) to var properties
733 return;
734 }
735 }
736
737#define PROPERTY_STORE(cpptype, value)
738 cpptype o = value;
739 int status = -1;
740 int flags = 0;
741 void *argv[] = { &o, 0, &status, &flags };
742 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
743
745 // functions are already handled, except for the QJSValue case and for var properties of
746 // objects owned by another engine
751
752 if (value.isNull() && property->isQObject()) {
753 PROPERTY_STORE(QObject*, nullptr);
754 } else if (value.isUndefined() && property->isResettable()) {
755 void *a[] = { nullptr };
757 } else if (value.isUndefined() && propType == QMetaType::fromType<QVariant>()) {
759 } else if (value.isUndefined() && propType == QMetaType::fromType<QJsonValue>()) {
761 } else if (propType == QMetaType::fromType<QJSValue>()) {
764 QString error = QLatin1String("Cannot assign [undefined] to ");
765 if (!propType.name())
766 error += QLatin1String("[unknown property type]");
767 else
770 return;
771 } else if (propType == QMetaType::fromType<int>() && value.isNumber()) {
773 } else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) {
775 } else if (propType == QMetaType::fromType<float>() && value.isNumber()) {
776 PROPERTY_STORE(float, float(value.asDouble()));
777 } else if (propType == QMetaType::fromType<double>() && value.isNumber()) {
778 PROPERTY_STORE(double, double(value.asDouble()));
779 } else if (propType == QMetaType::fromType<QString>() && value.isString()) {
782 && (value.isUndefined() || value.isPrimitive())) {
783 QQmlScriptString ss(value.toQStringNoThrow(), nullptr /* context */, object);
784 if (value.isNumber()) {
786 ss.d->isNumberLiteral = true;
787 } else if (value.isString()) {
789 ss.d->isStringLiteral = true;
790 }
792 } else {
793 QVariant v;
796 else
798
801 const char *valueType = (v.userType() == QMetaType::UnknownType)
802 ? "an unknown type"
803 : QMetaType(v.userType()).name();
804
805 const char *targetTypeName = propType.name();
806 if (!targetTypeName)
807 targetTypeName = "an unregistered type";
808
809 QString error = QLatin1String("Cannot assign ") +
811 QLatin1String(" to ") +
814 return;
815 }
816 }
817}
818
820{
822
823 QQmlData *ddata = QQmlData::get(object, true);
824 if (!ddata)
825 return Encode::undefined();
826
828
829 if (ddata->jsWrapper.isUndefined() &&
830 (ddata->jsEngineId == engine->m_engineId || // We own the QObject
831 ddata->jsEngineId == 0 || // No one owns the QObject
832 !ddata->hasTaintedV4Object)) { // Someone else has used the QObject, but it isn't tainted
833
837 return rv->asReturnedValue();
838
839 } else {
840 // If this object is tainted, we have to check to see if it is in our
841 // tainted object list
845
846 // If our tainted handle doesn't exist or has been collected, and there isn't
847 // a handle in the ddata, we can assume ownership of the ddata->jsWrapper
852 return result->asReturnedValue();
853 }
854
855 if (!alternateWrapper) {
861 }
862
864 }
865}
866
868{
869 const QObject *constObject = object;
870
871 QQmlData *ddata = QQmlData::get(object, true);
872
877
878 if (!constWrapper) {
884 ddata->hasConstWrapper = true;
885 }
886
888}
889
907
912
914{
915 Q_ASSERT(propertyIndex < 0xffff);
917
919 return;
920 QQmlData *ddata = QQmlData::get(object, /*create*/false);
921 if (!ddata)
922 return;
923
926 Q_ASSERT(property); // We resolved this property earlier, so it better exist!
928}
929
931{
933 const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a);
936
937 // We can have a const and a non-const wrapper for the same object.
940}
941
952
964
966{
967 if (!id.isString())
968 return Object::virtualPut(m, id, value, receiver);
969
970 Scope scope(m);
971 QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
973
974 if (that->internalClass()->isFrozen()) {
975 QString error = QLatin1String("Cannot assign to property \"") +
976 name->toQString() + QLatin1String("\" of read-only object");
978 return false;
979 }
980
982 return false;
983
987 // Types created by QML are not extensible at run-time, but for other QObjects we can store them
988 // as regular JavaScript properties, like on JavaScript objects.
989 if (ddata && ddata->context) {
990 QString error = QLatin1String("Cannot assign to non-existent property \"") +
991 name->toQString() + QLatin1Char('\"');
993 return false;
994 } else {
995 return Object::virtualPut(m, id, value, receiver);
996 }
997 }
998
999 return true;
1000}
1001
1003{
1004 if (id.isString()) {
1005 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
1006 const QObject *thatObject = that->d()->object();
1008 Scope scope(m);
1014 if (p) {
1015 // ### probably not the fastest implementation
1016 bool hasProperty;
1019 }
1020 return Attr_Data;
1021 }
1022 }
1023 }
1024
1025 return Object::virtualGetOwnProperty(m, id, p);
1026}
1027
1029{
1032 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
1033
1034private:
1035 QSet<QByteArray> m_alreadySeen;
1036};
1037
1038PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
1039{
1040 // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
1041 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
1042 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
1043 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
1044
1045 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(o);
1046
1047 QObject *thatObject = that->d()->object();
1048 if (thatObject && !QQmlData::wasDeleted(thatObject)) {
1049 const QMetaObject *mo = thatObject->metaObject();
1050 // These indices don't apply to gadgets, so don't block them.
1051 const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
1052 const int propertyCount = mo->propertyCount();
1053 if (propertyIndex < propertyCount) {
1054 ExecutionEngine *thatEngine = that->engine();
1055 Scope scope(thatEngine);
1056 const QMetaProperty property = mo->property(propertyIndex);
1057 ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name())));
1058 ++propertyIndex;
1059 if (attrs)
1060 *attrs= Attr_Data;
1061 if (pd) {
1062 QQmlPropertyData local;
1063 local.load(property);
1064 pd->value = that->getProperty(
1065 thatEngine, that->d(), thatObject, &local,
1066 QObjectWrapper::AttachMethods);
1067 }
1068 return propName->toPropertyKey();
1069 }
1070 const int methodCount = mo->methodCount();
1071 while (propertyIndex < propertyCount + methodCount) {
1072 Q_ASSERT(propertyIndex >= propertyCount);
1073 int index = propertyIndex - propertyCount;
1074 const QMetaMethod method = mo->method(index);
1075 ++propertyIndex;
1076 if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
1077 continue;
1078 // filter out duplicates due to overloads:
1079 if (m_alreadySeen.contains(method.name()))
1080 continue;
1081 else
1082 m_alreadySeen.insert(method.name());
1083 ExecutionEngine *thatEngine = that->engine();
1084 Scope scope(thatEngine);
1085 ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name())));
1086 if (attrs)
1087 *attrs = Attr_Data;
1088 if (pd) {
1089 QQmlPropertyData local;
1090 local.load(method);
1091 pd->value = that->getProperty(
1092 thatEngine, that->d(), thatObject, &local,
1093 QObjectWrapper::AttachMethods);
1094 }
1095 return methodName->toPropertyKey();
1096 }
1097 }
1098
1099 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1100}
1101
1107
1109{
1110 // Keep this code in sync with ::getQmlProperty
1113 if (!id.isString())
1116
1117 const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
1120
1121 QObject * const qobj = This->d()->object();
1122
1123 if (QQmlData::wasDeleted(qobj))
1124 return Encode::undefined();
1125
1126 QQmlData *ddata = QQmlData::get(qobj, false);
1129 if (!ddata)
1130 ddata = QQmlData::get(qobj, true);
1136 return method.asReturnedValue();
1137 }
1138
1139 if (!ddata || !ddata->propertyCache) {
1143 if (!property)
1144 return Encode::undefined();
1151 } else {
1158 }
1159 return result->asReturnedValue();
1160 }
1162
1163 if (!property) {
1164 // Check for attached properties
1165 if (name->startsWithUpper()) {
1167 return *importProperty;
1168 }
1170 }
1171
1172 if (property->isFunction()
1173 && !property->isVarProperty()
1174 && !property->isVMEFunction() // Handled by QObjectLookup
1175 && !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too
1176 QV4::Heap::QObjectMethod *method = nullptr;
1179 return lookup->getter(engine, *object);
1180 }
1181
1183
1185 return lookup->getter(engine, *object);
1186}
1187
1193
1195{
1198
1199 if (QObject *qObject = wrapper->object())
1200 return QMetaObject::metacall(qObject, call, index, a);
1201
1202 return 0;
1203}
1204
1207{
1208 if (!metaObject)
1209 return QLatin1String("null");
1210
1211 if (!object)
1212 return QString::fromUtf8(metaObject->className()) + QLatin1String("(0x0)");
1213
1214 const int id = metaObject->indexOfMethod("toString()");
1215 if (id >= 0) {
1221 return result.toString();
1224 return value->toQString();
1225 }
1226
1229 QLatin1String("(0x") + QString::number(quintptr(object), 16);
1231 if (!objectName.isEmpty())
1232 result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
1233 result += QLatin1Char(')');
1234 return result;
1235}
1236
1238{
1243
1247
1248 static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
1249 {
1250 switch (which) {
1251 case Destroy: {
1252 delete static_cast<QObjectSlotDispatcher*>(this_);
1253 }
1254 break;
1255 case Call: {
1256 if (QQmlData::wasDeleted(receiver))
1257 break;
1258
1259 QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
1260 ExecutionEngine *v4 = This->function.engine();
1261 // Might be that we're still connected to a signal that's emitted long
1262 // after the engine died. We don't track connections in a global list, so
1263 // we need this safeguard.
1264 if (!v4)
1265 break;
1266
1267 QQmlMetaObject::ArgTypeStorage<9> storage;
1268 QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr);
1269
1270 const qsizetype argCount = std::min(storage.size(), This->maxNumArguments);
1271
1272 Scope scope(v4);
1273 ScopedFunctionObject f(scope, This->function.value());
1274
1275 JSCallArguments jsCallData(scope, argCount);
1276 *jsCallData.thisObject = This->thisObject.isUndefined()
1277 ? v4->globalObject->asReturnedValue()
1278 : This->thisObject.value();
1279 for (qsizetype ii = 0; ii < argCount; ++ii) {
1280 QMetaType type = storage[ii];
1281 if (type == QMetaType::fromType<QVariant>()) {
1282 jsCallData.args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1283 } else {
1284 jsCallData.args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
1285 }
1286 }
1287
1288 f->call(jsCallData);
1289 if (scope.hasException()) {
1290 QQmlError error = v4->catchExceptionAsQmlError();
1291 if (error.description().isEmpty()) {
1292 ScopedString name(scope, f->name());
1293 error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString()));
1294 }
1295 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
1296 QQmlEnginePrivate::get(qmlEngine)->warning(error);
1297 } else {
1298 QMessageLogger(error.url().toString().toLatin1().constData(),
1299 error.line(), nullptr).warning().noquote()
1300 << error.toString();
1301 }
1302 }
1303 }
1304 break;
1305 case Compare: {
1306 QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
1307 if (connection->function.isUndefined()) {
1308 *ret = false;
1309 return;
1310 }
1311
1312 // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
1313 // for the new-style QObject::connect. Here we use the engine pointer as sentinel
1314 // to distinguish those type of QSlotObjectBase connections from our QML connections.
1315 ExecutionEngine *v4 = reinterpret_cast<ExecutionEngine*>(metaArgs[0]);
1316 if (v4 != connection->function.engine()) {
1317 *ret = false;
1318 return;
1319 }
1320
1321 Scope scope(v4);
1322 ScopedValue function(scope, *reinterpret_cast<Value*>(metaArgs[1]));
1323 ScopedValue thisObject(scope, *reinterpret_cast<Value*>(metaArgs[2]));
1324 QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
1325 int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
1326
1327 if (slotIndexToDisconnect != -1) {
1328 // This is a QObject function wrapper
1329 if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1330 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1331
1332 ScopedFunctionObject f(scope, connection->function.value());
1333 std::pair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f);
1334 if (connectedFunctionData.first == receiverToDisconnect &&
1335 connectedFunctionData.second == slotIndexToDisconnect) {
1336 *ret = true;
1337 return;
1338 }
1339 }
1340 } else {
1341 // This is a normal JS function
1342 if (RuntimeHelpers::strictEqual(*connection->function.valueRef(), function) &&
1343 connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1344 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1345 *ret = true;
1346 return;
1347 }
1348 }
1349
1350 *ret = false;
1351 }
1352 break;
1353 case NumOperations:
1354 break;
1355 }
1356 };
1357};
1358
1360{
1361 Scope scope(b);
1362
1363 if (argc == 0)
1364 THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
1365
1368 int signalIndex = signalInfo.second; // in method range, not signal range!
1369
1370 if (signalIndex < 0)
1371 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1372
1373 if (!signalObject)
1374 THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1375
1378 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1379
1382
1383 if (argc == 1) {
1384 f = argv[0];
1385 } else if (argc >= 2) {
1386 object = argv[0];
1387 f = argv[1];
1388 }
1389
1390 if (!f)
1391 THROW_GENERIC_ERROR("Function.prototype.connect: target is not a function");
1392
1393 if (!object->isUndefined() && !object->isObject())
1394 THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object");
1395
1398
1401
1405 }
1406 }
1407
1408 std::pair<QObject *, int> functionData = QObjectMethod::extractQtMethod(f); // align with disconnect
1409 QObject *receiver = nullptr;
1410
1411 if (functionData.first)
1413 else if (auto qobjectWrapper = object->as<QV4::QObjectWrapper>())
1415 else if (auto typeWrapper = object->as<QV4::QQmlTypeWrapper>())
1417
1418 if (receiver) {
1419 if (functionData.second == -1) {
1421 } else {
1422 // This means we are connecting to QObjectMethod which complains about extra arguments.
1423 Heap::QObjectMethod *d = static_cast<Heap::QObjectMethod *>(f->d());
1426 [](int a, const QQmlPropertyData &b) {
1427 return std::max(a, b.metaMethod().parameterCount());
1428 });
1429 }
1430
1432 } else {
1435 "Could not find receiver of the connection, using sender as receiver. Disconnect "
1436 "explicitly (or delete the sender) to make sure the connection is removed.");
1438 }
1439
1441}
1442
1444{
1445 Scope scope(b);
1446
1447 if (argc == 0)
1448 THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
1449
1453
1454 if (signalIndex == -1)
1455 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1456
1457 if (!signalObject)
1458 THROW_GENERIC_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1459
1461 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1462
1465
1466 if (argc == 1) {
1467 functionValue = argv[0];
1468 } else if (argc >= 2) {
1470 functionValue = argv[1];
1471 }
1472
1473 if (!functionValue)
1474 THROW_GENERIC_ERROR("Function.prototype.disconnect: target is not a function");
1475
1477 THROW_GENERIC_ERROR("Function.prototype.disconnect: target this is not an object");
1478
1480
1481 void *a[] = {
1482 scope.engine,
1487 };
1488
1489 QObject *receiver = nullptr;
1490
1491 if (functionData.first)
1495 else if (auto typeWrapper = functionThisValue->as<QV4::QQmlTypeWrapper>())
1497
1498 if (receiver) {
1500 reinterpret_cast<void **>(&a));
1501 } else {
1503 reinterpret_cast<void **>(&a));
1504 }
1505
1507}
1508
1509static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack)
1510{
1511 QQueue<QObject *> queue;
1512 queue.append(parent->children());
1513
1514 while (!queue.isEmpty()) {
1515 QObject *child = queue.dequeue();
1516 if (!child)
1517 continue;
1518 QObjectWrapper::markWrapper(child, markStack);
1519 queue.append(child->children());
1520 }
1521}
1522
1523// returns true if and only if an object was recorded
1525 QObject *o, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
1526 MemoryManager::ObjectsForCompilationUnit *recorded)
1527{
1528 Q_ASSERT(compilationUnit);
1529 Q_ASSERT(o);
1530 Q_ASSERT(recorded);
1531
1532 const auto &baseUnit = compilationUnit->baseCompilationUnit();
1533 Q_ASSERT(baseUnit);
1534
1535 for (const auto &unit : recorded->compilationUnits) {
1536 if (baseUnit == unit) {
1537 recorded->objects.push_back(o);
1538 return true;
1539 }
1540 }
1541
1542 return false;
1543}
1544
1545// returns true if and only if an object was recorded
1547 QObject *o, const QQmlVMEMetaObject *vme,
1548 MemoryManager::ObjectsForCompilationUnit *recorded)
1549{
1550 for (; vme; vme = vme->parentVMEMetaObject()) {
1551 if (recordObjectForCompilationUnits(o, vme->compilationUnit(), recorded))
1552 return true;
1553 }
1554
1555 return false;
1556}
1557
1559{
1561
1562 QObject *o = static_cast<QObjectWrapper *>(that)->object();
1563 if (!o)
1564 return;
1565
1567 // Children usually don't need to be marked, the gc keeps them alive.
1568 // But in the rare case of a "floating" QObject without a parent that
1569 // _gets_ marked (we've been called here!) then we also need to
1570 // propagate the marking down to the children recursively.
1571 if (!o->parent())
1573 });
1574
1576 if (!ddata)
1577 return;
1578
1579 if (ddata->hasVMEMetaObject) {
1580 if (auto *vme = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) {
1581 vme->mark(markStack);
1583 // ddata->compilationUnit can be different from the VME compilation units
1584 // if the "topmost" instantiation of the object is inaddressible.
1589 }
1590 }
1591 }
1592 } else if (const auto &cu = ddata->compilationUnit) {
1593 // Objects without a VMEMetaObject are identified via QQmlData::compilationUnit.
1596 }
1597
1599 return;
1600
1601 // mark the const wrapper if our engine has interacted with it at some point
1604 scope, scope.engine->m_multiplyWrappedQObjects->value(static_cast<const QObject *>(o)));
1605
1606 if (!constWrapper)
1607 return;
1608
1609 if (that != constWrapper->d()) {
1610 // We've got the non-const wrapper. Also mark the const one.
1612 return;
1613 }
1614
1615 // We've got the const wrapper. Also mark the non-const one
1618 else
1620}
1621
1623{
1624 Heap::QObjectWrapper *h = d();
1626
1627 if (QObject *o = h->object()) {
1628 QQmlData *ddata = QQmlData::get(o, false);
1629 if (ddata) {
1630 if (!o->parent() && !ddata->indestructible) {
1631 if (ddata && ddata->ownContext) {
1635 ddata->context = nullptr;
1636 }
1637
1638 // This object is notionally destroyed now. It might still live until the next
1639 // event loop iteration, but it won't need its connections, CU, or deferredData
1640 // anymore.
1641
1642 ddata->isQueuedForDeletion = true;
1645
1648
1649 if (lastCall)
1650 delete o;
1651 else
1652 o->deleteLater();
1653 } else {
1654 // If the object is C++-owned, we still have to release the weak reference we have
1655 // to it. If the "main" wrapper is not ours, we should leave it alone, though.
1656 if (ddata->jsWrapper.as<QObjectWrapper>() == this)
1658 }
1659 }
1660 }
1661
1662 h->destroy();
1663}
1664
1665
1667
1668template<typename Retrieve>
1669int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
1670 if (conversionMetaType == QMetaType::fromType<QVariant>())
1671 return 0;
1672
1673 const QMetaType type = retrieve();
1674 if (type == conversionMetaType)
1675 return 0;
1676
1677 if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
1678 if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject))
1679 return 1;
1680 }
1681
1682 if (QMetaType::canConvert(type, conversionMetaType)) {
1683 if (conversionMetaType == QMetaType::fromType<QJSValue>()
1684 || conversionMetaType == QMetaType::fromType<double>()
1685 || conversionMetaType == QMetaType::fromType<QString>()) {
1686 // Unspecific conversions receive lower score. You can convert anything
1687 // to QString or double via toString() and valueOf(), respectively.
1688 // And anything can be wrapped into QJSValue, but that's inefficient.
1689 return 6;
1690 }
1691
1692 // We have an explicitly defined conversion method to a non-boring type.
1693 return 5;
1694 }
1695
1696 return 10;
1697};
1698
1699/*
1700 Returns the match score for converting \a actual to be of type \a conversionType. A
1701 zero score means "perfect match" whereas a higher score is worse.
1702
1703 The conversion table is copied out of the \l QScript::callQtMethod() function.
1704*/
1705static int MatchScore(const Value &actual, QMetaType conversionMetaType)
1706{
1707 const int conversionType = conversionMetaType.id();
1708 const auto convertibleScore = [&](QMetaType actualType) {
1709 // There are a number of things we can do in JavaScript to subvert this, but
1710 // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
1711 if (!QMetaType::canConvert(actualType, conversionMetaType))
1712 return 10;
1713
1714 // You can convert anything to QJSValue, but that's inefficient.
1715 // If we have a better option, we should use it.
1716 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1717 return 9;
1718
1719 // You can also convert anything to QVariant, but that's also suboptimal.
1720 // You can convert anything to string or double via toString() and valueOf().
1721 // Those are also rather unspecific.
1722 switch (conversionType) {
1723 case QMetaType::QVariant:
1724 case QMetaType::Double:
1725 case QMetaType::QString:
1726 return 9;
1727 default:
1728 break;
1729 }
1730
1731 // We have an explicitly defined conversion method to a non-boring type.
1732 return 8;
1733 };
1734
1735 if (actual.isNumber()) {
1736 switch (conversionType) {
1737 case QMetaType::Double:
1738 return 0;
1739 case QMetaType::Float:
1740 return 1;
1741 case QMetaType::LongLong:
1742 case QMetaType::ULongLong:
1743 return 2;
1744 case QMetaType::Long:
1745 case QMetaType::ULong:
1746 return 3;
1747 case QMetaType::Int:
1748 case QMetaType::UInt:
1749 return 4;
1750 case QMetaType::Short:
1751 case QMetaType::UShort:
1752 return 5;
1753 break;
1754 case QMetaType::Char:
1755 case QMetaType::UChar:
1756 return 6;
1757 case QMetaType::QJsonValue:
1758 return 5;
1759 default:
1760 return convertibleScore(actual.isInteger()
1761 ? QMetaType::fromType<int>()
1762 : QMetaType::fromType<double>());
1763 }
1764 } else if (actual.isString()) {
1765 switch (conversionType) {
1766 case QMetaType::QString:
1767 return 0;
1768 case QMetaType::QJsonValue:
1769 return 5;
1770 case QMetaType::QUrl:
1771 return 6; // we like to convert strings to URLs in QML
1772 case QMetaType::Double:
1773 case QMetaType::Float:
1774 case QMetaType::LongLong:
1775 case QMetaType::ULongLong:
1776 case QMetaType::Int:
1777 case QMetaType::UInt:
1778 case QMetaType::Short:
1779 case QMetaType::UShort:
1780 case QMetaType::Char:
1781 case QMetaType::UChar:
1782 // QMetaType can natively convert strings to numbers.
1783 // However, in the general case it's of course extremely lossy.
1784 return 10;
1785 default:
1786 return convertibleScore(QMetaType::fromType<QString>());
1787 }
1788 } else if (actual.isBoolean()) {
1789 switch (conversionType) {
1790 case QMetaType::Bool:
1791 return 0;
1792 case QMetaType::QJsonValue:
1793 return 5;
1794 default:
1795 return convertibleScore(QMetaType::fromType<bool>());
1796 }
1797 } else if (actual.as<DateObject>()) {
1798 switch (conversionType) {
1799 case QMetaType::QDateTime:
1800 return 0;
1801 case QMetaType::QDate:
1802 return 1;
1803 case QMetaType::QTime:
1804 return 2;
1805 default:
1806 return convertibleScore(QMetaType::fromType<QDateTime>());
1807 }
1808 } else if (actual.as<RegExpObject>()) {
1809 switch (conversionType) {
1810#if QT_CONFIG(regularexpression)
1811 case QMetaType::QRegularExpression:
1812 return 0;
1813 default:
1814 return convertibleScore(QMetaType::fromType<QRegularExpression>());
1815#else
1816 default:
1817 return convertibleScore(QMetaType());
1818#endif
1819 }
1820 } else if (actual.as<ArrayBuffer>()) {
1821 switch (conversionType) {
1822 case QMetaType::QByteArray:
1823 return 0;
1824 default:
1825 return convertibleScore(QMetaType::fromType<QByteArray>());
1826 }
1827 } else if (actual.as<ArrayObject>()) {
1828 switch (conversionType) {
1829 case QMetaType::QJsonArray:
1830 return 3;
1831 case QMetaType::QStringList:
1832 case QMetaType::QVariantList:
1833 return 5;
1834 case QMetaType::QVector4D:
1835 case QMetaType::QMatrix4x4:
1836 return 6;
1837 case QMetaType::QVector3D:
1838 return 7;
1839 default:
1840 return convertibleScore(QMetaType());
1841 }
1842 } else if (actual.isNull()) {
1843 switch (conversionType) {
1844 case QMetaType::Nullptr:
1845 case QMetaType::VoidStar:
1846 case QMetaType::QObjectStar:
1847 case QMetaType::QJsonValue:
1848 return 0;
1849 default: {
1850 if (conversionMetaType.flags().testFlag(QMetaType::IsPointer))
1851 return 0;
1852 else
1853 return convertibleScore(QMetaType());
1854 }
1855 }
1856 } else if (const Object *obj = actual.as<Object>()) {
1857 if (const VariantObject *variantObject = obj->as<VariantObject>()) {
1858 return MatchVariant(conversionMetaType, [variantObject]() {
1859 return variantObject->d()->data().metaType();
1860 });
1861 }
1862
1863 if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
1864 switch (conversionType) {
1865 case QMetaType::QObjectStar:
1866 return 0;
1867 default:
1868 if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
1869 QObject *wrapped = wrapper->object();
1870 if (!wrapped)
1871 return 0;
1872 if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject()))
1873 return 0;
1874 }
1875 }
1876
1877 return convertibleScore(QMetaType::fromType<QObject *>());
1878 }
1879
1880 if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
1881 const QQmlType type = wrapper->d()->type();
1882 if (type.isSingleton()) {
1883 const QMetaType metaType = type.typeId();
1884 if (metaType == conversionMetaType)
1885 return 0;
1886
1887 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1888 && metaType.flags() & QMetaType::PointerToQObject
1889 && type.metaObject()->inherits(conversionMetaType.metaObject())) {
1890 return 0;
1891 }
1892 } else if (QObject *object = wrapper->object()) {
1893 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1894 && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) {
1895 return 0;
1896 }
1897 }
1898
1899 return convertibleScore(QMetaType());
1900 }
1901
1902 if (const Sequence *sequence = obj->as<Sequence>()) {
1903 const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(sequence);
1904 if (sequenceType == conversionMetaType)
1905 return 1;
1906
1907 return convertibleScore(sequenceType);
1908 }
1909
1910 if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
1911 return MatchVariant(conversionMetaType, [wrapper]() {
1912 return wrapper->d()->isVariant()
1913 ? wrapper->toVariant().metaType()
1914 : wrapper->type();
1915 });
1916 }
1917
1918 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1919 return 0;
1920
1921 switch (conversionType) {
1922 case QMetaType::QJsonObject:
1923 case QMetaType::QVariantMap:
1924 return 5;
1925 default:
1926 break;
1927 }
1928
1929 }
1930
1931 return convertibleScore(QMetaType());
1932}
1933
1934static int numDefinedArguments(CallData *callArgs)
1935{
1936 int numDefinedArguments = callArgs->argc();
1937 while (numDefinedArguments > 0
1938 && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) {
1939 --numDefinedArguments;
1940 }
1941 return numDefinedArguments;
1942}
1943
1944static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
1945{
1946 const QMetaObject *metaObject = object.metaObject();
1947 const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
1948 return indexOfClassInfo != -1
1949 && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
1950}
1951
1952static ReturnedValue doConvertReturnValue(ExecutionEngine *engine, QMetaType type, const void *data)
1953{
1954 if (type.flags() & QMetaType::PointerToQObject) {
1955 // We consider QObjects returned from invokables as owned by the QML engine unless
1956 // explicitly configured otherwise.
1957 if (QObject *object = *static_cast<QObject *const *>(data))
1958 QQmlData::get(object, true)->setImplicitDestructible();
1959 }
1960 return engine->fromData(type, data);
1961}
1962
1963static ReturnedValue doConvertReturnValue(ExecutionEngine *engine, const QVariant &value)
1964{
1965 // QVariant is treated as transparent when returned. That means we still adopt
1966 // QObjects if they're wrapped in QVariant. See above.
1967 return doConvertReturnValue(engine, value.metaType(), value.constData());
1968}
1969
1973{
1974 const auto constructReturnValue = [&](QMetaType returnType, void *returnValue) {
1975 // If we construct a value in place, we mustn't initialize the memory before that.
1978 };
1979
1980 const auto convertReturnValue
1982
1983 // In contrast to other invocations of convertAndCall() we do not want to create
1984 // a URL object from a QUrl here like metaTypeFromJS does. This is for compatibility.
1985 // URL objects are proper, specified, objects that behave different from our variant
1986 // objects when it comes to equality comparisons.
1987
1989 ? doConvertReturnValue(engine, *reinterpret_cast<const QVariant *>(returnValue))
1991
1992 // NB: A type can be both a PointerToQObject and NeedsDestruction if the user registers
1993 // such a thing explicitly. We respect that.
1996 return result;
1997 };
1998
1999 const auto conversionErrorHandler = [&](QMetaType argumentType, void *argument, int ii) {
2000 // We've checked the number of arguments before
2001 Q_ASSERT(ii < callArgs->argc());
2002
2004 // We have an engine here. So we can conjure up a QJSManagedValue for anything.
2005 *static_cast<QJSManagedValue *>(argument)
2007 return true;
2008 }
2009
2011 && callArgs->args[ii].isUndefined()) {
2012 // TODO: QObjectMethod silently converts undefined to null when you pass undefined
2013 // to a method that accepts some QObject-derived. This is wrong because
2014 // undefined is certainly not null. We accept it for compatibility.
2015 *static_cast<QObject **>(argument) = nullptr;
2016 return true;
2017 }
2018
2019 // TODO: QObjectMethod is allowed to use QVariant conversion.
2020 // This is wrong because we can't see the converters at compile time.
2021 // It only exists for compatibility with old versions of Qt.
2025 return true;
2026
2027 qWarning() << QString::fromLatin1("Could not convert argument %1 from %2 to %3")
2028 .arg(ii)
2030 .arg(argumentType.name());
2031 const StackTrace stack = engine->stackTrace();
2032 for (const StackFrame &frame : stack) {
2033 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source + (frame.line > 0
2034 ? (QLatin1Char(':') + QString::number(frame.line))
2035 : QString());
2036
2037 }
2038
2040 qWarning() << "Passing incompatible arguments to signals is not supported.";
2042 }
2043
2045 "Passing incompatible arguments to C++ functions from JavaScript is not allowed."));
2046 return false;
2047 };
2048
2050
2051 const auto handleTooManyArguments = [&](int expectedArguments) {
2053 engine->throwError(QStringLiteral("Too many arguments"));
2054 return false;
2055 }
2056
2057 const auto stackTrace = engine->stackTrace();
2058 if (stackTrace.isEmpty()) {
2060 << "When matching arguments for "
2061 << object.className() << "::" << data.name(object.metaObject()) << "():";
2062 } else {
2063 const StackFrame frame = stackTrace.first();
2065 + (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
2066 : QString());
2067 }
2068
2069 qWarning().noquote() << QStringLiteral("Too many arguments, ignoring %1")
2071 return true;
2072 };
2073
2075
2076 if (data.hasArguments()) {
2078
2079 const bool ok = data.isConstructor()
2084 if (!ok)
2086
2087 const int expectedArgumentCount = storage.size() - 1;
2089 return engine->throwError(QLatin1String("Insufficient arguments"));
2090
2093 return Encode::undefined();
2094 }
2095
2096 return QV4::convertAndCall(
2099 [&](void **argData, const QMetaType *types, int argc) {
2100 Q_UNUSED(types);
2101 Q_UNUSED(argc);
2104 }
2105
2107 return Encode::undefined();
2108
2110 if (!returnType.isValid())
2112
2113 return QV4::convertAndCall(
2114 engine, &returnType, 1, nullptr, 0,
2115 [&](void **argData, const QMetaType *types, int argc) {
2116 Q_UNUSED(types);
2117 Q_UNUSED(argc);
2120}
2121
2122/*
2123Resolve the overloaded method to call. The algorithm works conceptually like this:
2124 1. Resolve the set of overloads it is *possible* to call.
2125 Impossible overloads include those that have too many parameters or have parameters
2126 of unknown type.
2127 2. Filter the set of overloads to only contain those with the closest number of
2128 parameters.
2129 For example, if we are called with 3 parameters and there are 2 overloads that
2130 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
2131 3. Find the best remaining overload based on its match score.
2132 If two or more overloads have the same match score, return the last one. The match
2133 score is constructed by adding the matchScore() result for each of the parameters.
2134*/
2138{
2139 const int argumentCount = callArgs->argc();
2141
2142 const QQmlPropertyData *best = nullptr;
2146
2149
2150 for (int i = 0; i < methodCount; ++i) {
2151 const QQmlPropertyData *attempt = methods + i;
2152
2158 qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
2159 }
2160
2161 // QQmlV4Function overrides anything that doesn't provide the exact number of arguments
2162 int methodParameterScore = 1;
2163 // QQmlV4Function overrides the "no idea" option, which is 10
2164 int maxMethodMatchScore = 9;
2165 // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments
2167
2168 if (!attempt->isV4Function()) {
2170 int methodArgumentCount = 0;
2171 if (attempt->hasArguments()) {
2172 if (attempt->isConstructor()) {
2173 if (!object.constructorParameterTypes(*attempt, &storage, nullptr)) {
2174 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2175 continue;
2176 }
2177 } else {
2178 if (!object.methodParameterTypes(*attempt, &storage, nullptr)) {
2179 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2180 continue;
2181 }
2182 }
2184 }
2185
2187 qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
2188 continue; // We don't have sufficient arguments to call this method
2189 }
2190
2192 ? 0
2195 qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
2196 continue; // We already have a better option
2197 }
2198
2201 for (int ii = 0; ii < methodArgumentCount; ++ii) {
2202 const int score = MatchScore((v = Value::fromStaticValue(callArgs->args[ii])),
2203 storage[ii]);
2206 }
2207 }
2208
2213 best = attempt;
2217 qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
2218 << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
2219 << "bestSumMatchScore" << bestSumMatchScore << "\n";
2220 } else {
2221 qCDebug(lcOverloadResolution) << "did not update best\n"
2222 << "bestParameterScore" << bestParameterScore << "\t"
2223 << "methodParameterScore" << methodParameterScore << "\n"
2224 << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
2225 << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
2226 << "bestSumMatchScore" << bestSumMatchScore << "\t"
2227 << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
2228 }
2229
2230 if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
2231 qCDebug(lcOverloadResolution, "perfect match");
2232 break; // We can't get better than that
2233 }
2234
2235 };
2236
2237 if (best && best->isValid()) {
2238 return best;
2239 } else {
2240 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
2241 for (int i = 0; i < methodCount; ++i) {
2246 error += u"\n " + QString::fromUtf8(m.methodSignature());
2247 }
2248
2250 return nullptr;
2251 }
2252}
2253
2254static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
2255{
2256 if (required == QMetaType::fromType<QVariant>()
2257 || required == QMetaType::fromType<QJSValue>()
2258 || required == QMetaType::fromType<QJSManagedValue>()) {
2259 return true;
2260 }
2261
2262 if (data) {
2263 if (passed == QMetaType::fromType<QVariant>())
2264 passed = static_cast<const QVariant *>(data)->metaType();
2265 else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
2266 passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
2267 }
2268
2269 if (passed == required)
2270 return true;
2271
2272 if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
2273 switch (passed.id()) {
2274 case QMetaType::UnknownType:
2275 case QMetaType::Nullptr:
2276 case QMetaType::Bool:
2277 case QMetaType::Int:
2278 case QMetaType::Double:
2279 case QMetaType::QString:
2280 return true;
2281 default:
2282 break;
2283 }
2284 }
2285
2286 return false;
2287}
2288
2290 const QMetaMethod &method, void **argv, int argc, const QMetaType *types)
2291{
2292 if (types[0].isValid() && !ExactMatch(method.returnMetaType(), types[0], nullptr))
2293 return false;
2294
2295 if (method.parameterCount() != argc)
2296 return false;
2297
2298 for (int i = 0; i < argc; ++i) {
2299 if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1]))
2300 return false;
2301 }
2302
2303 return true;
2304}
2305
2308 void **argv, int argc, const QMetaType *types)
2309{
2310 // We only accept exact matches here. Everything else goes through the JavaScript conversion.
2311 for (int i = 0; i < methodCount; ++i) {
2312 const QQmlPropertyData *attempt = methods + i;
2314 return attempt;
2315 }
2316
2317 return nullptr;
2318}
2319
2328
2338
2342{
2344
2346 if (cloneFrom->wrapper) {
2348 if (ref) {
2350 } else {
2351 // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what
2352 // value we should operate on. Without knowledge of the property the value
2353 // was read from, we cannot load the value from the given object.
2354 return Encode::undefined();
2355 }
2356 }
2357
2359 valueScope,
2362
2364
2365 Q_ASSERT(method->d()->methods == nullptr);
2366 switch (cloneFrom->methodCount) {
2367 case 0:
2368 Q_ASSERT(cloneFrom->methods == nullptr);
2369 break;
2370 case 1:
2372 == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
2373 method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
2374 *method->d()->methods = *cloneFrom->methods;
2375 break;
2376 default:
2377 Q_ASSERT(cloneFrom->methods != nullptr);
2381 break;
2382 }
2383
2384 return method.asReturnedValue();
2385}
2386
2388{
2392}
2393
2395{
2397
2399 return valueWrapper->metaObject();
2400 if (QObject *self = object())
2401 return self->metaObject();
2402
2403 return nullptr;
2404}
2405
2407{
2409
2411 return objectWrapper->object();
2413 return typeWrapper->object();
2414 return nullptr;
2415}
2416
2417bool Heap::QObjectMethod::isDetached() const
2418{
2419 if (!wrapper)
2420 return true;
2421
2424 return valueWrapper->d()->object() == nullptr;
2425
2426 return false;
2427}
2428
2430{
2433 return objectWrapper->object() == o;
2435 return typeWrapper->object() == o;
2436
2440 return qobject->object() == o;
2442 return type->object() == o;
2443
2444 // Attached to some nested value type or sequence object
2445 return false;
2446 }
2447
2448 return false;
2449}
2450
2452 const QMetaObject *thisMeta) const
2453{
2454 // Check that the metaobject matches.
2455
2456 if (!thisMeta) {
2457 // You can only get a detached method via a lookup, and then you have a thisObject.
2459 return Included;
2460 }
2461
2462 const auto check = [&](const QMetaObject *included) {
2467 "%s:%d: Calling C++ methods with 'this' objects different from the one "
2468 "they were retrieved from is broken, due to historical reasons. The "
2469 "original object is used as 'this' object. You can allow the given "
2470 "'this' object to be used by setting "
2471 "'pragma NativeMethodBehavior: AcceptThisObject'",
2473 return Included;
2474 }
2475
2476 // destroy() and toString() can be called on all QObjects, but not on gadgets.
2477 if (index < 0)
2479
2480 // Find the base type the method belongs to.
2482 while (true) {
2483 if (included == thisMeta)
2484 return Explicit;
2485
2486 if (methodOffset <= index)
2488
2492 };
2493
2495 };
2496
2497 if (const QMetaObject *meta = metaObject())
2498 return check(meta);
2499
2500 // If the QObjectMethod is detached, we can only have gotten here via a lookup.
2501 // The lookup checks that the QQmlPropertyCache matches.
2502 return Explicit;
2503}
2504
2506{
2508 return QStringLiteral("destroy");
2509 else if (index == QV4::QObjectMethod::ToStringMethod)
2510 return QStringLiteral("toString");
2511
2512 const QMetaObject *mo = metaObject();
2513 if (!mo)
2514 return QString();
2515
2516 int methodOffset = mo->methodOffset();
2517 while (methodOffset > index) {
2518 mo = mo->superClass();
2520 }
2521
2522 return "%1::%2"_L1.arg(QLatin1StringView{mo->className()},
2524}
2525
2527{
2528 if (methods) {
2529 Q_ASSERT(methodCount > 0);
2530 return;
2531 }
2532
2533 const QMetaObject *mo = metaObject();
2534
2535 if (!mo)
2536 mo = thisMeta;
2537
2538 Q_ASSERT(mo);
2539
2540 int methodOffset = mo->methodOffset();
2541 while (methodOffset > index) {
2542 mo = mo->superClass();
2544 }
2548 dummy.load(method);
2551 // Look for overloaded methods
2553 for (int ii = index - 1; ii >= methodOffset; --ii) {
2554 if (methodName == mo->method(ii).name()) {
2555 method = mo->method(ii);
2556 dummy.load(method);
2558 }
2559 }
2560 if (resolvedMethods.size() > 1) {
2564 } else {
2565 methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod);
2567 methodCount = 1;
2568 }
2569
2570 Q_ASSERT(methodCount > 0);
2571}
2572
2579
2581 ExecutionEngine *engine, QObject *o, const Value *args, int argc) const
2582{
2583 method_destroy(engine, o, argc > 0 ? args[0].toInt32() : 0);
2584 return Encode::undefined();
2585}
2586
2588{
2589 if (!o)
2590 return true;
2591
2593 engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2594 return false;
2595 }
2596
2597 if (delay > 0)
2599 else
2600 o->deleteLater();
2601
2602 return true;
2603}
2604
2606 const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2607{
2608 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2610}
2611
2613 const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
2614{
2615 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2617}
2618
2620{
2622
2623 const QMetaObject *thisMeta = nullptr;
2624
2625 QObject *o = nullptr;
2627 if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
2628 thisMeta = w->metaObject();
2629 o = w->object();
2630 } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
2631 thisMeta = w->metaObject();
2632 o = w->object();
2633 } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
2634 thisMeta = w->metaObject();
2635 valueWrapper = w->d();
2636 }
2637
2639 if (o && o == d()->object()) {
2641 // Nothing to do; objects are the same. This should be common
2642 } else if (valueWrapper && valueWrapper == d()->wrapper) {
2644 // Nothing to do; gadgets are the same. This should be somewhat common
2645 } else {
2647 if (mode == Heap::QObjectMethod::Invalid) {
2648 v4->throwError(QLatin1String("Cannot call method %1 on %2").arg(
2649 d()->name(), thisObject->toQStringNoThrow()));
2650 return Encode::undefined();
2651 }
2652 }
2653
2654 QQmlObjectOrGadget object = [&](){
2655 if (mode == Heap::QObjectMethod::Included) {
2656 QV4::Scope scope(v4);
2660 return QQmlObjectOrGadget(type->object());
2662 valueWrapper = value->d();
2664 }
2665 Q_UNREACHABLE();
2666 } else {
2667 if (o)
2668 return QQmlObjectOrGadget(o);
2669
2674 }
2675 }();
2676
2677 if (object.isNull())
2678 return Encode::undefined();
2679
2680 if (d()->index == DestroyMethod)
2681 return method_destroy(v4, object.qObject(), argv, argc);
2682 else if (d()->index == ToStringMethod) {
2684 const QMetaObject *meta = object.metaObject();
2685 if (meta && meta->indexOfMethod("toString()") >= 0)
2687 }
2688 return method_toString(v4, object.qObject());
2689 }
2690
2692
2693 Scope scope(v4);
2696
2697 const QQmlPropertyData *method = d()->methods;
2698
2699 // If we call the method, we have to write back any value type references afterwards.
2700 // The method might change the value.
2701 const auto doCall = [&](const auto &call) {
2702 if (!method->isConstant()) {
2706 return rv->asReturnedValue();
2707 }
2708 }
2709
2710 return call();
2711 };
2712
2713 if (d()->methodCount != 1) {
2714 Q_ASSERT(d()->methodCount > 0);
2716 if (method == nullptr)
2717 return Encode::undefined();
2718 }
2719
2722
2723 if (method->isV4Function()) {
2724 return doCall([&]() {
2728
2729 void *args[] = { nullptr, &funcptr };
2731
2732 return rv->asReturnedValue();
2733 });
2734 }
2735
2736 return doCall([&]() { return callPrecise(object, *method, v4, callData); });
2737}
2738
2740{
2741 constexpr int parameterCount() const { return 0; }
2742 constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
2743 constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
2744};
2745
2747 QObject *thisObject, void **argv, const QMetaType *types, int argc) const
2748{
2750
2751 const QMetaObject *thisMeta = nullptr;
2753
2754 if (thisObject) {
2756 } else {
2760 }
2761
2762 QQmlObjectOrGadget object = [&](){
2763 if (thisObject)
2765
2766 Scope scope(v4);
2769
2774 }();
2775
2776 if (object.isNull())
2777 return;
2778
2779 if (d()->index == DestroyMethod) {
2780 // method_destroy will use at most one argument
2782 v4, thisObject, argv, types, std::min(argc, 1),
2783 [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
2785 return method_destroy(v4, object.qObject(), argv, argc);
2786 });
2787 return;
2788 }
2789
2790 if (d()->index == ToStringMethod) {
2794 [v4, thisMeta, object](void **argv, int) {
2795 if (!argv[0])
2796 return;
2797 *static_cast<QString *>(argv[0])
2799 });
2800 return;
2801 }
2802
2804
2805 const QQmlPropertyData *method = d()->methods;
2806 if (d()->methodCount != 1) {
2807 Q_ASSERT(d()->methodCount > 0);
2809 }
2810
2811 if (!method || method->isV4Function()) {
2814 [this](const Value *thisObject, const Value *argv, int argc) {
2815 return callInternal(thisObject, argv, argc);
2816 });
2817 } else {
2821 [v4, object, valueWrapper, method](void **argv, int argc) {
2822 Q_UNUSED(argc);
2823
2824 // If we call the method, we have to write back any value type references afterwards.
2825 // The method might change the value.
2827 if (!method->isConstant()) {
2830 }
2831
2832 // If the method returns a QObject* we need to track it on the JS heap
2833 // (if it's destructible).
2834 QObject *qobjectPtr = nullptr;
2836 if (argv[0]) {
2838 qobjectPtr = *static_cast<QObject **>(argv[0]);
2839 } else if (resultType == QMetaType::fromType<QVariant>()) {
2840 const QVariant *result = static_cast<const QVariant *>(argv[0]);
2843 qobjectPtr = *static_cast<QObject *const *>(result->data());
2844 }
2845 }
2846
2847 if (qobjectPtr) {
2850 ddata->indestructible = false;
2852 }
2853 }
2854 });
2855 }
2856}
2857
2859
2860void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
2861{
2862 Object::init();
2863 this->signalIndex = signalIndex;
2864 setObject(object);
2865}
2866
2868
2870{
2874 << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
2875 "not call it directly. Make it a proper function and call "
2876 "that or emit the signal.")
2878
2879 Scope scope(engine());
2882 scope.engine,
2883 static_cast<Heap::QObjectWrapper *>(nullptr),
2884 signalIndex()));
2885
2886 return method->call(thisObject, argv, argc);
2887}
2888
2903
2904
2907{
2908 const QObjectBiPointer key = it.key();
2909 const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
2910 disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
2911 return QHash<QObjectBiPointer, WeakValue>::erase(it);
2912}
2913
2914void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
2915{
2916 QHash<QObjectBiPointer, WeakValue>::remove(object);
2917 QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object));
2918}
2919
2920} // namespace QV4
2921
2922QT_END_NAMESPACE
2923
2924#include "moc_qv4qobjectwrapper_p.cpp"
QHash< QObjectBiPointer, QV4::WeakValue >::Iterator Iterator
Definition qjsvalue.h:24
static int MatchScore(const Value &actual, QMetaType conversionMetaType)
static ReturnedValue doConvertReturnValue(ExecutionEngine *engine, QMetaType type, const void *data)
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)
static ReturnedValue doConvertReturnValue(ExecutionEngine *engine, const QVariant &value)
static bool recordVMEObjectForCompilationUnits(QObject *o, const QQmlVMEMetaObject *vme, MemoryManager::ObjectsForCompilationUnit *recorded)
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 bool recordObjectForCompilationUnits(QObject *o, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, MemoryManager::ObjectsForCompilationUnit *recorded)
static int numDefinedArguments(CallData *callArgs)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
Q_QML_EXPORT Q_DECL_COLD_FUNCTION void qt_v4AboutToCallNativeMethodHook(const QMetaObject *receiverMeta)
Q_QML_EXPORT bool qt_v4NativeCallHookEnabled
#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