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 */
611
614 const QQmlPropertyData *property, const Value &value)
615{
616 if (!property->isWritable() && !property->isQList()) {
617 QString error = QLatin1String("Cannot assign to read-only property \"") +
620 return;
621 }
622
625 if (f->as<QQmlTypeWrapper>()) {
626 // Ignore. It's probably a singleton or an attached type.
627 } else if (!f->isBinding()) {
628 const bool isAliasToAllowed = [&]() {
629 if (property->isAlias()) {
641 } else {
642 return false;
643 }
644 }();
647 // assigning a JS function to a non var or QJSValue property or is not allowed.
648 QString error = QLatin1String("Cannot assign JavaScript function to ");
649 if (!QMetaType(property->propType()).name())
650 error += QLatin1String("[unknown property type]");
651 else
654 return;
655 }
656 } else {
657
662
663 // binding assignment.
664 if (property->acceptsQBinding()) {
665 const QQmlPropertyIndex idx(property->coreIndex(), /*not a value type*/-1);
668 if (f->isBoundFunction()) {
669 auto boundFunction = static_cast<BoundFunction *>(f.getPointer());
672 } else {
675
676 }
678 void *argv = {&bindable};
679 // indirect metacall in case interceptors are installed
682 if (!ok) {
683 auto error = QStringLiteral("Failed to set binding on %1::%2.").
686 }
687 } else {
690 if (f->isBoundFunction())
692 newBinding->setTarget(object, *property, nullptr);
694 }
695 return;
696 }
697 }
698
702 binding && !binding->isSticky()) {
703 const auto stackFrame = engine->currentStackFrame;
704 switch (binding->kind()) {
706 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
708 "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
712 break;
713 }
717 "Overwriting binding on %s::%s at %s:%d",
720 break;
721 }
722 }
723 }
724 }
727
728 if (property->isVarProperty()) {
729 // allow assignment of "special" values (null, undefined, function) to var properties
732 return;
733 }
734 }
735
736#define PROPERTY_STORE(cpptype, value)
737 cpptype o = value;
738 int status = -1;
739 int flags = 0;
740 void *argv[] = { &o, 0, &status, &flags };
741 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
742
744 // functions are already handled, except for the QJSValue case and for var properties of
745 // objects owned by another engine
750
751 if (value.isNull() && property->isQObject()) {
752 PROPERTY_STORE(QObject*, nullptr);
753 } else if (value.isUndefined() && property->isResettable()) {
754 void *a[] = { nullptr };
756 } else if (value.isUndefined() && propType == QMetaType::fromType<QVariant>()) {
758 } else if (value.isUndefined() && propType == QMetaType::fromType<QJsonValue>()) {
760 } else if (propType == QMetaType::fromType<QJSValue>()) {
763 QString error = QLatin1String("Cannot assign [undefined] to ");
764 if (!propType.name())
765 error += QLatin1String("[unknown property type]");
766 else
769 return;
770 } else if (propType == QMetaType::fromType<int>() && value.isNumber()) {
772 } else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) {
774 } else if (propType == QMetaType::fromType<float>() && value.isNumber()) {
775 PROPERTY_STORE(float, float(value.asDouble()));
776 } else if (propType == QMetaType::fromType<double>() && value.isNumber()) {
777 PROPERTY_STORE(double, double(value.asDouble()));
778 } else if (propType == QMetaType::fromType<QString>() && value.isString()) {
781 && (value.isUndefined() || value.isPrimitive())) {
782 QQmlScriptString ss(value.toQStringNoThrow(), nullptr /* context */, object);
783 if (value.isNumber()) {
785 ss.d->isNumberLiteral = true;
786 } else if (value.isString()) {
788 ss.d->isStringLiteral = true;
789 }
791 } else {
792 QVariant v;
795 else
797
800 const char *valueType = (v.userType() == QMetaType::UnknownType)
801 ? "an unknown type"
802 : QMetaType(v.userType()).name();
803
804 const char *targetTypeName = propType.name();
805 if (!targetTypeName)
806 targetTypeName = "an unregistered type";
807
808 QString error = QLatin1String("Cannot assign ") +
810 QLatin1String(" to ") +
813 return;
814 }
815 }
816}
817
819{
821
822 QQmlData *ddata = QQmlData::get(object, true);
823 if (!ddata)
824 return Encode::undefined();
825
827
828 if (ddata->jsWrapper.isUndefined() &&
829 (ddata->jsEngineId == engine->m_engineId || // We own the QObject
830 ddata->jsEngineId == 0 || // No one owns the QObject
831 !ddata->hasTaintedV4Object)) { // Someone else has used the QObject, but it isn't tainted
832
836 return rv->asReturnedValue();
837
838 } else {
839 // If this object is tainted, we have to check to see if it is in our
840 // tainted object list
844
845 // If our tainted handle doesn't exist or has been collected, and there isn't
846 // a handle in the ddata, we can assume ownership of the ddata->jsWrapper
851 return result->asReturnedValue();
852 }
853
854 if (!alternateWrapper) {
860 }
861
863 }
864}
865
867{
868 const QObject *constObject = object;
869
870 QQmlData *ddata = QQmlData::get(object, true);
871
876
877 if (!constWrapper) {
883 ddata->hasConstWrapper = true;
884 }
885
887}
888
906
911
913{
914 Q_ASSERT(propertyIndex < 0xffff);
916
918 return;
919 QQmlData *ddata = QQmlData::get(object, /*create*/false);
920 if (!ddata)
921 return;
922
925 Q_ASSERT(property); // We resolved this property earlier, so it better exist!
927}
928
930{
932 const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a);
935
936 // We can have a const and a non-const wrapper for the same object.
939}
940
951
963
965{
966 if (!id.isString())
967 return Object::virtualPut(m, id, value, receiver);
968
969 Scope scope(m);
970 QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
972
973 if (that->internalClass()->isFrozen()) {
974 QString error = QLatin1String("Cannot assign to property \"") +
975 name->toQString() + QLatin1String("\" of read-only object");
977 return false;
978 }
979
981 return false;
982
986 // Types created by QML are not extensible at run-time, but for other QObjects we can store them
987 // as regular JavaScript properties, like on JavaScript objects.
988 if (ddata && ddata->context) {
989 QString error = QLatin1String("Cannot assign to non-existent property \"") +
990 name->toQString() + QLatin1Char('\"');
992 return false;
993 } else {
994 return Object::virtualPut(m, id, value, receiver);
995 }
996 }
997
998 return true;
999}
1000
1002{
1003 if (id.isString()) {
1004 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
1005 const QObject *thatObject = that->d()->object();
1007 Scope scope(m);
1013 if (p) {
1014 // ### probably not the fastest implementation
1015 bool hasProperty;
1018 }
1019 return Attr_Data;
1020 }
1021 }
1022 }
1023
1024 return Object::virtualGetOwnProperty(m, id, p);
1025}
1026
1028{
1031 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
1032
1033private:
1034 QSet<QByteArray> m_alreadySeen;
1035};
1036
1037PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
1038{
1039 // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
1040 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
1041 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
1042 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
1043
1044 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(o);
1045
1046 QObject *thatObject = that->d()->object();
1047 if (thatObject && !QQmlData::wasDeleted(thatObject)) {
1048 const QMetaObject *mo = thatObject->metaObject();
1049 // These indices don't apply to gadgets, so don't block them.
1050 const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
1051 const int propertyCount = mo->propertyCount();
1052 if (propertyIndex < propertyCount) {
1053 ExecutionEngine *thatEngine = that->engine();
1054 Scope scope(thatEngine);
1055 const QMetaProperty property = mo->property(propertyIndex);
1056 ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name())));
1057 ++propertyIndex;
1058 if (attrs)
1059 *attrs= Attr_Data;
1060 if (pd) {
1061 QQmlPropertyData local;
1062 local.load(property);
1063 pd->value = that->getProperty(
1064 thatEngine, that->d(), thatObject, &local,
1065 QObjectWrapper::AttachMethods);
1066 }
1067 return propName->toPropertyKey();
1068 }
1069 const int methodCount = mo->methodCount();
1070 while (propertyIndex < propertyCount + methodCount) {
1071 Q_ASSERT(propertyIndex >= propertyCount);
1072 int index = propertyIndex - propertyCount;
1073 const QMetaMethod method = mo->method(index);
1074 ++propertyIndex;
1075 if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
1076 continue;
1077 // filter out duplicates due to overloads:
1078 if (m_alreadySeen.contains(method.name()))
1079 continue;
1080 else
1081 m_alreadySeen.insert(method.name());
1082 ExecutionEngine *thatEngine = that->engine();
1083 Scope scope(thatEngine);
1084 ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name())));
1085 if (attrs)
1086 *attrs = Attr_Data;
1087 if (pd) {
1088 QQmlPropertyData local;
1089 local.load(method);
1090 pd->value = that->getProperty(
1091 thatEngine, that->d(), thatObject, &local,
1092 QObjectWrapper::AttachMethods);
1093 }
1094 return methodName->toPropertyKey();
1095 }
1096 }
1097
1098 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1099}
1100
1106
1108{
1109 // Keep this code in sync with ::getQmlProperty
1112 if (!id.isString())
1115
1116 const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
1119
1120 QObject * const qobj = This->d()->object();
1121
1122 if (QQmlData::wasDeleted(qobj))
1123 return Encode::undefined();
1124
1125 QQmlData *ddata = QQmlData::get(qobj, false);
1128 if (!ddata)
1129 ddata = QQmlData::get(qobj, true);
1135 return method.asReturnedValue();
1136 }
1137
1138 if (!ddata || !ddata->propertyCache) {
1142 if (!property)
1143 return Encode::undefined();
1150 } else {
1157 }
1158 return result->asReturnedValue();
1159 }
1161
1162 if (!property) {
1163 // Check for attached properties
1164 if (name->startsWithUpper()) {
1166 return *importProperty;
1167 }
1169 }
1170
1171 if (property->isFunction()
1172 && !property->isVarProperty()
1173 && !property->isVMEFunction() // Handled by QObjectLookup
1174 && !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too
1175 QV4::Heap::QObjectMethod *method = nullptr;
1178 return lookup->getter(engine, *object);
1179 }
1180
1182
1184 return lookup->getter(engine, *object);
1185}
1186
1192
1194{
1197
1198 if (QObject *qObject = wrapper->object())
1199 return QMetaObject::metacall(qObject, call, index, a);
1200
1201 return 0;
1202}
1203
1206{
1207 if (!metaObject)
1208 return QLatin1String("null");
1209
1210 if (!object)
1211 return QString::fromUtf8(metaObject->className()) + QLatin1String("(0x0)");
1212
1213 const int id = metaObject->indexOfMethod("toString()");
1214 if (id >= 0) {
1220 return result.toString();
1223 return value->toQString();
1224 }
1225
1228 QLatin1String("(0x") + QString::number(quintptr(object), 16);
1230 if (!objectName.isEmpty())
1231 result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
1232 result += QLatin1Char(')');
1233 return result;
1234}
1235
1237{
1242
1246
1247 static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
1248 {
1249 switch (which) {
1250 case Destroy: {
1251 delete static_cast<QObjectSlotDispatcher*>(this_);
1252 }
1253 break;
1254 case Call: {
1255 if (QQmlData::wasDeleted(receiver))
1256 break;
1257
1258 QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
1259 ExecutionEngine *v4 = This->function.engine();
1260 // Might be that we're still connected to a signal that's emitted long
1261 // after the engine died. We don't track connections in a global list, so
1262 // we need this safeguard.
1263 if (!v4)
1264 break;
1265
1266 QQmlMetaObject::ArgTypeStorage<9> storage;
1267 QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr);
1268
1269 const qsizetype argCount = std::min(storage.size(), This->maxNumArguments);
1270
1271 Scope scope(v4);
1272 ScopedFunctionObject f(scope, This->function.value());
1273
1274 JSCallArguments jsCallData(scope, argCount);
1275 *jsCallData.thisObject = This->thisObject.isUndefined()
1276 ? v4->globalObject->asReturnedValue()
1277 : This->thisObject.value();
1278 for (qsizetype ii = 0; ii < argCount; ++ii) {
1279 QMetaType type = storage[ii];
1280 if (type == QMetaType::fromType<QVariant>()) {
1281 jsCallData.args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1282 } else {
1283 jsCallData.args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
1284 }
1285 }
1286
1287 f->call(jsCallData);
1288 if (scope.hasException()) {
1289 QQmlError error = v4->catchExceptionAsQmlError();
1290 if (error.description().isEmpty()) {
1291 ScopedString name(scope, f->name());
1292 error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString()));
1293 }
1294 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
1295 QQmlEnginePrivate::get(qmlEngine)->warning(error);
1296 } else {
1297 QMessageLogger(error.url().toString().toLatin1().constData(),
1298 error.line(), nullptr).warning().noquote()
1299 << error.toString();
1300 }
1301 }
1302 }
1303 break;
1304 case Compare: {
1305 QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
1306 if (connection->function.isUndefined()) {
1307 *ret = false;
1308 return;
1309 }
1310
1311 // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
1312 // for the new-style QObject::connect. Here we use the engine pointer as sentinel
1313 // to distinguish those type of QSlotObjectBase connections from our QML connections.
1314 ExecutionEngine *v4 = reinterpret_cast<ExecutionEngine*>(metaArgs[0]);
1315 if (v4 != connection->function.engine()) {
1316 *ret = false;
1317 return;
1318 }
1319
1320 Scope scope(v4);
1321 ScopedValue function(scope, *reinterpret_cast<Value*>(metaArgs[1]));
1322 ScopedValue thisObject(scope, *reinterpret_cast<Value*>(metaArgs[2]));
1323 QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
1324 int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
1325
1326 if (slotIndexToDisconnect != -1) {
1327 // This is a QObject function wrapper
1328 if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1329 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1330
1331 ScopedFunctionObject f(scope, connection->function.value());
1332 std::pair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f);
1333 if (connectedFunctionData.first == receiverToDisconnect &&
1334 connectedFunctionData.second == slotIndexToDisconnect) {
1335 *ret = true;
1336 return;
1337 }
1338 }
1339 } else {
1340 // This is a normal JS function
1341 if (RuntimeHelpers::strictEqual(*connection->function.valueRef(), function) &&
1342 connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1343 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1344 *ret = true;
1345 return;
1346 }
1347 }
1348
1349 *ret = false;
1350 }
1351 break;
1352 case NumOperations:
1353 break;
1354 }
1355 };
1356};
1357
1359{
1360 Scope scope(b);
1361
1362 if (argc == 0)
1363 THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
1364
1367 int signalIndex = signalInfo.second; // in method range, not signal range!
1368
1369 if (signalIndex < 0)
1370 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1371
1372 if (!signalObject)
1373 THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1374
1377 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1378
1381
1382 if (argc == 1) {
1383 f = argv[0];
1384 } else if (argc >= 2) {
1385 object = argv[0];
1386 f = argv[1];
1387 }
1388
1389 if (!f)
1390 THROW_GENERIC_ERROR("Function.prototype.connect: target is not a function");
1391
1392 if (!object->isUndefined() && !object->isObject())
1393 THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object");
1394
1397
1400
1404 }
1405 }
1406
1407 std::pair<QObject *, int> functionData = QObjectMethod::extractQtMethod(f); // align with disconnect
1408 QObject *receiver = nullptr;
1409
1410 if (functionData.first)
1412 else if (auto qobjectWrapper = object->as<QV4::QObjectWrapper>())
1414 else if (auto typeWrapper = object->as<QV4::QQmlTypeWrapper>())
1416
1417 if (receiver) {
1418 if (functionData.second == -1) {
1420 } else {
1421 // This means we are connecting to QObjectMethod which complains about extra arguments.
1422 Heap::QObjectMethod *d = static_cast<Heap::QObjectMethod *>(f->d());
1425 [](int a, const QQmlPropertyData &b) {
1426 return std::max(a, b.metaMethod().parameterCount());
1427 });
1428 }
1429
1431 } else {
1434 "Could not find receiver of the connection, using sender as receiver. Disconnect "
1435 "explicitly (or delete the sender) to make sure the connection is removed.");
1437 }
1438
1440}
1441
1443{
1444 Scope scope(b);
1445
1446 if (argc == 0)
1447 THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
1448
1452
1453 if (signalIndex == -1)
1454 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1455
1456 if (!signalObject)
1457 THROW_GENERIC_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1458
1460 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1461
1464
1465 if (argc == 1) {
1466 functionValue = argv[0];
1467 } else if (argc >= 2) {
1469 functionValue = argv[1];
1470 }
1471
1472 if (!functionValue)
1473 THROW_GENERIC_ERROR("Function.prototype.disconnect: target is not a function");
1474
1476 THROW_GENERIC_ERROR("Function.prototype.disconnect: target this is not an object");
1477
1479
1480 void *a[] = {
1481 scope.engine,
1486 };
1487
1488 QObject *receiver = nullptr;
1489
1490 if (functionData.first)
1494 else if (auto typeWrapper = functionThisValue->as<QV4::QQmlTypeWrapper>())
1496
1497 if (receiver) {
1499 reinterpret_cast<void **>(&a));
1500 } else {
1502 reinterpret_cast<void **>(&a));
1503 }
1504
1506}
1507
1508static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack)
1509{
1510 QQueue<QObject *> queue;
1511 queue.append(parent->children());
1512
1513 while (!queue.isEmpty()) {
1514 QObject *child = queue.dequeue();
1515 if (!child)
1516 continue;
1517 QObjectWrapper::markWrapper(child, markStack);
1518 queue.append(child->children());
1519 }
1520}
1521
1522// returns true if and only if an object was recorded
1524 QObject *o, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
1525 MemoryManager::ObjectsForCompilationUnit *recorded)
1526{
1527 Q_ASSERT(compilationUnit);
1528 Q_ASSERT(o);
1529 Q_ASSERT(recorded);
1530
1531 const auto &baseUnit = compilationUnit->baseCompilationUnit();
1532 Q_ASSERT(baseUnit);
1533
1534 for (const auto &unit : recorded->compilationUnits) {
1535 if (baseUnit == unit) {
1536 recorded->objects.push_back(o);
1537 return true;
1538 }
1539 }
1540
1541 return false;
1542}
1543
1544// returns true if and only if an object was recorded
1546 QObject *o, const QQmlVMEMetaObject *vme,
1547 MemoryManager::ObjectsForCompilationUnit *recorded)
1548{
1549 for (; vme; vme = vme->parentVMEMetaObject()) {
1550 if (recordObjectForCompilationUnits(o, vme->compilationUnit(), recorded))
1551 return true;
1552 }
1553
1554 return false;
1555}
1556
1558{
1560
1561 QObject *o = static_cast<QObjectWrapper *>(that)->object();
1562 if (!o)
1563 return;
1564
1566 // Children usually don't need to be marked, the gc keeps them alive.
1567 // But in the rare case of a "floating" QObject without a parent that
1568 // _gets_ marked (we've been called here!) then we also need to
1569 // propagate the marking down to the children recursively.
1570 if (!o->parent())
1572 });
1573
1575 if (!ddata)
1576 return;
1577
1578 if (ddata->hasVMEMetaObject) {
1579 if (auto *vme = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) {
1580 vme->mark(markStack);
1582 // ddata->compilationUnit can be different from the VME compilation units
1583 // if the "topmost" instantiation of the object is inaddressible.
1587 }
1588 }
1589 }
1590 } else if (const auto &cu = ddata->compilationUnit) {
1591 // Objects without a VMEMetaObject are identified via QQmlData::compilationUnit.
1594 }
1595
1597 return;
1598
1599 // mark the const wrapper if our engine has interacted with it at some point
1602 scope, scope.engine->m_multiplyWrappedQObjects->value(static_cast<const QObject *>(o)));
1603
1604 if (!constWrapper)
1605 return;
1606
1607 if (that != constWrapper->d()) {
1608 // We've got the non-const wrapper. Also mark the const one.
1610 return;
1611 }
1612
1613 // We've got the const wrapper. Also mark the non-const one
1616 else
1618}
1619
1621{
1622 Heap::QObjectWrapper *h = d();
1624
1625 if (QObject *o = h->object()) {
1626 QQmlData *ddata = QQmlData::get(o, false);
1627 if (ddata) {
1628 if (!o->parent() && !ddata->indestructible) {
1629 if (ddata && ddata->ownContext) {
1633 ddata->context = nullptr;
1634 }
1635
1636 // This object is notionally destroyed now. It might still live until the next
1637 // event loop iteration, but it won't need its connections, CU, or deferredData
1638 // anymore.
1639
1640 ddata->isQueuedForDeletion = true;
1643
1646
1647 if (lastCall)
1648 delete o;
1649 else
1650 o->deleteLater();
1651 } else {
1652 // If the object is C++-owned, we still have to release the weak reference we have
1653 // to it. If the "main" wrapper is not ours, we should leave it alone, though.
1654 if (ddata->jsWrapper.as<QObjectWrapper>() == this)
1656 }
1657 }
1658 }
1659
1660 h->destroy();
1661}
1662
1663
1665
1666template<typename Retrieve>
1667int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
1668 if (conversionMetaType == QMetaType::fromType<QVariant>())
1669 return 0;
1670
1671 const QMetaType type = retrieve();
1672 if (type == conversionMetaType)
1673 return 0;
1674
1675 if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
1676 if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject))
1677 return 1;
1678 }
1679
1680 if (QMetaType::canConvert(type, conversionMetaType)) {
1681 if (conversionMetaType == QMetaType::fromType<QJSValue>()
1682 || conversionMetaType == QMetaType::fromType<double>()
1683 || conversionMetaType == QMetaType::fromType<QString>()) {
1684 // Unspecific conversions receive lower score. You can convert anything
1685 // to QString or double via toString() and valueOf(), respectively.
1686 // And anything can be wrapped into QJSValue, but that's inefficient.
1687 return 6;
1688 }
1689
1690 // We have an explicitly defined conversion method to a non-boring type.
1691 return 5;
1692 }
1693
1694 return 10;
1695};
1696
1697/*
1698 Returns the match score for converting \a actual to be of type \a conversionType. A
1699 zero score means "perfect match" whereas a higher score is worse.
1700
1701 The conversion table is copied out of the \l QScript::callQtMethod() function.
1702*/
1703static int MatchScore(const Value &actual, QMetaType conversionMetaType)
1704{
1705 const int conversionType = conversionMetaType.id();
1706 const auto convertibleScore = [&](QMetaType actualType) {
1707 // There are a number of things we can do in JavaScript to subvert this, but
1708 // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
1709 if (!QMetaType::canConvert(actualType, conversionMetaType))
1710 return 10;
1711
1712 // You can convert anything to QJSValue, but that's inefficient.
1713 // If we have a better option, we should use it.
1714 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1715 return 9;
1716
1717 // You can also convert anything to QVariant, but that's also suboptimal.
1718 // You can convert anything to string or double via toString() and valueOf().
1719 // Those are also rather unspecific.
1720 switch (conversionType) {
1721 case QMetaType::QVariant:
1722 case QMetaType::Double:
1723 case QMetaType::QString:
1724 return 9;
1725 default:
1726 break;
1727 }
1728
1729 // We have an explicitly defined conversion method to a non-boring type.
1730 return 8;
1731 };
1732
1733 if (actual.isNumber()) {
1734 switch (conversionType) {
1735 case QMetaType::Double:
1736 return 0;
1737 case QMetaType::Float:
1738 return 1;
1739 case QMetaType::LongLong:
1740 case QMetaType::ULongLong:
1741 return 2;
1742 case QMetaType::Long:
1743 case QMetaType::ULong:
1744 return 3;
1745 case QMetaType::Int:
1746 case QMetaType::UInt:
1747 return 4;
1748 case QMetaType::Short:
1749 case QMetaType::UShort:
1750 return 5;
1751 break;
1752 case QMetaType::Char:
1753 case QMetaType::UChar:
1754 return 6;
1755 case QMetaType::QJsonValue:
1756 return 5;
1757 default:
1758 return convertibleScore(actual.isInteger()
1759 ? QMetaType::fromType<int>()
1760 : QMetaType::fromType<double>());
1761 }
1762 } else if (actual.isString()) {
1763 switch (conversionType) {
1764 case QMetaType::QString:
1765 return 0;
1766 case QMetaType::QJsonValue:
1767 return 5;
1768 case QMetaType::QUrl:
1769 return 6; // we like to convert strings to URLs in QML
1770 case QMetaType::Double:
1771 case QMetaType::Float:
1772 case QMetaType::LongLong:
1773 case QMetaType::ULongLong:
1774 case QMetaType::Int:
1775 case QMetaType::UInt:
1776 case QMetaType::Short:
1777 case QMetaType::UShort:
1778 case QMetaType::Char:
1779 case QMetaType::UChar:
1780 // QMetaType can natively convert strings to numbers.
1781 // However, in the general case it's of course extremely lossy.
1782 return 10;
1783 default:
1784 return convertibleScore(QMetaType::fromType<QString>());
1785 }
1786 } else if (actual.isBoolean()) {
1787 switch (conversionType) {
1788 case QMetaType::Bool:
1789 return 0;
1790 case QMetaType::QJsonValue:
1791 return 5;
1792 default:
1793 return convertibleScore(QMetaType::fromType<bool>());
1794 }
1795 } else if (actual.as<DateObject>()) {
1796 switch (conversionType) {
1797 case QMetaType::QDateTime:
1798 return 0;
1799 case QMetaType::QDate:
1800 return 1;
1801 case QMetaType::QTime:
1802 return 2;
1803 default:
1804 return convertibleScore(QMetaType::fromType<QDateTime>());
1805 }
1806 } else if (actual.as<RegExpObject>()) {
1807 switch (conversionType) {
1808#if QT_CONFIG(regularexpression)
1809 case QMetaType::QRegularExpression:
1810 return 0;
1811 default:
1812 return convertibleScore(QMetaType::fromType<QRegularExpression>());
1813#else
1814 default:
1815 return convertibleScore(QMetaType());
1816#endif
1817 }
1818 } else if (actual.as<ArrayBuffer>()) {
1819 switch (conversionType) {
1820 case QMetaType::QByteArray:
1821 return 0;
1822 default:
1823 return convertibleScore(QMetaType::fromType<QByteArray>());
1824 }
1825 } else if (actual.as<ArrayObject>()) {
1826 switch (conversionType) {
1827 case QMetaType::QJsonArray:
1828 return 3;
1829 case QMetaType::QStringList:
1830 case QMetaType::QVariantList:
1831 return 5;
1832 case QMetaType::QVector4D:
1833 case QMetaType::QMatrix4x4:
1834 return 6;
1835 case QMetaType::QVector3D:
1836 return 7;
1837 default:
1838 return convertibleScore(QMetaType());
1839 }
1840 } else if (actual.isNull()) {
1841 switch (conversionType) {
1842 case QMetaType::Nullptr:
1843 case QMetaType::VoidStar:
1844 case QMetaType::QObjectStar:
1845 case QMetaType::QJsonValue:
1846 return 0;
1847 default: {
1848 if (conversionMetaType.flags().testFlag(QMetaType::IsPointer))
1849 return 0;
1850 else
1851 return convertibleScore(QMetaType());
1852 }
1853 }
1854 } else if (const Object *obj = actual.as<Object>()) {
1855 if (const VariantObject *variantObject = obj->as<VariantObject>()) {
1856 return MatchVariant(conversionMetaType, [variantObject]() {
1857 return variantObject->d()->data().metaType();
1858 });
1859 }
1860
1861 if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
1862 switch (conversionType) {
1863 case QMetaType::QObjectStar:
1864 return 0;
1865 default:
1866 if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
1867 QObject *wrapped = wrapper->object();
1868 if (!wrapped)
1869 return 0;
1870 if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject()))
1871 return 0;
1872 }
1873 }
1874
1875 return convertibleScore(QMetaType::fromType<QObject *>());
1876 }
1877
1878 if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
1879 const QQmlType type = wrapper->d()->type();
1880 if (type.isSingleton()) {
1881 const QMetaType metaType = type.typeId();
1882 if (metaType == conversionMetaType)
1883 return 0;
1884
1885 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1886 && metaType.flags() & QMetaType::PointerToQObject
1887 && type.metaObject()->inherits(conversionMetaType.metaObject())) {
1888 return 0;
1889 }
1890 } else if (QObject *object = wrapper->object()) {
1891 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1892 && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) {
1893 return 0;
1894 }
1895 }
1896
1897 return convertibleScore(QMetaType());
1898 }
1899
1900 if (const Sequence *sequence = obj->as<Sequence>()) {
1901 const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(sequence);
1902 if (sequenceType == conversionMetaType)
1903 return 1;
1904
1905 return convertibleScore(sequenceType);
1906 }
1907
1908 if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
1909 return MatchVariant(conversionMetaType, [wrapper]() {
1910 return wrapper->d()->isVariant()
1911 ? wrapper->toVariant().metaType()
1912 : wrapper->type();
1913 });
1914 }
1915
1916 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1917 return 0;
1918
1919 switch (conversionType) {
1920 case QMetaType::QJsonObject:
1921 case QMetaType::QVariantMap:
1922 return 5;
1923 default:
1924 break;
1925 }
1926
1927 }
1928
1929 return convertibleScore(QMetaType());
1930}
1931
1932static int numDefinedArguments(CallData *callArgs)
1933{
1934 int numDefinedArguments = callArgs->argc();
1935 while (numDefinedArguments > 0
1936 && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) {
1937 --numDefinedArguments;
1938 }
1939 return numDefinedArguments;
1940}
1941
1942static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
1943{
1944 const QMetaObject *metaObject = object.metaObject();
1945 const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
1946 return indexOfClassInfo != -1
1947 && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
1948}
1949
1950static ReturnedValue doConvertReturnValue(ExecutionEngine *engine, QMetaType type, const void *data)
1951{
1952 if (type.flags() & QMetaType::PointerToQObject) {
1953 // We consider QObjects returned from invokables as owned by the QML engine unless
1954 // explicitly configured otherwise.
1955 if (QObject *object = *static_cast<QObject *const *>(data))
1956 QQmlData::get(object, true)->setImplicitDestructible();
1957 }
1958 return engine->fromData(type, data);
1959}
1960
1961static ReturnedValue doConvertReturnValue(ExecutionEngine *engine, const QVariant &value)
1962{
1963 // QVariant is treated as transparent when returned. That means we still adopt
1964 // QObjects if they're wrapped in QVariant. See above.
1965 return doConvertReturnValue(engine, value.metaType(), value.constData());
1966}
1967
1971{
1972 const auto constructReturnValue = [&](QMetaType returnType, void *returnValue) {
1973 // If we construct a value in place, we mustn't initialize the memory before that.
1976 };
1977
1978 const auto convertReturnValue
1980
1981 // In contrast to other invocations of convertAndCall() we do not want to create
1982 // a URL object from a QUrl here like metaTypeFromJS does. This is for compatibility.
1983 // URL objects are proper, specified, objects that behave different from our variant
1984 // objects when it comes to equality comparisons.
1985
1987 ? doConvertReturnValue(engine, *reinterpret_cast<const QVariant *>(returnValue))
1989
1990 // NB: A type can be both a PointerToQObject and NeedsDestruction if the user registers
1991 // such a thing explicitly. We respect that.
1994 return result;
1995 };
1996
1997 const auto conversionErrorHandler = [&](QMetaType argumentType, void *argument, int ii) {
1998 // We've checked the number of arguments before
1999 Q_ASSERT(ii < callArgs->argc());
2000
2002 // We have an engine here. So we can conjure up a QJSManagedValue for anything.
2003 *static_cast<QJSManagedValue *>(argument)
2005 return true;
2006 }
2007
2009 && callArgs->args[ii].isUndefined()) {
2010 // TODO: QObjectMethod silently converts undefined to null when you pass undefined
2011 // to a method that accepts some QObject-derived. This is wrong because
2012 // undefined is certainly not null. We accept it for compatibility.
2013 *static_cast<QObject **>(argument) = nullptr;
2014 return true;
2015 }
2016
2017 // TODO: QObjectMethod is allowed to use QVariant conversion.
2018 // This is wrong because we can't see the converters at compile time.
2019 // It only exists for compatibility with old versions of Qt.
2023 return true;
2024
2025 qWarning() << QString::fromLatin1("Could not convert argument %1 from %2 to %3")
2026 .arg(ii)
2028 .arg(argumentType.name());
2029 const StackTrace stack = engine->stackTrace();
2030 for (const StackFrame &frame : stack) {
2031 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source + (frame.line > 0
2032 ? (QLatin1Char(':') + QString::number(frame.line))
2033 : QString());
2034
2035 }
2036
2038 qWarning() << "Passing incompatible arguments to signals is not supported.";
2040 }
2041
2043 "Passing incompatible arguments to C++ functions from JavaScript is not allowed."));
2044 return false;
2045 };
2046
2048
2049 const auto handleTooManyArguments = [&](int expectedArguments) {
2051 engine->throwError(QStringLiteral("Too many arguments"));
2052 return false;
2053 }
2054
2055 const auto stackTrace = engine->stackTrace();
2056 if (stackTrace.isEmpty()) {
2058 << "When matching arguments for "
2059 << object.className() << "::" << data.name(object.metaObject()) << "():";
2060 } else {
2061 const StackFrame frame = stackTrace.first();
2063 + (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
2064 : QString());
2065 }
2066
2067 qWarning().noquote() << QStringLiteral("Too many arguments, ignoring %1")
2069 return true;
2070 };
2071
2073
2074 if (data.hasArguments()) {
2076
2077 const bool ok = data.isConstructor()
2082 if (!ok)
2084
2085 const int expectedArgumentCount = storage.size() - 1;
2087 return engine->throwError(QLatin1String("Insufficient arguments"));
2088
2091 return Encode::undefined();
2092 }
2093
2094 return QV4::convertAndCall(
2097 [&](void **argData, const QMetaType *types, int argc) {
2098 Q_UNUSED(types);
2099 Q_UNUSED(argc);
2102 }
2103
2105 return Encode::undefined();
2106
2108 if (!returnType.isValid())
2110
2111 return QV4::convertAndCall(
2112 engine, &returnType, 1, nullptr, 0,
2113 [&](void **argData, const QMetaType *types, int argc) {
2114 Q_UNUSED(types);
2115 Q_UNUSED(argc);
2118}
2119
2120/*
2121Resolve the overloaded method to call. The algorithm works conceptually like this:
2122 1. Resolve the set of overloads it is *possible* to call.
2123 Impossible overloads include those that have too many parameters or have parameters
2124 of unknown type.
2125 2. Filter the set of overloads to only contain those with the closest number of
2126 parameters.
2127 For example, if we are called with 3 parameters and there are 2 overloads that
2128 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
2129 3. Find the best remaining overload based on its match score.
2130 If two or more overloads have the same match score, return the last one. The match
2131 score is constructed by adding the matchScore() result for each of the parameters.
2132*/
2136{
2137 const int argumentCount = callArgs->argc();
2139
2140 const QQmlPropertyData *best = nullptr;
2144
2147
2148 for (int i = 0; i < methodCount; ++i) {
2149 const QQmlPropertyData *attempt = methods + i;
2150
2156 qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
2157 }
2158
2159 // QQmlV4Function overrides anything that doesn't provide the exact number of arguments
2160 int methodParameterScore = 1;
2161 // QQmlV4Function overrides the "no idea" option, which is 10
2162 int maxMethodMatchScore = 9;
2163 // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments
2165
2166 if (!attempt->isV4Function()) {
2168 int methodArgumentCount = 0;
2169 if (attempt->hasArguments()) {
2170 if (attempt->isConstructor()) {
2171 if (!object.constructorParameterTypes(*attempt, &storage, nullptr)) {
2172 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2173 continue;
2174 }
2175 } else {
2176 if (!object.methodParameterTypes(*attempt, &storage, nullptr)) {
2177 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2178 continue;
2179 }
2180 }
2182 }
2183
2185 qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
2186 continue; // We don't have sufficient arguments to call this method
2187 }
2188
2190 ? 0
2193 qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
2194 continue; // We already have a better option
2195 }
2196
2199 for (int ii = 0; ii < methodArgumentCount; ++ii) {
2200 const int score = MatchScore((v = Value::fromStaticValue(callArgs->args[ii])),
2201 storage[ii]);
2204 }
2205 }
2206
2211 best = attempt;
2215 qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
2216 << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
2217 << "bestSumMatchScore" << bestSumMatchScore << "\n";
2218 } else {
2219 qCDebug(lcOverloadResolution) << "did not update best\n"
2220 << "bestParameterScore" << bestParameterScore << "\t"
2221 << "methodParameterScore" << methodParameterScore << "\n"
2222 << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
2223 << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
2224 << "bestSumMatchScore" << bestSumMatchScore << "\t"
2225 << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
2226 }
2227
2228 if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
2229 qCDebug(lcOverloadResolution, "perfect match");
2230 break; // We can't get better than that
2231 }
2232
2233 };
2234
2235 if (best && best->isValid()) {
2236 return best;
2237 } else {
2238 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
2239 for (int i = 0; i < methodCount; ++i) {
2244 error += u"\n " + QString::fromUtf8(m.methodSignature());
2245 }
2246
2248 return nullptr;
2249 }
2250}
2251
2252static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
2253{
2254 if (required == QMetaType::fromType<QVariant>()
2255 || required == QMetaType::fromType<QJSValue>()
2256 || required == QMetaType::fromType<QJSManagedValue>()) {
2257 return true;
2258 }
2259
2260 if (data) {
2261 if (passed == QMetaType::fromType<QVariant>())
2262 passed = static_cast<const QVariant *>(data)->metaType();
2263 else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
2264 passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
2265 }
2266
2267 if (passed == required)
2268 return true;
2269
2270 if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
2271 switch (passed.id()) {
2272 case QMetaType::UnknownType:
2273 case QMetaType::Nullptr:
2274 case QMetaType::Bool:
2275 case QMetaType::Int:
2276 case QMetaType::Double:
2277 case QMetaType::QString:
2278 return true;
2279 default:
2280 break;
2281 }
2282 }
2283
2284 return false;
2285}
2286
2288 const QMetaMethod &method, void **argv, int argc, const QMetaType *types)
2289{
2290 if (types[0].isValid() && !ExactMatch(method.returnMetaType(), types[0], nullptr))
2291 return false;
2292
2293 if (method.parameterCount() != argc)
2294 return false;
2295
2296 for (int i = 0; i < argc; ++i) {
2297 if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1]))
2298 return false;
2299 }
2300
2301 return true;
2302}
2303
2306 void **argv, int argc, const QMetaType *types)
2307{
2308 // We only accept exact matches here. Everything else goes through the JavaScript conversion.
2309 for (int i = 0; i < methodCount; ++i) {
2310 const QQmlPropertyData *attempt = methods + i;
2312 return attempt;
2313 }
2314
2315 return nullptr;
2316}
2317
2326
2336
2340{
2342
2344 if (cloneFrom->wrapper) {
2346 if (ref) {
2348 } else {
2349 // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what
2350 // value we should operate on. Without knowledge of the property the value
2351 // was read from, we cannot load the value from the given object.
2352 return Encode::undefined();
2353 }
2354 }
2355
2357 valueScope,
2360
2362
2363 Q_ASSERT(method->d()->methods == nullptr);
2364 switch (cloneFrom->methodCount) {
2365 case 0:
2366 Q_ASSERT(cloneFrom->methods == nullptr);
2367 break;
2368 case 1:
2370 == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
2371 method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
2372 *method->d()->methods = *cloneFrom->methods;
2373 break;
2374 default:
2375 Q_ASSERT(cloneFrom->methods != nullptr);
2379 break;
2380 }
2381
2382 return method.asReturnedValue();
2383}
2384
2386{
2390}
2391
2393{
2395
2397 return valueWrapper->metaObject();
2398 if (QObject *self = object())
2399 return self->metaObject();
2400
2401 return nullptr;
2402}
2403
2405{
2407
2409 return objectWrapper->object();
2411 return typeWrapper->object();
2412 return nullptr;
2413}
2414
2415bool Heap::QObjectMethod::isDetached() const
2416{
2417 if (!wrapper)
2418 return true;
2419
2422 return valueWrapper->d()->object() == nullptr;
2423
2424 return false;
2425}
2426
2428{
2431 return objectWrapper->object() == o;
2433 return typeWrapper->object() == o;
2434
2438 return qobject->object() == o;
2440 return type->object() == o;
2441
2442 // Attached to some nested value type or sequence object
2443 return false;
2444 }
2445
2446 return false;
2447}
2448
2450 const QMetaObject *thisMeta) const
2451{
2452 // Check that the metaobject matches.
2453
2454 if (!thisMeta) {
2455 // You can only get a detached method via a lookup, and then you have a thisObject.
2457 return Included;
2458 }
2459
2460 const auto check = [&](const QMetaObject *included) {
2465 "%s:%d: Calling C++ methods with 'this' objects different from the one "
2466 "they were retrieved from is broken, due to historical reasons. The "
2467 "original object is used as 'this' object. You can allow the given "
2468 "'this' object to be used by setting "
2469 "'pragma NativeMethodBehavior: AcceptThisObject'",
2471 return Included;
2472 }
2473
2474 // destroy() and toString() can be called on all QObjects, but not on gadgets.
2475 if (index < 0)
2477
2478 // Find the base type the method belongs to.
2480 while (true) {
2481 if (included == thisMeta)
2482 return Explicit;
2483
2484 if (methodOffset <= index)
2486
2490 };
2491
2493 };
2494
2495 if (const QMetaObject *meta = metaObject())
2496 return check(meta);
2497
2498 // If the QObjectMethod is detached, we can only have gotten here via a lookup.
2499 // The lookup checks that the QQmlPropertyCache matches.
2500 return Explicit;
2501}
2502
2504{
2506 return QStringLiteral("destroy");
2507 else if (index == QV4::QObjectMethod::ToStringMethod)
2508 return QStringLiteral("toString");
2509
2510 const QMetaObject *mo = metaObject();
2511 if (!mo)
2512 return QString();
2513
2514 int methodOffset = mo->methodOffset();
2515 while (methodOffset > index) {
2516 mo = mo->superClass();
2518 }
2519
2520 return "%1::%2"_L1.arg(QLatin1StringView{mo->className()},
2522}
2523
2525{
2526 if (methods) {
2527 Q_ASSERT(methodCount > 0);
2528 return;
2529 }
2530
2531 const QMetaObject *mo = metaObject();
2532
2533 if (!mo)
2534 mo = thisMeta;
2535
2536 Q_ASSERT(mo);
2537
2538 int methodOffset = mo->methodOffset();
2539 while (methodOffset > index) {
2540 mo = mo->superClass();
2542 }
2546 dummy.load(method);
2549 // Look for overloaded methods
2551 for (int ii = index - 1; ii >= methodOffset; --ii) {
2552 if (methodName == mo->method(ii).name()) {
2553 method = mo->method(ii);
2554 dummy.load(method);
2556 }
2557 }
2558 if (resolvedMethods.size() > 1) {
2562 } else {
2563 methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod);
2565 methodCount = 1;
2566 }
2567
2568 Q_ASSERT(methodCount > 0);
2569}
2570
2577
2579 ExecutionEngine *engine, QObject *o, const Value *args, int argc) const
2580{
2581 method_destroy(engine, o, argc > 0 ? args[0].toInt32() : 0);
2582 return Encode::undefined();
2583}
2584
2586{
2587 if (!o)
2588 return true;
2589
2591 engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2592 return false;
2593 }
2594
2595 if (delay > 0)
2597 else
2598 o->deleteLater();
2599
2600 return true;
2601}
2602
2604 const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2605{
2606 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2608}
2609
2611 const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
2612{
2613 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2615}
2616
2618{
2620
2621 const QMetaObject *thisMeta = nullptr;
2622
2623 QObject *o = nullptr;
2625 if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
2626 thisMeta = w->metaObject();
2627 o = w->object();
2628 } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
2629 thisMeta = w->metaObject();
2630 o = w->object();
2631 } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
2632 thisMeta = w->metaObject();
2633 valueWrapper = w->d();
2634 }
2635
2637 if (o && o == d()->object()) {
2639 // Nothing to do; objects are the same. This should be common
2640 } else if (valueWrapper && valueWrapper == d()->wrapper) {
2642 // Nothing to do; gadgets are the same. This should be somewhat common
2643 } else {
2645 if (mode == Heap::QObjectMethod::Invalid) {
2646 v4->throwError(QLatin1String("Cannot call method %1 on %2").arg(
2647 d()->name(), thisObject->toQStringNoThrow()));
2648 return Encode::undefined();
2649 }
2650 }
2651
2652 QQmlObjectOrGadget object = [&](){
2653 if (mode == Heap::QObjectMethod::Included) {
2654 QV4::Scope scope(v4);
2658 return QQmlObjectOrGadget(type->object());
2660 valueWrapper = value->d();
2662 }
2663 Q_UNREACHABLE();
2664 } else {
2665 if (o)
2666 return QQmlObjectOrGadget(o);
2667
2672 }
2673 }();
2674
2675 if (object.isNull())
2676 return Encode::undefined();
2677
2678 if (d()->index == DestroyMethod)
2679 return method_destroy(v4, object.qObject(), argv, argc);
2680 else if (d()->index == ToStringMethod) {
2682 const QMetaObject *meta = object.metaObject();
2683 if (meta && meta->indexOfMethod("toString()") >= 0)
2685 }
2686 return method_toString(v4, object.qObject());
2687 }
2688
2690
2691 Scope scope(v4);
2694
2695 const QQmlPropertyData *method = d()->methods;
2696
2697 // If we call the method, we have to write back any value type references afterwards.
2698 // The method might change the value.
2699 const auto doCall = [&](const auto &call) {
2700 if (!method->isConstant()) {
2704 return rv->asReturnedValue();
2705 }
2706 }
2707
2708 return call();
2709 };
2710
2711 if (d()->methodCount != 1) {
2712 Q_ASSERT(d()->methodCount > 0);
2714 if (method == nullptr)
2715 return Encode::undefined();
2716 }
2717
2720
2721 if (method->isV4Function()) {
2722 return doCall([&]() {
2726
2727 void *args[] = { nullptr, &funcptr };
2729
2730 return rv->asReturnedValue();
2731 });
2732 }
2733
2734 return doCall([&]() { return callPrecise(object, *method, v4, callData); });
2735}
2736
2738{
2739 constexpr int parameterCount() const { return 0; }
2740 constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
2741 constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
2742};
2743
2745 QObject *thisObject, void **argv, const QMetaType *types, int argc) const
2746{
2748
2749 const QMetaObject *thisMeta = nullptr;
2751
2752 if (thisObject) {
2754 } else {
2758 }
2759
2760 QQmlObjectOrGadget object = [&](){
2761 if (thisObject)
2763
2764 Scope scope(v4);
2767
2772 }();
2773
2774 if (object.isNull())
2775 return;
2776
2777 if (d()->index == DestroyMethod) {
2778 // method_destroy will use at most one argument
2780 v4, thisObject, argv, types, std::min(argc, 1),
2781 [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
2783 return method_destroy(v4, object.qObject(), argv, argc);
2784 });
2785 return;
2786 }
2787
2788 if (d()->index == ToStringMethod) {
2792 [v4, thisMeta, object](void **argv, int) {
2793 if (!argv[0])
2794 return;
2795 *static_cast<QString *>(argv[0])
2797 });
2798 return;
2799 }
2800
2802
2803 const QQmlPropertyData *method = d()->methods;
2804 if (d()->methodCount != 1) {
2805 Q_ASSERT(d()->methodCount > 0);
2807 }
2808
2809 if (!method || method->isV4Function()) {
2812 [this](const Value *thisObject, const Value *argv, int argc) {
2813 return callInternal(thisObject, argv, argc);
2814 });
2815 } else {
2819 [v4, object, valueWrapper, method](void **argv, int argc) {
2820 Q_UNUSED(argc);
2821
2822 // If we call the method, we have to write back any value type references afterwards.
2823 // The method might change the value.
2825 if (!method->isConstant()) {
2828 }
2829
2830 // If the method returns a QObject* we need to track it on the JS heap
2831 // (if it's destructible).
2832 QObject *qobjectPtr = nullptr;
2834 if (argv[0]) {
2836 qobjectPtr = *static_cast<QObject **>(argv[0]);
2837 } else if (resultType == QMetaType::fromType<QVariant>()) {
2838 const QVariant *result = static_cast<const QVariant *>(argv[0]);
2841 qobjectPtr = *static_cast<QObject *const *>(result->data());
2842 }
2843 }
2844
2845 if (qobjectPtr) {
2848 ddata->indestructible = false;
2850 }
2851 }
2852 });
2853 }
2854}
2855
2857
2858void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
2859{
2860 Object::init();
2861 this->signalIndex = signalIndex;
2862 setObject(object);
2863}
2864
2866
2868{
2872 << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
2873 "not call it directly. Make it a proper function and call "
2874 "that or emit the signal.")
2876
2877 Scope scope(engine());
2880 scope.engine,
2881 static_cast<Heap::QObjectWrapper *>(nullptr),
2882 signalIndex()));
2883
2884 return method->call(thisObject, argv, argc);
2885}
2886
2901
2902
2905{
2906 const QObjectBiPointer key = it.key();
2907 const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
2908 disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
2909 return QHash<QObjectBiPointer, WeakValue>::erase(it);
2910}
2911
2912void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
2913{
2914 QHash<QObjectBiPointer, WeakValue>::remove(object);
2915 QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object));
2916}
2917
2918} // namespace QV4
2919
2920QT_END_NAMESPACE
2921
2922#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