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
296 if (property.isQVariant()) {
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()) {
376 return vmemo->vmeMethod(property->coreIndex());
377 } else if (property->isV4Function()) {
378 return QObjectMethod::create(
379 engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
380 } else if (property->isSignalHandler()) {
384 } else {
385 return QObjectMethod::create(
386 engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
387 }
388 }
389
391
392 if (ep && ep->propertyCapture && !property->isConstant()) {
397 }
398 }
399
400 if (property->isVarProperty()) {
404 } else {
406 }
407}
408
410 ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
411{
412 int index = 0;
413 if (name->equals(v4->id_destroy()))
415 else if (name->equals(v4->id_toString()))
417 else
418 return OptionalReturnedValue();
419
420 if (hasProperty)
421 *hasProperty = true;
423}
424
426 ExecutionEngine *v4, String *name, const QQmlRefPointer<QQmlContextData> &qmlContext,
427 QObject *qobj, bool *hasProperty = nullptr)
428{
429 if (!qmlContext || !qmlContext->imports())
430 return OptionalReturnedValue();
431
432 if (hasProperty)
433 *hasProperty = true;
434
435 if (QQmlTypeLoader *typeLoader = v4->typeLoader()) {
436 QQmlTypeNameCache::Result r = qmlContext->imports()->query(name, typeLoader);
437
438 if (!r.isValid())
439 return OptionalReturnedValue();
440
441 if (r.scriptIndex != -1) {
442 return OptionalReturnedValue(Encode::undefined());
443 } else if (r.type.isValid()) {
444 return OptionalReturnedValue(
445 QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
446 } else if (r.importNamespace) {
447 return OptionalReturnedValue(QQmlTypeWrapper::create(
448 v4, qobj, qmlContext->imports(), r.importNamespace,
449 Heap::QQmlTypeWrapper::ExcludeEnums));
450 }
451 Q_UNREACHABLE_RETURN(OptionalReturnedValue());
452 } else {
453 return OptionalReturnedValue();
454 }
455}
456
460{
461 // Keep this code in sync with ::virtualResolveLookupGetter
462
463 if (QQmlData::wasDeleted(d()->object())) {
464 if (hasProperty)
465 *hasProperty = false;
466 return Encode::undefined();
467 }
468
470
472 return *methodValue;
473
476
477 if (!result) {
478 // Check for attached properties
482 return *importProperty;
483 }
484 return Object::virtualGet(this, name->propertyKey(), this, hasProperty);
485 }
486
487 QQmlData *ddata = QQmlData::get(d()->object(), false);
488
489 if ((flags & CheckRevision) && result->hasRevision()) {
491 if (hasProperty)
492 *hasProperty = false;
493 return Encode::undefined();
494 }
495 }
496
497 if (hasProperty)
498 *hasProperty = true;
499
500 return getProperty(v4, d(), d()->object(), result, flags);
501}
502
507{
508 if (QQmlData::wasDeleted(object)) {
509 if (hasProperty)
510 *hasProperty = false;
511 return Encode::null();
512 }
513
515 return *methodValue;
516
517 QQmlData *ddata = QQmlData::get(object, false);
520
521 if (result) {
525 if (hasProperty)
526 *hasProperty = false;
527 return Encode::undefined();
528 }
529 }
530
531 if (hasProperty)
532 *hasProperty = true;
533
534 if (property && result != &local)
535 *property = result;
536
538 } else {
539 // Check if this object is already wrapped.
540 if (!ddata || (ddata->jsWrapper.isUndefined() &&
541 (ddata->jsEngineId == 0 || // Nobody owns the QObject
542 !ddata->hasTaintedV4Object))) { // Someone else has used the QObject, but it isn't tainted
543
544 // Not wrapped. Last chance: try query QObjectWrapper's prototype.
545 // If it can't handle this, then there is no point
546 // to wrap the QObject just to look at an empty set of JS props.
548 return proto->get(name, hasProperty);
549 }
550 }
551
552 // If we get here, we must already be wrapped (which implies a ddata).
553 // There's no point wrapping again, as there wouldn't be any new props.
555
558 if (!rewrapped) {
559 if (hasProperty)
560 *hasProperty = false;
561 return Encode::null();
562 }
564}
565
566
588
589/*!
590 \internal
591 If an QObjectWrapper is created via wrap, then it needs to be stored somewhere.
592 Otherwise, the garbage collector will immediately collect it if it is already
593 past the "mark QObjectWrapper's" phase (note that QObjectWrapper are marked
594 by iterating over a list of all QObjectWrapper, and then checking if the
595 wrapper fulfills some conditions).
596 However, sometimes we don't really want to keep a reference to the wrapper,
597 but just want to make sure that it exists (and we know that the wrapper
598 already fulfills the conditions to be kept alive). Then ensureWrapper
599 can be used, which creates the wrapper and ensures that it is also
600 marked.
601 */
610
613 const QQmlPropertyData *property, const Value &value)
614{
615 if (!property->isWritable() && !property->isQList()) {
616 QString error = QLatin1String("Cannot assign to read-only property \"") +
619 return;
620 }
621
624 if (f->as<QQmlTypeWrapper>()) {
625 // Ignore. It's probably a singleton or an attached type.
626 } else if (!f->isBinding()) {
627 const bool isAliasToAllowed = [&]() {
628 if (property->isAlias()) {
640 } else {
641 return false;
642 }
643 }();
646 // assigning a JS function to a non var or QJSValue property or is not allowed.
647 QString error = QLatin1String("Cannot assign JavaScript function to ");
648 if (!QMetaType(property->propType()).name())
649 error += QLatin1String("[unknown property type]");
650 else
653 return;
654 }
655 } else {
656
661
662 // binding assignment.
663 if (property->acceptsQBinding()) {
664 const QQmlPropertyIndex idx(property->coreIndex(), /*not a value type*/-1);
667 if (f->isBoundFunction()) {
668 auto boundFunction = static_cast<BoundFunction *>(f.getPointer());
671 } else {
674
675 }
677 void *argv = {&bindable};
678 // indirect metacall in case interceptors are installed
681 if (!ok) {
682 auto error = QStringLiteral("Failed to set binding on %1::%2.").
685 }
686 } else {
689 if (f->isBoundFunction())
691 newBinding->setTarget(object, *property, nullptr);
693 }
694 return;
695 }
696 }
697
701 binding && !binding->isSticky()) {
702 const auto stackFrame = engine->currentStackFrame;
703 switch (binding->kind()) {
705 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
707 "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
711 break;
712 }
716 "Overwriting binding on %s::%s at %s:%d",
719 break;
720 }
721 }
722 }
723 }
726
727 if (property->isVarProperty()) {
728 // allow assignment of "special" values (null, undefined, function) to var properties
732 return;
733 }
734
735#define PROPERTY_STORE(cpptype, value)
736 cpptype o = value;
737 int status = -1;
738 int flags = 0;
739 void *argv[] = { &o, 0, &status, &flags };
740 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
741
743 // functions are already handled, except for the QJSValue case
747
748 if (value.isNull() && property->isQObject()) {
749 PROPERTY_STORE(QObject*, nullptr);
750 } else if (value.isUndefined() && property->isResettable()) {
751 void *a[] = { nullptr };
753 } else if (value.isUndefined() && propType == QMetaType::fromType<QVariant>()) {
755 } else if (value.isUndefined() && propType == QMetaType::fromType<QJsonValue>()) {
757 } else if (propType == QMetaType::fromType<QJSValue>()) {
760 QString error = QLatin1String("Cannot assign [undefined] to ");
761 if (!propType.name())
762 error += QLatin1String("[unknown property type]");
763 else
766 return;
767 } else if (propType == QMetaType::fromType<int>() && value.isNumber()) {
769 } else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) {
771 } else if (propType == QMetaType::fromType<float>() && value.isNumber()) {
772 PROPERTY_STORE(float, float(value.asDouble()));
773 } else if (propType == QMetaType::fromType<double>() && value.isNumber()) {
774 PROPERTY_STORE(double, double(value.asDouble()));
775 } else if (propType == QMetaType::fromType<QString>() && value.isString()) {
777 } else if (property->isVarProperty()) {
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.
1588 }
1589 }
1590 }
1591 } else if (const auto &cu = ddata->compilationUnit) {
1592 // Objects without a VMEMetaObject are identified via QQmlData::compilationUnit.
1595 }
1596
1598 return;
1599
1600 // mark the const wrapper if our engine has interacted with it at some point
1603 scope, scope.engine->m_multiplyWrappedQObjects->value(static_cast<const QObject *>(o)));
1604
1605 if (!constWrapper)
1606 return;
1607
1608 if (that != constWrapper->d()) {
1609 // We've got the non-const wrapper. Also mark the const one.
1611 return;
1612 }
1613
1614 // We've got the const wrapper. Also mark the non-const one
1617 else
1619}
1620
1622{
1623 Heap::QObjectWrapper *h = d();
1625
1626 if (QObject *o = h->object()) {
1627 QQmlData *ddata = QQmlData::get(o, false);
1628 if (ddata) {
1629 if (!o->parent() && !ddata->indestructible) {
1630 if (ddata && ddata->ownContext) {
1634 ddata->context = nullptr;
1635 }
1636
1637 // This object is notionally destroyed now. It might still live until the next
1638 // event loop iteration, but it won't need its connections, CU, or deferredData
1639 // anymore.
1640
1641 ddata->isQueuedForDeletion = true;
1644
1647
1648 if (lastCall)
1649 delete o;
1650 else
1651 o->deleteLater();
1652 } else {
1653 // If the object is C++-owned, we still have to release the weak reference we have
1654 // to it. If the "main" wrapper is not ours, we should leave it alone, though.
1655 if (ddata->jsWrapper.as<QObjectWrapper>() == this)
1657 }
1658 }
1659 }
1660
1661 h->destroy();
1662}
1663
1664
1666
1667template<typename Retrieve>
1668int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
1669 if (conversionMetaType == QMetaType::fromType<QVariant>())
1670 return 0;
1671
1672 const QMetaType type = retrieve();
1673 if (type == conversionMetaType)
1674 return 0;
1675
1676 if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
1677 if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject))
1678 return 1;
1679 }
1680
1681 if (QMetaType::canConvert(type, conversionMetaType)) {
1682 if (conversionMetaType == QMetaType::fromType<QJSValue>()
1683 || conversionMetaType == QMetaType::fromType<double>()
1684 || conversionMetaType == QMetaType::fromType<QString>()) {
1685 // Unspecific conversions receive lower score. You can convert anything
1686 // to QString or double via toString() and valueOf(), respectively.
1687 // And anything can be wrapped into QJSValue, but that's inefficient.
1688 return 6;
1689 }
1690
1691 // We have an explicitly defined conversion method to a non-boring type.
1692 return 5;
1693 }
1694
1695 return 10;
1696};
1697
1698/*
1699 Returns the match score for converting \a actual to be of type \a conversionType. A
1700 zero score means "perfect match" whereas a higher score is worse.
1701
1702 The conversion table is copied out of the \l QScript::callQtMethod() function.
1703*/
1704static int MatchScore(const Value &actual, QMetaType conversionMetaType)
1705{
1706 const int conversionType = conversionMetaType.id();
1707 const auto convertibleScore = [&](QMetaType actualType) {
1708 // There are a number of things we can do in JavaScript to subvert this, but
1709 // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
1710 if (!QMetaType::canConvert(actualType, conversionMetaType))
1711 return 10;
1712
1713 // You can convert anything to QJSValue, but that's inefficient.
1714 // If we have a better option, we should use it.
1715 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1716 return 9;
1717
1718 // You can also convert anything to QVariant, but that's also suboptimal.
1719 // You can convert anything to string or double via toString() and valueOf().
1720 // Those are also rather unspecific.
1721 switch (conversionType) {
1722 case QMetaType::QVariant:
1723 case QMetaType::Double:
1724 case QMetaType::QString:
1725 return 9;
1726 default:
1727 break;
1728 }
1729
1730 // We have an explicitly defined conversion method to a non-boring type.
1731 return 8;
1732 };
1733
1734 if (actual.isNumber()) {
1735 switch (conversionType) {
1736 case QMetaType::Double:
1737 return 0;
1738 case QMetaType::Float:
1739 return 1;
1740 case QMetaType::LongLong:
1741 case QMetaType::ULongLong:
1742 return 2;
1743 case QMetaType::Long:
1744 case QMetaType::ULong:
1745 return 3;
1746 case QMetaType::Int:
1747 case QMetaType::UInt:
1748 return 4;
1749 case QMetaType::Short:
1750 case QMetaType::UShort:
1751 return 5;
1752 break;
1753 case QMetaType::Char:
1754 case QMetaType::UChar:
1755 return 6;
1756 case QMetaType::QJsonValue:
1757 return 5;
1758 default:
1759 return convertibleScore(actual.isInteger()
1760 ? QMetaType::fromType<int>()
1761 : QMetaType::fromType<double>());
1762 }
1763 } else if (actual.isString()) {
1764 switch (conversionType) {
1765 case QMetaType::QString:
1766 return 0;
1767 case QMetaType::QJsonValue:
1768 return 5;
1769 case QMetaType::QUrl:
1770 return 6; // we like to convert strings to URLs in QML
1771 case QMetaType::Double:
1772 case QMetaType::Float:
1773 case QMetaType::LongLong:
1774 case QMetaType::ULongLong:
1775 case QMetaType::Int:
1776 case QMetaType::UInt:
1777 case QMetaType::Short:
1778 case QMetaType::UShort:
1779 case QMetaType::Char:
1780 case QMetaType::UChar:
1781 // QMetaType can natively convert strings to numbers.
1782 // However, in the general case it's of course extremely lossy.
1783 return 10;
1784 default:
1785 return convertibleScore(QMetaType::fromType<QString>());
1786 }
1787 } else if (actual.isBoolean()) {
1788 switch (conversionType) {
1789 case QMetaType::Bool:
1790 return 0;
1791 case QMetaType::QJsonValue:
1792 return 5;
1793 default:
1794 return convertibleScore(QMetaType::fromType<bool>());
1795 }
1796 } else if (actual.as<DateObject>()) {
1797 switch (conversionType) {
1798 case QMetaType::QDateTime:
1799 return 0;
1800 case QMetaType::QDate:
1801 return 1;
1802 case QMetaType::QTime:
1803 return 2;
1804 default:
1805 return convertibleScore(QMetaType::fromType<QDateTime>());
1806 }
1807 } else if (actual.as<RegExpObject>()) {
1808 switch (conversionType) {
1809#if QT_CONFIG(regularexpression)
1810 case QMetaType::QRegularExpression:
1811 return 0;
1812 default:
1813 return convertibleScore(QMetaType::fromType<QRegularExpression>());
1814#else
1815 default:
1816 return convertibleScore(QMetaType());
1817#endif
1818 }
1819 } else if (actual.as<ArrayBuffer>()) {
1820 switch (conversionType) {
1821 case QMetaType::QByteArray:
1822 return 0;
1823 default:
1824 return convertibleScore(QMetaType::fromType<QByteArray>());
1825 }
1826 } else if (actual.as<ArrayObject>()) {
1827 switch (conversionType) {
1828 case QMetaType::QJsonArray:
1829 return 3;
1830 case QMetaType::QStringList:
1831 case QMetaType::QVariantList:
1832 return 5;
1833 case QMetaType::QVector4D:
1834 case QMetaType::QMatrix4x4:
1835 return 6;
1836 case QMetaType::QVector3D:
1837 return 7;
1838 default:
1839 return convertibleScore(QMetaType());
1840 }
1841 } else if (actual.isNull()) {
1842 switch (conversionType) {
1843 case QMetaType::Nullptr:
1844 case QMetaType::VoidStar:
1845 case QMetaType::QObjectStar:
1846 case QMetaType::QJsonValue:
1847 return 0;
1848 default: {
1849 if (conversionMetaType.flags().testFlag(QMetaType::IsPointer))
1850 return 0;
1851 else
1852 return convertibleScore(QMetaType());
1853 }
1854 }
1855 } else if (const Object *obj = actual.as<Object>()) {
1856 if (const VariantObject *variantObject = obj->as<VariantObject>()) {
1857 return MatchVariant(conversionMetaType, [variantObject]() {
1858 return variantObject->d()->data().metaType();
1859 });
1860 }
1861
1862 if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
1863 switch (conversionType) {
1864 case QMetaType::QObjectStar:
1865 return 0;
1866 default:
1867 if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
1868 QObject *wrapped = wrapper->object();
1869 if (!wrapped)
1870 return 0;
1871 if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject()))
1872 return 0;
1873 }
1874 }
1875
1876 return convertibleScore(QMetaType::fromType<QObject *>());
1877 }
1878
1879 if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
1880 const QQmlType type = wrapper->d()->type();
1881 if (type.isSingleton()) {
1882 const QMetaType metaType = type.typeId();
1883 if (metaType == conversionMetaType)
1884 return 0;
1885
1886 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1887 && metaType.flags() & QMetaType::PointerToQObject
1888 && type.metaObject()->inherits(conversionMetaType.metaObject())) {
1889 return 0;
1890 }
1891 } else if (QObject *object = wrapper->object()) {
1892 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1893 && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) {
1894 return 0;
1895 }
1896 }
1897
1898 return convertibleScore(QMetaType());
1899 }
1900
1901 if (const Sequence *sequence = obj->as<Sequence>()) {
1902 const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(sequence);
1903 if (sequenceType == conversionMetaType)
1904 return 1;
1905
1906 return convertibleScore(sequenceType);
1907 }
1908
1909 if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
1910 return MatchVariant(conversionMetaType, [wrapper]() {
1911 return wrapper->d()->isVariant()
1912 ? wrapper->toVariant().metaType()
1913 : wrapper->type();
1914 });
1915 }
1916
1917 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1918 return 0;
1919
1920 switch (conversionType) {
1921 case QMetaType::QJsonObject:
1922 case QMetaType::QVariantMap:
1923 return 5;
1924 default:
1925 break;
1926 }
1927
1928 }
1929
1930 return convertibleScore(QMetaType());
1931}
1932
1933static int numDefinedArguments(CallData *callArgs)
1934{
1935 int numDefinedArguments = callArgs->argc();
1936 while (numDefinedArguments > 0
1937 && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) {
1938 --numDefinedArguments;
1939 }
1940 return numDefinedArguments;
1941}
1942
1943static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
1944{
1945 const QMetaObject *metaObject = object.metaObject();
1946 const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
1947 return indexOfClassInfo != -1
1948 && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
1949}
1950
1951static ReturnedValue doConvertReturnValue(ExecutionEngine *engine, QMetaType type, const void *data)
1952{
1953 if (type.flags() & QMetaType::PointerToQObject) {
1954 // We consider QObjects returned from invokables as owned by the QML engine unless
1955 // explicitly configured otherwise.
1956 if (QObject *object = *static_cast<QObject *const *>(data))
1957 QQmlData::get(object, true)->setImplicitDestructible();
1958 }
1959 return engine->fromData(type, data);
1960}
1961
1962static ReturnedValue doConvertReturnValue(ExecutionEngine *engine, const QVariant &value)
1963{
1964 // QVariant is treated as transparent when returned. That means we still adopt
1965 // QObjects if they're wrapped in QVariant. See above.
1966 return doConvertReturnValue(engine, value.metaType(), value.constData());
1967}
1968
1972{
1973 const auto constructReturnValue = [&](QMetaType returnType, void *returnValue) {
1974 // If we construct a value in place, we mustn't initialize the memory before that.
1977 };
1978
1979 const auto convertReturnValue
1981
1982 // In contrast to other invocations of convertAndCall() we do not want to create
1983 // a URL object from a QUrl here like metaTypeFromJS does. This is for compatibility.
1984 // URL objects are proper, specified, objects that behave different from our variant
1985 // objects when it comes to equality comparisons.
1986
1988 ? doConvertReturnValue(engine, *reinterpret_cast<const QVariant *>(returnValue))
1990
1991 // NB: A type can be both a PointerToQObject and NeedsDestruction if the user registers
1992 // such a thing explicitly. We respect that.
1995 return result;
1996 };
1997
1998 const auto conversionErrorHandler = [&](QMetaType argumentType, void *argument, int ii) {
1999 // We've checked the number of arguments before
2000 Q_ASSERT(ii < callArgs->argc());
2001
2003 // We have an engine here. So we can conjure up a QJSManagedValue for anything.
2004 *static_cast<QJSManagedValue *>(argument)
2006 return true;
2007 }
2008
2010 && callArgs->args[ii].isUndefined()) {
2011 // TODO: QObjectMethod silently converts undefined to null when you pass undefined
2012 // to a method that accepts some QObject-derived. This is wrong because
2013 // undefined is certainly not null. We accept it for compatibility.
2014 *static_cast<QObject **>(argument) = nullptr;
2015 return true;
2016 }
2017
2018 // TODO: QObjectMethod is allowed to use QVariant conversion.
2019 // This is wrong because we can't see the converters at compile time.
2020 // It only exists for compatibility with old versions of Qt.
2024 return true;
2025
2026 qWarning() << QString::fromLatin1("Could not convert argument %1 from %2 to %3")
2027 .arg(ii)
2029 .arg(argumentType.name());
2030 const StackTrace stack = engine->stackTrace();
2031 for (const StackFrame &frame : stack) {
2032 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source + (frame.line > 0
2033 ? (QLatin1Char(':') + QString::number(frame.line))
2034 : QString());
2035
2036 }
2037
2039 qWarning() << "Passing incompatible arguments to signals is not supported.";
2041 }
2042
2044 "Passing incompatible arguments to C++ functions from JavaScript is not allowed."));
2045 return false;
2046 };
2047
2049
2050 const auto handleTooManyArguments = [&](int expectedArguments) {
2052 engine->throwError(QStringLiteral("Too many arguments"));
2053 return false;
2054 }
2055
2056 const auto stackTrace = engine->stackTrace();
2057 if (stackTrace.isEmpty()) {
2059 << "When matching arguments for "
2060 << object.className() << "::" << data.name(object.metaObject()) << "():";
2061 } else {
2062 const StackFrame frame = stackTrace.first();
2064 + (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
2065 : QString());
2066 }
2067
2068 qWarning().noquote() << QStringLiteral("Too many arguments, ignoring %1")
2070 return true;
2071 };
2072
2074
2075 if (data.hasArguments()) {
2077
2078 const bool ok = data.isConstructor()
2083 if (!ok)
2085
2086 const int expectedArgumentCount = storage.size() - 1;
2088 return engine->throwError(QLatin1String("Insufficient arguments"));
2089
2092 return Encode::undefined();
2093 }
2094
2095 return QV4::convertAndCall(
2098 [&](void **argData, const QMetaType *types, int argc) {
2099 Q_UNUSED(types);
2100 Q_UNUSED(argc);
2103 }
2104
2106 return Encode::undefined();
2107
2109 if (!returnType.isValid())
2111
2112 return QV4::convertAndCall(
2113 engine, &returnType, 1, nullptr, 0,
2114 [&](void **argData, const QMetaType *types, int argc) {
2115 Q_UNUSED(types);
2116 Q_UNUSED(argc);
2119}
2120
2121/*
2122Resolve the overloaded method to call. The algorithm works conceptually like this:
2123 1. Resolve the set of overloads it is *possible* to call.
2124 Impossible overloads include those that have too many parameters or have parameters
2125 of unknown type.
2126 2. Filter the set of overloads to only contain those with the closest number of
2127 parameters.
2128 For example, if we are called with 3 parameters and there are 2 overloads that
2129 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
2130 3. Find the best remaining overload based on its match score.
2131 If two or more overloads have the same match score, return the last one. The match
2132 score is constructed by adding the matchScore() result for each of the parameters.
2133*/
2137{
2138 const int argumentCount = callArgs->argc();
2140
2141 const QQmlPropertyData *best = nullptr;
2145
2148
2149 for (int i = 0; i < methodCount; ++i) {
2150 const QQmlPropertyData *attempt = methods + i;
2151
2157 qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
2158 }
2159
2160 // QQmlV4Function overrides anything that doesn't provide the exact number of arguments
2161 int methodParameterScore = 1;
2162 // QQmlV4Function overrides the "no idea" option, which is 10
2163 int maxMethodMatchScore = 9;
2164 // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments
2166
2167 if (!attempt->isV4Function()) {
2169 int methodArgumentCount = 0;
2170 if (attempt->hasArguments()) {
2171 if (attempt->isConstructor()) {
2172 if (!object.constructorParameterTypes(*attempt, &storage, nullptr)) {
2173 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2174 continue;
2175 }
2176 } else {
2177 if (!object.methodParameterTypes(*attempt, &storage, nullptr)) {
2178 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2179 continue;
2180 }
2181 }
2183 }
2184
2186 qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
2187 continue; // We don't have sufficient arguments to call this method
2188 }
2189
2191 ? 0
2194 qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
2195 continue; // We already have a better option
2196 }
2197
2200 for (int ii = 0; ii < methodArgumentCount; ++ii) {
2201 const int score = MatchScore((v = Value::fromStaticValue(callArgs->args[ii])),
2202 storage[ii]);
2205 }
2206 }
2207
2212 best = attempt;
2216 qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
2217 << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
2218 << "bestSumMatchScore" << bestSumMatchScore << "\n";
2219 } else {
2220 qCDebug(lcOverloadResolution) << "did not update best\n"
2221 << "bestParameterScore" << bestParameterScore << "\t"
2222 << "methodParameterScore" << methodParameterScore << "\n"
2223 << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
2224 << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
2225 << "bestSumMatchScore" << bestSumMatchScore << "\t"
2226 << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
2227 }
2228
2229 if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
2230 qCDebug(lcOverloadResolution, "perfect match");
2231 break; // We can't get better than that
2232 }
2233
2234 };
2235
2236 if (best && best->isValid()) {
2237 return best;
2238 } else {
2239 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
2240 for (int i = 0; i < methodCount; ++i) {
2245 error += u"\n " + QString::fromUtf8(m.methodSignature());
2246 }
2247
2249 return nullptr;
2250 }
2251}
2252
2253static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
2254{
2255 if (required == QMetaType::fromType<QVariant>()
2256 || required == QMetaType::fromType<QJSValue>()
2257 || required == QMetaType::fromType<QJSManagedValue>()) {
2258 return true;
2259 }
2260
2261 if (data) {
2262 if (passed == QMetaType::fromType<QVariant>())
2263 passed = static_cast<const QVariant *>(data)->metaType();
2264 else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
2265 passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
2266 }
2267
2268 if (passed == required)
2269 return true;
2270
2271 if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
2272 switch (passed.id()) {
2273 case QMetaType::UnknownType:
2274 case QMetaType::Nullptr:
2275 case QMetaType::Bool:
2276 case QMetaType::Int:
2277 case QMetaType::Double:
2278 case QMetaType::QString:
2279 return true;
2280 default:
2281 break;
2282 }
2283 }
2284
2285 return false;
2286}
2287
2289 const QMetaMethod &method, void **argv, int argc, const QMetaType *types)
2290{
2291 if (types[0].isValid() && !ExactMatch(method.returnMetaType(), types[0], nullptr))
2292 return false;
2293
2294 if (method.parameterCount() != argc)
2295 return false;
2296
2297 for (int i = 0; i < argc; ++i) {
2298 if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1]))
2299 return false;
2300 }
2301
2302 return true;
2303}
2304
2307 void **argv, int argc, const QMetaType *types)
2308{
2309 // We only accept exact matches here. Everything else goes through the JavaScript conversion.
2310 for (int i = 0; i < methodCount; ++i) {
2311 const QQmlPropertyData *attempt = methods + i;
2313 return attempt;
2314 }
2315
2316 return nullptr;
2317}
2318
2327
2337
2341{
2343
2345 if (cloneFrom->wrapper) {
2347 if (ref) {
2349 } else {
2350 // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what
2351 // value we should operate on. Without knowledge of the property the value
2352 // was read from, we cannot load the value from the given object.
2353 return Encode::undefined();
2354 }
2355 }
2356
2358 valueScope,
2361
2363
2364 Q_ASSERT(method->d()->methods == nullptr);
2365 switch (cloneFrom->methodCount) {
2366 case 0:
2367 Q_ASSERT(cloneFrom->methods == nullptr);
2368 break;
2369 case 1:
2371 == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
2372 method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
2373 *method->d()->methods = *cloneFrom->methods;
2374 break;
2375 default:
2376 Q_ASSERT(cloneFrom->methods != nullptr);
2380 break;
2381 }
2382
2383 return method.asReturnedValue();
2384}
2385
2387{
2391}
2392
2394{
2396
2398 return valueWrapper->metaObject();
2399 if (QObject *self = object())
2400 return self->metaObject();
2401
2402 return nullptr;
2403}
2404
2406{
2408
2410 return objectWrapper->object();
2412 return typeWrapper->object();
2413 return nullptr;
2414}
2415
2416bool Heap::QObjectMethod::isDetached() const
2417{
2418 if (!wrapper)
2419 return true;
2420
2423 return valueWrapper->d()->object() == nullptr;
2424
2425 return false;
2426}
2427
2429{
2432 return objectWrapper->object() == o;
2434 return typeWrapper->object() == o;
2435
2439 return qobject->object() == o;
2441 return type->object() == o;
2442
2443 // Attached to some nested value type or sequence object
2444 return false;
2445 }
2446
2447 return false;
2448}
2449
2451 const QMetaObject *thisMeta) const
2452{
2453 // Check that the metaobject matches.
2454
2455 if (!thisMeta) {
2456 // You can only get a detached method via a lookup, and then you have a thisObject.
2458 return Included;
2459 }
2460
2461 const auto check = [&](const QMetaObject *included) {
2466 "%s:%d: Calling C++ methods with 'this' objects different from the one "
2467 "they were retrieved from is broken, due to historical reasons. The "
2468 "original object is used as 'this' object. You can allow the given "
2469 "'this' object to be used by setting "
2470 "'pragma NativeMethodBehavior: AcceptThisObject'",
2472 return Included;
2473 }
2474
2475 // destroy() and toString() can be called on all QObjects, but not on gadgets.
2476 if (index < 0)
2478
2479 // Find the base type the method belongs to.
2481 while (true) {
2482 if (included == thisMeta)
2483 return Explicit;
2484
2485 if (methodOffset <= index)
2487
2491 };
2492
2494 };
2495
2496 if (const QMetaObject *meta = metaObject())
2497 return check(meta);
2498
2499 // If the QObjectMethod is detached, we can only have gotten here via a lookup.
2500 // The lookup checks that the QQmlPropertyCache matches.
2501 return Explicit;
2502}
2503
2505{
2507 return QStringLiteral("destroy");
2508 else if (index == QV4::QObjectMethod::ToStringMethod)
2509 return QStringLiteral("toString");
2510
2511 const QMetaObject *mo = metaObject();
2512 if (!mo)
2513 return QString();
2514
2515 int methodOffset = mo->methodOffset();
2516 while (methodOffset > index) {
2517 mo = mo->superClass();
2519 }
2520
2521 return "%1::%2"_L1.arg(QLatin1StringView{mo->className()},
2523}
2524
2526{
2527 if (methods) {
2528 Q_ASSERT(methodCount > 0);
2529 return;
2530 }
2531
2532 const QMetaObject *mo = metaObject();
2533
2534 if (!mo)
2535 mo = thisMeta;
2536
2537 Q_ASSERT(mo);
2538
2539 int methodOffset = mo->methodOffset();
2540 while (methodOffset > index) {
2541 mo = mo->superClass();
2543 }
2547 dummy.load(method);
2550 // Look for overloaded methods
2552 for (int ii = index - 1; ii >= methodOffset; --ii) {
2553 if (methodName == mo->method(ii).name()) {
2554 method = mo->method(ii);
2555 dummy.load(method);
2557 }
2558 }
2559 if (resolvedMethods.size() > 1) {
2563 } else {
2564 methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod);
2566 methodCount = 1;
2567 }
2568
2569 Q_ASSERT(methodCount > 0);
2570}
2571
2578
2580 ExecutionEngine *engine, QObject *o, const Value *args, int argc) const
2581{
2582 method_destroy(engine, o, argc > 0 ? args[0].toInt32() : 0);
2583 return Encode::undefined();
2584}
2585
2587{
2588 if (!o)
2589 return true;
2590
2592 engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2593 return false;
2594 }
2595
2596 if (delay > 0)
2598 else
2599 o->deleteLater();
2600
2601 return true;
2602}
2603
2605 const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2606{
2607 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2609}
2610
2612 const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
2613{
2614 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2616}
2617
2619{
2621
2622 const QMetaObject *thisMeta = nullptr;
2623
2624 QObject *o = nullptr;
2626 if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
2627 thisMeta = w->metaObject();
2628 o = w->object();
2629 } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
2630 thisMeta = w->metaObject();
2631 o = w->object();
2632 } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
2633 thisMeta = w->metaObject();
2634 valueWrapper = w->d();
2635 }
2636
2638 if (o && o == d()->object()) {
2640 // Nothing to do; objects are the same. This should be common
2641 } else if (valueWrapper && valueWrapper == d()->wrapper) {
2643 // Nothing to do; gadgets are the same. This should be somewhat common
2644 } else {
2646 if (mode == Heap::QObjectMethod::Invalid) {
2647 v4->throwError(QLatin1String("Cannot call method %1 on %2").arg(
2648 d()->name(), thisObject->toQStringNoThrow()));
2649 return Encode::undefined();
2650 }
2651 }
2652
2653 QQmlObjectOrGadget object = [&](){
2654 if (mode == Heap::QObjectMethod::Included) {
2655 QV4::Scope scope(v4);
2659 return QQmlObjectOrGadget(type->object());
2661 valueWrapper = value->d();
2663 }
2664 Q_UNREACHABLE();
2665 } else {
2666 if (o)
2667 return QQmlObjectOrGadget(o);
2668
2673 }
2674 }();
2675
2676 if (object.isNull())
2677 return Encode::undefined();
2678
2679 if (d()->index == DestroyMethod)
2680 return method_destroy(v4, object.qObject(), argv, argc);
2681 else if (d()->index == ToStringMethod) {
2683 const QMetaObject *meta = object.metaObject();
2684 if (meta && meta->indexOfMethod("toString()") >= 0)
2686 }
2687 return method_toString(v4, object.qObject());
2688 }
2689
2691
2692 Scope scope(v4);
2695
2696 const QQmlPropertyData *method = d()->methods;
2697
2698 // If we call the method, we have to write back any value type references afterwards.
2699 // The method might change the value.
2700 const auto doCall = [&](const auto &call) {
2701 if (!method->isConstant()) {
2705 return rv->asReturnedValue();
2706 }
2707 }
2708
2709 return call();
2710 };
2711
2712 if (d()->methodCount != 1) {
2713 Q_ASSERT(d()->methodCount > 0);
2715 if (method == nullptr)
2716 return Encode::undefined();
2717 }
2718
2721
2722 if (method->isV4Function()) {
2723 return doCall([&]() {
2727
2728 void *args[] = { nullptr, &funcptr };
2730
2731 return rv->asReturnedValue();
2732 });
2733 }
2734
2735 return doCall([&]() { return callPrecise(object, *method, v4, callData); });
2736}
2737
2739{
2740 constexpr int parameterCount() const { return 0; }
2741 constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
2742 constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
2743};
2744
2746 QObject *thisObject, void **argv, const QMetaType *types, int argc) const
2747{
2749
2750 const QMetaObject *thisMeta = nullptr;
2752
2753 if (thisObject) {
2755 } else {
2759 }
2760
2761 QQmlObjectOrGadget object = [&](){
2762 if (thisObject)
2764
2765 Scope scope(v4);
2768
2773 }();
2774
2775 if (object.isNull())
2776 return;
2777
2778 if (d()->index == DestroyMethod) {
2779 // method_destroy will use at most one argument
2781 v4, thisObject, argv, types, std::min(argc, 1),
2782 [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
2784 return method_destroy(v4, object.qObject(), argv, argc);
2785 });
2786 return;
2787 }
2788
2789 if (d()->index == ToStringMethod) {
2793 [v4, thisMeta, object](void **argv, int) {
2794 if (!argv[0])
2795 return;
2796 *static_cast<QString *>(argv[0])
2798 });
2799 return;
2800 }
2801
2803
2804 const QQmlPropertyData *method = d()->methods;
2805 if (d()->methodCount != 1) {
2806 Q_ASSERT(d()->methodCount > 0);
2808 }
2809
2810 if (!method || method->isV4Function()) {
2813 [this](const Value *thisObject, const Value *argv, int argc) {
2814 return callInternal(thisObject, argv, argc);
2815 });
2816 } else {
2820 [v4, object, valueWrapper, method](void **argv, int argc) {
2821 Q_UNUSED(argc);
2822
2823 // If we call the method, we have to write back any value type references afterwards.
2824 // The method might change the value.
2826 if (!method->isConstant()) {
2829 }
2830
2831 // If the method returns a QObject* we need to track it on the JS heap
2832 // (if it's destructible).
2833 QObject *qobjectPtr = nullptr;
2835 if (argv[0]) {
2837 qobjectPtr = *static_cast<QObject **>(argv[0]);
2838 } else if (resultType == QMetaType::fromType<QVariant>()) {
2839 const QVariant *result = static_cast<const QVariant *>(argv[0]);
2842 qobjectPtr = *static_cast<QObject *const *>(result->data());
2843 }
2844 }
2845
2846 if (qobjectPtr) {
2849 ddata->indestructible = false;
2851 }
2852 }
2853 });
2854 }
2855}
2856
2858
2859void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
2860{
2861 Object::init();
2862 this->signalIndex = signalIndex;
2863 setObject(object);
2864}
2865
2867
2869{
2873 << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
2874 "not call it directly. Make it a proper function and call "
2875 "that or emit the signal.")
2877
2878 Scope scope(engine());
2881 scope.engine,
2882 static_cast<Heap::QObjectWrapper *>(nullptr),
2883 signalIndex()));
2884
2885 return method->call(thisObject, argv, argc);
2886}
2887
2902
2903
2906{
2907 const QObjectBiPointer key = it.key();
2908 const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
2909 disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
2910 return QHash<QObjectBiPointer, WeakValue>::erase(it);
2911}
2912
2913void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
2914{
2915 QHash<QObjectBiPointer, WeakValue>::remove(object);
2916 QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object));
2917}
2918
2919} // namespace QV4
2920
2921QT_END_NAMESPACE
2922
2923#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