Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4qobjectwrapper.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6
7#include <private/qjsvalue_p.h>
8#include <private/qjsmanagedvalue_p.h>
9
10#include <private/qqmlbinding_p.h>
11#include <private/qqmlbuiltinfunctions_p.h>
12#include <private/qqmlengine_p.h>
13#include <private/qqmlobjectorgadget_p.h>
14#include <private/qqmlpropertybinding_p.h>
15#include <private/qqmlscriptstring_p.h>
16#include <private/qqmlsignalnames_p.h>
17#include <private/qqmltypewrapper_p.h>
18#include <private/qqmlvaluetypewrapper_p.h>
19#include <private/qqmlvmemetaobject_p.h>
20
21#include <private/qv4arraybuffer_p.h>
22#include <private/qv4arrayobject_p.h>
23#include <private/qv4compileddata_p.h>
24#include <private/qv4dateobject_p.h>
25#include <private/qv4functionobject_p.h>
26#include <private/qv4identifiertable_p.h>
27#include <private/qv4jscall_p.h>
28#include <private/qv4jsonobject_p.h>
29#include <private/qv4lookup_p.h>
30#include <private/qv4mm_p.h>
31#include <private/qv4regexpobject_p.h>
32#include <private/qv4runtime_p.h>
33#include <private/qv4scopedvalue_p.h>
34#include <private/qv4sequenceobject_p.h>
35#include <private/qv4variantobject_p.h>
36
37#include <QtCore/qjsonarray.h>
38#include <QtCore/qjsonobject.h>
39#include <QtCore/qjsonvalue.h>
40#include <QtCore/qloggingcategory.h>
41#include <QtCore/qmetaobject.h>
42#include <QtCore/qqueue.h>
43#include <QtCore/qtimer.h>
44#include <QtCore/qtypes.h>
45#include <QtCore/qvarlengtharray.h>
46
47#include <vector>
48
49#if QT_CONFIG(qml_itemmodel)
50#include <QtCore/qabstractitemmodel.h>
51#endif
52
54
55Q_LOGGING_CATEGORY(lcBuiltinsBindingRemoval, "qt.qml.binding.removal", QtWarningMsg)
56Q_STATIC_LOGGING_CATEGORY(lcObjectConnect, "qt.qml.object.connect", QtWarningMsg)
57Q_STATIC_LOGGING_CATEGORY(lcOverloadResolution, "qt.qml.overloadresolution", QtWarningMsg)
58Q_STATIC_LOGGING_CATEGORY(lcMethodBehavior, "qt.qml.method.behavior")
59Q_STATIC_LOGGING_CATEGORY(lcSignalHandler, "qt.qml.signalhandler")
60
61// The code in this file does not violate strict aliasing, but GCC thinks it does
62// so turn off the warnings for us to have a clean build
63QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
64
65using namespace Qt::StringLiterals;
66
67namespace QV4 {
68
70{
72 if (v4) {
73 Scope scope(v4);
75 if (method)
77 }
78
79 return std::make_pair((QObject *)nullptr, -1);
80}
81
82static std::pair<QObject *, int> extractQtSignal(const Value &value)
83{
84 if (value.isObject()) {
85 ExecutionEngine *v4 = value.as<Object>()->engine();
86 Scope scope(v4);
87 ScopedFunctionObject function(scope, value);
88 if (function)
89 return QObjectMethod::extractQtMethod(function);
90
91 Scoped<QmlSignalHandler> handler(scope, value);
92 if (handler)
93 return std::make_pair(handler->object(), handler->signalIndex());
94 }
95
96 return std::make_pair((QObject *)nullptr, -1);
97}
98
100 ExecutionEngine *v4,
101 const QQmlPropertyData &property)
102{
103 Heap::ReferenceObject::Flags flags = Heap::ReferenceObject::NoFlag;
104 if (CppStackFrame *stackFrame = v4->currentStackFrame) {
105 if (stackFrame->v4Function->executableCompilationUnit()->valueTypesAreCopied())
106 flags |= Heap::ReferenceObject::EnforcesLocation;
107 }
108
109 if (property.isWritable())
110 flags |= Heap::ReferenceObject::CanWriteBack;
111
112 return flags;
113}
114
118{
120 Scope scope(v4);
121
123 if (property.isQObject()) {
124 QObject *rv = nullptr;
127 return QObjectWrapper::wrapConst(v4, rv);
128 else
129 return QObjectWrapper::wrap(v4, rv);
130 }
131
134
135 const auto encodeSimple = [&](auto v) {
137 return Encode(v);
138 };
139
140 const auto encodeInt = [&](auto v) {
142 return Encode(int(v));
143 };
144
145 const auto encodeDouble = [&](auto v) {
147 return Encode(double(v));
148 };
149
150 const auto encodeDate = [&](auto v) {
152 return Encode(v4->newDateObject(
154 };
155
156 const auto encodeString = [&](auto v) {
158 return v4->newString(v)->asReturnedValue();
159 };
160
161 const auto encodeSequence = [&](QMetaSequence metaSequence) {
162 // Pass nullptr as data. It's lazy-loaded.
164 v4, propMetaType, metaSequence, nullptr,
166 };
167
168
171 case QMetaType::Void:
172 return Encode::undefined();
173 case QMetaType::Nullptr:
174 case QMetaType::VoidStar:
175 return Encode::null();
176 case QMetaType::Int:
177 return encodeSimple(int());
178 case QMetaType::Bool:
179 return encodeSimple(bool());
180 case QMetaType::QString:
181 return encodeString(QString());
182 case QMetaType::QByteArray: {
186 }
187 case QMetaType::QChar:
188 return encodeString(QChar());
189 case QMetaType::Char16:
190 return encodeString(char16_t());
191 case QMetaType::UInt:
192 return encodeSimple(uint());
193 case QMetaType::Float:
194 return encodeSimple(float());
195 case QMetaType::Double:
196 return encodeSimple(double());
197 case QMetaType::Short:
198 return encodeInt(short());
199 case QMetaType::UShort:
200 return encodeInt(ushort());
201 case QMetaType::Char:
202 return encodeInt(char());
203 case QMetaType::UChar:
204 return encodeInt(uchar());
205 case QMetaType::SChar:
206 return encodeInt(qint8());
207 case QMetaType::Long:
208 return encodeDouble(long());
209 case QMetaType::ULong:
210 return encodeDouble(ulong());
211 case QMetaType::LongLong:
212 return encodeDouble(qlonglong());
213 case QMetaType::ULongLong:
214 return encodeDouble(qulonglong());
215 case QMetaType::QDateTime:
216 return encodeDate(QDateTime());
217 case QMetaType::QDate:
218 return encodeDate(QDate());
219 case QMetaType::QTime:
220 return encodeDate(QTime());
221#if QT_CONFIG(regularexpression)
225 return Encode(v4->newRegExpObject(v));
226 }
227#endif
228 case QMetaType::QVariantMap: {
231 return scope.engine->fromData(
233 }
234 case QMetaType::QVariantHash: {
237 return scope.engine->fromData(
239 }
240 case QMetaType::QJsonValue: {
243 return QV4::JsonObject::fromJsonValue(v4, v);
244 }
245 case QMetaType::QJsonObject: {
248 return QV4::JsonObject::fromJsonObject(v4, v);
249 }
250 case QMetaType::QJsonArray:
256 case QMetaType::QUrl: {
257 // ### Qt7: We really want this to be a JS URL object, but that would break things.
258 QUrl v;
261 }
262 case QMetaType::QPixmap:
263 case QMetaType::QImage: {
264 // Scarce value types
268 }
269 default:
270 break;
271 }
272
274 QJSValue v;
277 }
278
279 if (property.isQVariant()) {
280 // We have to read the property even if it's a lazy-loaded reference object.
281 // Without reading it, we wouldn't know its inner type.
282 QVariant v;
284 return scope.engine->fromVariant(
287 }
288
289 if (!propMetaType.isValid()) {
291 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
292 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
293 return Encode::undefined();
294 }
295
296 // TODO: For historical reasons we don't enforce locations for reference objects here.
297 // Once we do, we can eager load and use the fromVariant() below.
298 // Then the extra checks for value types and sequences can be dropped.
299
303 // Lazy loaded value type reference. Pass nullptr as data.
307 }
308 }
309
310 // See if it's a sequence type.
314
317 return scope.engine->fromVariant(
319}
320
326
333
348
352{
354
356 if (property->isVMEFunction()) {
359 return vmemo->vmeMethod(property->coreIndex());
360 } else if (property->isV4Function()) {
361 return QObjectMethod::create(
362 engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
363 } else if (property->isSignalHandler()) {
367 } else {
368 return QObjectMethod::create(
369 engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
370 }
371 }
372
374
375 if (ep && ep->propertyCapture && !property->isConstant()) {
380 }
381 }
382
383 if (property->isVarProperty()) {
387 } else {
389 }
390}
391
393 ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
394{
395 int index = 0;
396 if (name->equals(v4->id_destroy()))
398 else if (name->equals(v4->id_toString()))
400 else
401 return OptionalReturnedValue();
402
403 if (hasProperty)
404 *hasProperty = true;
406}
407
409 ExecutionEngine *v4, String *name, const QQmlRefPointer<QQmlContextData> &qmlContext,
410 QObject *qobj, bool *hasProperty = nullptr)
411{
412 if (!qmlContext || !qmlContext->imports())
413 return OptionalReturnedValue();
414
415 if (hasProperty)
416 *hasProperty = true;
417
418 if (QQmlTypeLoader *typeLoader = v4->typeLoader()) {
419 QQmlTypeNameCache::Result r = qmlContext->imports()->query(name, typeLoader);
420
421 if (!r.isValid())
422 return OptionalReturnedValue();
423
424 if (r.scriptIndex != -1) {
425 return OptionalReturnedValue(Encode::undefined());
426 } else if (r.type.isValid()) {
427 return OptionalReturnedValue(
428 QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
429 } else if (r.importNamespace) {
430 return OptionalReturnedValue(QQmlTypeWrapper::create(
431 v4, qobj, qmlContext->imports(), r.importNamespace,
432 Heap::QQmlTypeWrapper::ExcludeEnums));
433 }
434 Q_UNREACHABLE_RETURN(OptionalReturnedValue());
435 } else {
436 return OptionalReturnedValue();
437 }
438}
439
443{
444 // Keep this code in sync with ::virtualResolveLookupGetter
445
446 if (QQmlData::wasDeleted(d()->object())) {
447 if (hasProperty)
448 *hasProperty = false;
449 return Encode::undefined();
450 }
451
453
455 return *methodValue;
456
459
460 if (!result) {
461 // Check for attached properties
465 return *importProperty;
466 }
467 return Object::virtualGet(this, name->propertyKey(), this, hasProperty);
468 }
469
470 QQmlData *ddata = QQmlData::get(d()->object(), false);
471
472 if ((flags & CheckRevision) && result->hasRevision()) {
474 if (hasProperty)
475 *hasProperty = false;
476 return Encode::undefined();
477 }
478 }
479
480 if (hasProperty)
481 *hasProperty = true;
482
483 return getProperty(v4, d(), d()->object(), result, flags);
484}
485
490{
491 if (QQmlData::wasDeleted(object)) {
492 if (hasProperty)
493 *hasProperty = false;
494 return Encode::null();
495 }
496
498 return *methodValue;
499
500 QQmlData *ddata = QQmlData::get(object, false);
503
504 if (result) {
508 if (hasProperty)
509 *hasProperty = false;
510 return Encode::undefined();
511 }
512 }
513
514 if (hasProperty)
515 *hasProperty = true;
516
517 if (property && result != &local)
518 *property = result;
519
521 } else {
522 // Check if this object is already wrapped.
523 if (!ddata || (ddata->jsWrapper.isUndefined() &&
524 (ddata->jsEngineId == 0 || // Nobody owns the QObject
525 !ddata->hasTaintedV4Object))) { // Someone else has used the QObject, but it isn't tainted
526
527 // Not wrapped. Last chance: try query QObjectWrapper's prototype.
528 // If it can't handle this, then there is no point
529 // to wrap the QObject just to look at an empty set of JS props.
531 return proto->get(name, hasProperty);
532 }
533 }
534
535 // If we get here, we must already be wrapped (which implies a ddata).
536 // There's no point wrapping again, as there wouldn't be any new props.
538
541 if (!rewrapped) {
542 if (hasProperty)
543 *hasProperty = false;
544 return Encode::null();
545 }
547}
548
549
571
572/*!
573 \internal
574 If an QObjectWrapper is created via wrap, then it needs to be stored somewhere.
575 Otherwise, the garbage collector will immediately collect it if it is already
576 past the "mark QObjectWrapper's" phase (note that QObjectWrapper are marked
577 by iterating over a list of all QObjectWrapper, and then checking if the
578 wrapper fulfills some conditions).
579 However, sometimes we don't really want to keep a reference to the wrapper,
580 but just want to make sure that it exists (and we know that the wrapper
581 already fulfills the conditions to be kept alive). Then ensureWrapper
582 can be used, which creates the wrapper and ensures that it is also
583 marked.
584 */
593
596 const QQmlPropertyData *property, const Value &value)
597{
598 if (!property->isWritable() && !property->isQList()) {
599 QString error = QLatin1String("Cannot assign to read-only property \"") +
602 return;
603 }
604
607 if (f->as<QQmlTypeWrapper>()) {
608 // Ignore. It's probably a singleton or an attached type.
609 } else if (!f->isBinding()) {
610 const bool isAliasToAllowed = [&]() {
611 if (property->isAlias()) {
623 } else {
624 return false;
625 }
626 }();
629 // assigning a JS function to a non var or QJSValue property or is not allowed.
630 QString error = QLatin1String("Cannot assign JavaScript function to ");
631 if (!QMetaType(property->propType()).name())
632 error += QLatin1String("[unknown property type]");
633 else
636 return;
637 }
638 } else {
639
644
645 // binding assignment.
646 if (property->acceptsQBinding()) {
647 const QQmlPropertyIndex idx(property->coreIndex(), /*not a value type*/-1);
650 if (f->isBoundFunction()) {
651 auto boundFunction = static_cast<BoundFunction *>(f.getPointer());
654 } else {
657
658 }
660 void *argv = {&bindable};
661 // indirect metacall in case interceptors are installed
664 if (!ok) {
665 auto error = QStringLiteral("Failed to set binding on %1::%2.").
668 }
669 } else {
672 if (f->isBoundFunction())
674 newBinding->setTarget(object, *property, nullptr);
676 }
677 return;
678 }
679 }
680
684 binding && !binding->isSticky()) {
685 const auto stackFrame = engine->currentStackFrame;
686 switch (binding->kind()) {
688 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
690 "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
694 break;
695 }
699 "Overwriting binding on %s::%s at %s:%d",
702 break;
703 }
704 }
705 }
706 }
709
710 if (property->isVarProperty()) {
711 // allow assignment of "special" values (null, undefined, function) to var properties
715 return;
716 }
717
718#define PROPERTY_STORE(cpptype, value)
719 cpptype o = value;
720 int status = -1;
721 int flags = 0;
722 void *argv[] = { &o, 0, &status, &flags };
723 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
724
726 // functions are already handled, except for the QJSValue case
730
731 if (value.isNull() && property->isQObject()) {
732 PROPERTY_STORE(QObject*, nullptr);
733 } else if (value.isUndefined() && property->isResettable()) {
734 void *a[] = { nullptr };
736 } else if (value.isUndefined() && propType == QMetaType::fromType<QVariant>()) {
738 } else if (value.isUndefined() && propType == QMetaType::fromType<QJsonValue>()) {
740 } else if (propType == QMetaType::fromType<QJSValue>()) {
743 QString error = QLatin1String("Cannot assign [undefined] to ");
744 if (!propType.name())
745 error += QLatin1String("[unknown property type]");
746 else
749 return;
750 } else if (propType == QMetaType::fromType<int>() && value.isNumber()) {
752 } else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) {
754 } else if (propType == QMetaType::fromType<float>() && value.isNumber()) {
755 PROPERTY_STORE(float, float(value.asDouble()));
756 } else if (propType == QMetaType::fromType<double>() && value.isNumber()) {
757 PROPERTY_STORE(double, double(value.asDouble()));
758 } else if (propType == QMetaType::fromType<QString>() && value.isString()) {
760 } else if (property->isVarProperty()) {
765 && (value.isUndefined() || value.isPrimitive())) {
766 QQmlScriptString ss(value.toQStringNoThrow(), nullptr /* context */, object);
767 if (value.isNumber()) {
769 ss.d->isNumberLiteral = true;
770 } else if (value.isString()) {
772 ss.d->isStringLiteral = true;
773 }
775 } else {
776 QVariant v;
779 else
781
784 const char *valueType = (v.userType() == QMetaType::UnknownType)
785 ? "an unknown type"
786 : QMetaType(v.userType()).name();
787
788 const char *targetTypeName = propType.name();
789 if (!targetTypeName)
790 targetTypeName = "an unregistered type";
791
792 QString error = QLatin1String("Cannot assign ") +
794 QLatin1String(" to ") +
797 return;
798 }
799 }
800}
801
803{
805
806 QQmlData *ddata = QQmlData::get(object, true);
807 if (!ddata)
808 return Encode::undefined();
809
811
812 if (ddata->jsWrapper.isUndefined() &&
813 (ddata->jsEngineId == engine->m_engineId || // We own the QObject
814 ddata->jsEngineId == 0 || // No one owns the QObject
815 !ddata->hasTaintedV4Object)) { // Someone else has used the QObject, but it isn't tainted
816
820 return rv->asReturnedValue();
821
822 } else {
823 // If this object is tainted, we have to check to see if it is in our
824 // tainted object list
828
829 // If our tainted handle doesn't exist or has been collected, and there isn't
830 // a handle in the ddata, we can assume ownership of the ddata->jsWrapper
835 return result->asReturnedValue();
836 }
837
838 if (!alternateWrapper) {
844 }
845
847 }
848}
849
851{
852 const QObject *constObject = object;
853
854 QQmlData *ddata = QQmlData::get(object, true);
855
860
861 if (!constWrapper) {
867 ddata->hasConstWrapper = true;
868 }
869
871}
872
890
895
897{
898 Q_ASSERT(propertyIndex < 0xffff);
900
902 return;
903 QQmlData *ddata = QQmlData::get(object, /*create*/false);
904 if (!ddata)
905 return;
906
909 Q_ASSERT(property); // We resolved this property earlier, so it better exist!
911}
912
914{
916 const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a);
919
920 // We can have a const and a non-const wrapper for the same object.
923}
924
935
947
949{
950 if (!id.isString())
951 return Object::virtualPut(m, id, value, receiver);
952
953 Scope scope(m);
954 QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
956
957 if (that->internalClass()->isFrozen()) {
958 QString error = QLatin1String("Cannot assign to property \"") +
959 name->toQString() + QLatin1String("\" of read-only object");
961 return false;
962 }
963
965 return false;
966
970 // Types created by QML are not extensible at run-time, but for other QObjects we can store them
971 // as regular JavaScript properties, like on JavaScript objects.
972 if (ddata && ddata->context) {
973 QString error = QLatin1String("Cannot assign to non-existent property \"") +
974 name->toQString() + QLatin1Char('\"');
976 return false;
977 } else {
978 return Object::virtualPut(m, id, value, receiver);
979 }
980 }
981
982 return true;
983}
984
986{
987 if (id.isString()) {
988 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
989 const QObject *thatObject = that->d()->object();
991 Scope scope(m);
997 if (p) {
998 // ### probably not the fastest implementation
999 bool hasProperty;
1002 }
1003 return Attr_Data;
1004 }
1005 }
1006 }
1007
1008 return Object::virtualGetOwnProperty(m, id, p);
1009}
1010
1012{
1015 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
1016
1017private:
1018 QSet<QByteArray> m_alreadySeen;
1019};
1020
1021PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
1022{
1023 // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
1024 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
1025 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
1026 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
1027
1028 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(o);
1029
1030 QObject *thatObject = that->d()->object();
1031 if (thatObject && !QQmlData::wasDeleted(thatObject)) {
1032 const QMetaObject *mo = thatObject->metaObject();
1033 // These indices don't apply to gadgets, so don't block them.
1034 const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
1035 const int propertyCount = mo->propertyCount();
1036 if (propertyIndex < propertyCount) {
1037 ExecutionEngine *thatEngine = that->engine();
1038 Scope scope(thatEngine);
1039 const QMetaProperty property = mo->property(propertyIndex);
1040 ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name())));
1041 ++propertyIndex;
1042 if (attrs)
1043 *attrs= Attr_Data;
1044 if (pd) {
1045 QQmlPropertyData local;
1046 local.load(property);
1047 pd->value = that->getProperty(
1048 thatEngine, that->d(), thatObject, &local,
1049 QObjectWrapper::AttachMethods);
1050 }
1051 return propName->toPropertyKey();
1052 }
1053 const int methodCount = mo->methodCount();
1054 while (propertyIndex < propertyCount + methodCount) {
1055 Q_ASSERT(propertyIndex >= propertyCount);
1056 int index = propertyIndex - propertyCount;
1057 const QMetaMethod method = mo->method(index);
1058 ++propertyIndex;
1059 if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
1060 continue;
1061 // filter out duplicates due to overloads:
1062 if (m_alreadySeen.contains(method.name()))
1063 continue;
1064 else
1065 m_alreadySeen.insert(method.name());
1066 ExecutionEngine *thatEngine = that->engine();
1067 Scope scope(thatEngine);
1068 ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name())));
1069 if (attrs)
1070 *attrs = Attr_Data;
1071 if (pd) {
1072 QQmlPropertyData local;
1073 local.load(method);
1074 pd->value = that->getProperty(
1075 thatEngine, that->d(), thatObject, &local,
1076 QObjectWrapper::AttachMethods);
1077 }
1078 return methodName->toPropertyKey();
1079 }
1080 }
1081
1082 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1083}
1084
1090
1092{
1093 // Keep this code in sync with ::getQmlProperty
1096 if (!id.isString())
1099
1100 const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
1103
1104 QObject * const qobj = This->d()->object();
1105
1106 if (QQmlData::wasDeleted(qobj))
1107 return Encode::undefined();
1108
1109 QQmlData *ddata = QQmlData::get(qobj, false);
1112 if (!ddata)
1113 ddata = QQmlData::get(qobj, true);
1119 return method.asReturnedValue();
1120 }
1121
1122 if (!ddata || !ddata->propertyCache) {
1126 if (!property)
1127 return Encode::undefined();
1134 } else {
1141 }
1142 return result->asReturnedValue();
1143 }
1145
1146 if (!property) {
1147 // Check for attached properties
1148 if (name->startsWithUpper()) {
1150 return *importProperty;
1151 }
1153 }
1154
1155 if (property->isFunction()
1156 && !property->isVarProperty()
1157 && !property->isVMEFunction() // Handled by QObjectLookup
1158 && !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too
1159 QV4::Heap::QObjectMethod *method = nullptr;
1162 return lookup->getter(engine, *object);
1163 }
1164
1166
1168 return lookup->getter(engine, *object);
1169}
1170
1176
1178{
1181
1182 if (QObject *qObject = wrapper->object())
1183 return QMetaObject::metacall(qObject, call, index, a);
1184
1185 return 0;
1186}
1187
1190{
1191 if (!metaObject)
1192 return QLatin1String("null");
1193
1194 if (!object)
1195 return QString::fromUtf8(metaObject->className()) + QLatin1String("(0x0)");
1196
1197 const int id = metaObject->indexOfMethod("toString()");
1198 if (id >= 0) {
1204 return result.toString();
1207 return value->toQString();
1208 }
1209
1212 QLatin1String("(0x") + QString::number(quintptr(object), 16);
1214 if (!objectName.isEmpty())
1215 result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
1216 result += QLatin1Char(')');
1217 return result;
1218}
1219
1221{
1226
1230
1231 static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
1232 {
1233 switch (which) {
1234 case Destroy: {
1235 delete static_cast<QObjectSlotDispatcher*>(this_);
1236 }
1237 break;
1238 case Call: {
1239 if (QQmlData::wasDeleted(receiver))
1240 break;
1241
1242 QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
1243 ExecutionEngine *v4 = This->function.engine();
1244 // Might be that we're still connected to a signal that's emitted long
1245 // after the engine died. We don't track connections in a global list, so
1246 // we need this safeguard.
1247 if (!v4)
1248 break;
1249
1250 QQmlMetaObject::ArgTypeStorage<9> storage;
1251 QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr);
1252
1253 const qsizetype argCount = std::min(storage.size(), This->maxNumArguments);
1254
1255 Scope scope(v4);
1256 ScopedFunctionObject f(scope, This->function.value());
1257
1258 JSCallArguments jsCallData(scope, argCount);
1259 *jsCallData.thisObject = This->thisObject.isUndefined()
1260 ? v4->globalObject->asReturnedValue()
1261 : This->thisObject.value();
1262 for (qsizetype ii = 0; ii < argCount; ++ii) {
1263 QMetaType type = storage[ii];
1264 if (type == QMetaType::fromType<QVariant>()) {
1265 jsCallData.args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1266 } else {
1267 jsCallData.args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
1268 }
1269 }
1270
1271 f->call(jsCallData);
1272 if (scope.hasException()) {
1273 QQmlError error = v4->catchExceptionAsQmlError();
1274 if (error.description().isEmpty()) {
1275 ScopedString name(scope, f->name());
1276 error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString()));
1277 }
1278 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
1279 QQmlEnginePrivate::get(qmlEngine)->warning(error);
1280 } else {
1281 QMessageLogger(error.url().toString().toLatin1().constData(),
1282 error.line(), nullptr).warning().noquote()
1283 << error.toString();
1284 }
1285 }
1286 }
1287 break;
1288 case Compare: {
1289 QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
1290 if (connection->function.isUndefined()) {
1291 *ret = false;
1292 return;
1293 }
1294
1295 // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
1296 // for the new-style QObject::connect. Here we use the engine pointer as sentinel
1297 // to distinguish those type of QSlotObjectBase connections from our QML connections.
1298 ExecutionEngine *v4 = reinterpret_cast<ExecutionEngine*>(metaArgs[0]);
1299 if (v4 != connection->function.engine()) {
1300 *ret = false;
1301 return;
1302 }
1303
1304 Scope scope(v4);
1305 ScopedValue function(scope, *reinterpret_cast<Value*>(metaArgs[1]));
1306 ScopedValue thisObject(scope, *reinterpret_cast<Value*>(metaArgs[2]));
1307 QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
1308 int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
1309
1310 if (slotIndexToDisconnect != -1) {
1311 // This is a QObject function wrapper
1312 if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1313 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1314
1315 ScopedFunctionObject f(scope, connection->function.value());
1316 std::pair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f);
1317 if (connectedFunctionData.first == receiverToDisconnect &&
1318 connectedFunctionData.second == slotIndexToDisconnect) {
1319 *ret = true;
1320 return;
1321 }
1322 }
1323 } else {
1324 // This is a normal JS function
1325 if (RuntimeHelpers::strictEqual(*connection->function.valueRef(), function) &&
1326 connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1327 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1328 *ret = true;
1329 return;
1330 }
1331 }
1332
1333 *ret = false;
1334 }
1335 break;
1336 case NumOperations:
1337 break;
1338 }
1339 };
1340};
1341
1343{
1344 Scope scope(b);
1345
1346 if (argc == 0)
1347 THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
1348
1351 int signalIndex = signalInfo.second; // in method range, not signal range!
1352
1353 if (signalIndex < 0)
1354 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1355
1356 if (!signalObject)
1357 THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1358
1361 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1362
1365
1366 if (argc == 1) {
1367 f = argv[0];
1368 } else if (argc >= 2) {
1369 object = argv[0];
1370 f = argv[1];
1371 }
1372
1373 if (!f)
1374 THROW_GENERIC_ERROR("Function.prototype.connect: target is not a function");
1375
1376 if (!object->isUndefined() && !object->isObject())
1377 THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object");
1378
1381
1384
1388 }
1389 }
1390
1391 std::pair<QObject *, int> functionData = QObjectMethod::extractQtMethod(f); // align with disconnect
1392 QObject *receiver = nullptr;
1393
1394 if (functionData.first)
1396 else if (auto qobjectWrapper = object->as<QV4::QObjectWrapper>())
1398 else if (auto typeWrapper = object->as<QV4::QQmlTypeWrapper>())
1400
1401 if (receiver) {
1402 if (functionData.second == -1) {
1404 } else {
1405 // This means we are connecting to QObjectMethod which complains about extra arguments.
1406 Heap::QObjectMethod *d = static_cast<Heap::QObjectMethod *>(f->d());
1409 [](int a, const QQmlPropertyData &b) {
1410 return std::max(a, b.metaMethod().parameterCount());
1411 });
1412 }
1413
1415 } else {
1418 "Could not find receiver of the connection, using sender as receiver. Disconnect "
1419 "explicitly (or delete the sender) to make sure the connection is removed.");
1421 }
1422
1424}
1425
1427{
1428 Scope scope(b);
1429
1430 if (argc == 0)
1431 THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
1432
1436
1437 if (signalIndex == -1)
1438 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1439
1440 if (!signalObject)
1441 THROW_GENERIC_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1442
1444 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1445
1448
1449 if (argc == 1) {
1450 functionValue = argv[0];
1451 } else if (argc >= 2) {
1453 functionValue = argv[1];
1454 }
1455
1456 if (!functionValue)
1457 THROW_GENERIC_ERROR("Function.prototype.disconnect: target is not a function");
1458
1460 THROW_GENERIC_ERROR("Function.prototype.disconnect: target this is not an object");
1461
1463
1464 void *a[] = {
1465 scope.engine,
1470 };
1471
1472 QObject *receiver = nullptr;
1473
1474 if (functionData.first)
1478 else if (auto typeWrapper = functionThisValue->as<QV4::QQmlTypeWrapper>())
1480
1481 if (receiver) {
1483 reinterpret_cast<void **>(&a));
1484 } else {
1486 reinterpret_cast<void **>(&a));
1487 }
1488
1490}
1491
1492static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack)
1493{
1494 QQueue<QObject *> queue;
1495 queue.append(parent->children());
1496
1497 while (!queue.isEmpty()) {
1498 QObject *child = queue.dequeue();
1499 if (!child)
1500 continue;
1501 QObjectWrapper::markWrapper(child, markStack);
1502 queue.append(child->children());
1503 }
1504}
1505
1506// returns true if and only if an object was recorded
1508 QObject *o, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
1509 MemoryManager::ObjectsForCompilationUnit *recorded)
1510{
1511 Q_ASSERT(compilationUnit);
1512 Q_ASSERT(o);
1513 Q_ASSERT(recorded);
1514
1515 const auto &baseUnit = compilationUnit->baseCompilationUnit();
1516 Q_ASSERT(baseUnit);
1517
1518 for (const auto &unit : recorded->compilationUnits) {
1519 if (baseUnit == unit) {
1520 recorded->objects.push_back(o);
1521 return true;
1522 }
1523 }
1524
1525 return false;
1526}
1527
1528// returns true if and only if an object was recorded
1530 QObject *o, const QQmlVMEMetaObject *vme,
1531 MemoryManager::ObjectsForCompilationUnit *recorded)
1532{
1533 for (; vme; vme = vme->parentVMEMetaObject()) {
1534 if (recordObjectForCompilationUnits(o, vme->compilationUnit(), recorded))
1535 return true;
1536 }
1537
1538 return false;
1539}
1540
1542{
1544
1545 QObject *o = static_cast<QObjectWrapper *>(that)->object();
1546 if (!o)
1547 return;
1548
1550 // Children usually don't need to be marked, the gc keeps them alive.
1551 // But in the rare case of a "floating" QObject without a parent that
1552 // _gets_ marked (we've been called here!) then we also need to
1553 // propagate the marking down to the children recursively.
1554 if (!o->parent())
1556 });
1557
1559 if (!ddata)
1560 return;
1561
1562 if (ddata->hasVMEMetaObject) {
1563 if (auto *vme = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) {
1564 vme->mark(markStack);
1566 // ddata->compilationUnit can be different from the VME compilation units
1567 // if the "topmost" instantiation of the object is inaddressible.
1571 }
1572 }
1573 }
1574 } else if (const auto &cu = ddata->compilationUnit) {
1575 // Objects without a VMEMetaObject are identified via QQmlData::compilationUnit.
1578 }
1579
1581 return;
1582
1583 // mark the const wrapper if our engine has interacted with it at some point
1586 scope, scope.engine->m_multiplyWrappedQObjects->value(static_cast<const QObject *>(o)));
1587
1588 if (!constWrapper)
1589 return;
1590
1591 if (that != constWrapper->d()) {
1592 // We've got the non-const wrapper. Also mark the const one.
1594 return;
1595 }
1596
1597 // We've got the const wrapper. Also mark the non-const one
1600 else
1602}
1603
1605{
1606 Heap::QObjectWrapper *h = d();
1608
1609 if (QObject *o = h->object()) {
1610 QQmlData *ddata = QQmlData::get(o, false);
1611 if (ddata) {
1612 if (!o->parent() && !ddata->indestructible) {
1613 if (ddata && ddata->ownContext) {
1617 ddata->context = nullptr;
1618 }
1619
1620 // This object is notionally destroyed now. It might still live until the next
1621 // event loop iteration, but it won't need its connections, CU, or deferredData
1622 // anymore.
1623
1624 ddata->isQueuedForDeletion = true;
1627
1630
1631 if (lastCall)
1632 delete o;
1633 else
1634 o->deleteLater();
1635 } else {
1636 // If the object is C++-owned, we still have to release the weak reference we have
1637 // to it. If the "main" wrapper is not ours, we should leave it alone, though.
1638 if (ddata->jsWrapper.as<QObjectWrapper>() == this)
1640 }
1641 }
1642 }
1643
1644 h->destroy();
1645}
1646
1647
1649
1650template<typename Retrieve>
1651int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
1652 if (conversionMetaType == QMetaType::fromType<QVariant>())
1653 return 0;
1654
1655 const QMetaType type = retrieve();
1656 if (type == conversionMetaType)
1657 return 0;
1658
1659 if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
1660 if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject))
1661 return 1;
1662 }
1663
1664 if (QMetaType::canConvert(type, conversionMetaType)) {
1665 if (conversionMetaType == QMetaType::fromType<QJSValue>()
1666 || conversionMetaType == QMetaType::fromType<double>()
1667 || conversionMetaType == QMetaType::fromType<QString>()) {
1668 // Unspecific conversions receive lower score. You can convert anything
1669 // to QString or double via toString() and valueOf(), respectively.
1670 // And anything can be wrapped into QJSValue, but that's inefficient.
1671 return 6;
1672 }
1673
1674 // We have an explicitly defined conversion method to a non-boring type.
1675 return 5;
1676 }
1677
1678 return 10;
1679};
1680
1681/*
1682 Returns the match score for converting \a actual to be of type \a conversionType. A
1683 zero score means "perfect match" whereas a higher score is worse.
1684
1685 The conversion table is copied out of the \l QScript::callQtMethod() function.
1686*/
1687static int MatchScore(const Value &actual, QMetaType conversionMetaType)
1688{
1689 const int conversionType = conversionMetaType.id();
1690 const auto convertibleScore = [&](QMetaType actualType) {
1691 // There are a number of things we can do in JavaScript to subvert this, but
1692 // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
1693 if (!QMetaType::canConvert(actualType, conversionMetaType))
1694 return 10;
1695
1696 // You can convert anything to QJSValue, but that's inefficient.
1697 // If we have a better option, we should use it.
1698 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1699 return 9;
1700
1701 // You can also convert anything to QVariant, but that's also suboptimal.
1702 // You can convert anything to string or double via toString() and valueOf().
1703 // Those are also rather unspecific.
1704 switch (conversionType) {
1705 case QMetaType::QVariant:
1706 case QMetaType::Double:
1707 case QMetaType::QString:
1708 return 9;
1709 default:
1710 break;
1711 }
1712
1713 // We have an explicitly defined conversion method to a non-boring type.
1714 return 8;
1715 };
1716
1717 if (actual.isNumber()) {
1718 switch (conversionType) {
1719 case QMetaType::Double:
1720 return 0;
1721 case QMetaType::Float:
1722 return 1;
1723 case QMetaType::LongLong:
1724 case QMetaType::ULongLong:
1725 return 2;
1726 case QMetaType::Long:
1727 case QMetaType::ULong:
1728 return 3;
1729 case QMetaType::Int:
1730 case QMetaType::UInt:
1731 return 4;
1732 case QMetaType::Short:
1733 case QMetaType::UShort:
1734 return 5;
1735 break;
1736 case QMetaType::Char:
1737 case QMetaType::UChar:
1738 return 6;
1739 case QMetaType::QJsonValue:
1740 return 5;
1741 default:
1742 return convertibleScore(actual.isInteger()
1743 ? QMetaType::fromType<int>()
1744 : QMetaType::fromType<double>());
1745 }
1746 } else if (actual.isString()) {
1747 switch (conversionType) {
1748 case QMetaType::QString:
1749 return 0;
1750 case QMetaType::QJsonValue:
1751 return 5;
1752 case QMetaType::QUrl:
1753 return 6; // we like to convert strings to URLs in QML
1754 case QMetaType::Double:
1755 case QMetaType::Float:
1756 case QMetaType::LongLong:
1757 case QMetaType::ULongLong:
1758 case QMetaType::Int:
1759 case QMetaType::UInt:
1760 case QMetaType::Short:
1761 case QMetaType::UShort:
1762 case QMetaType::Char:
1763 case QMetaType::UChar:
1764 // QMetaType can natively convert strings to numbers.
1765 // However, in the general case it's of course extremely lossy.
1766 return 10;
1767 default:
1768 return convertibleScore(QMetaType::fromType<QString>());
1769 }
1770 } else if (actual.isBoolean()) {
1771 switch (conversionType) {
1772 case QMetaType::Bool:
1773 return 0;
1774 case QMetaType::QJsonValue:
1775 return 5;
1776 default:
1777 return convertibleScore(QMetaType::fromType<bool>());
1778 }
1779 } else if (actual.as<DateObject>()) {
1780 switch (conversionType) {
1781 case QMetaType::QDateTime:
1782 return 0;
1783 case QMetaType::QDate:
1784 return 1;
1785 case QMetaType::QTime:
1786 return 2;
1787 default:
1788 return convertibleScore(QMetaType::fromType<QDateTime>());
1789 }
1790 } else if (actual.as<RegExpObject>()) {
1791 switch (conversionType) {
1792#if QT_CONFIG(regularexpression)
1793 case QMetaType::QRegularExpression:
1794 return 0;
1795 default:
1796 return convertibleScore(QMetaType::fromType<QRegularExpression>());
1797#else
1798 default:
1799 return convertibleScore(QMetaType());
1800#endif
1801 }
1802 } else if (actual.as<ArrayBuffer>()) {
1803 switch (conversionType) {
1804 case QMetaType::QByteArray:
1805 return 0;
1806 default:
1807 return convertibleScore(QMetaType::fromType<QByteArray>());
1808 }
1809 } else if (actual.as<ArrayObject>()) {
1810 switch (conversionType) {
1811 case QMetaType::QJsonArray:
1812 return 3;
1813 case QMetaType::QStringList:
1814 case QMetaType::QVariantList:
1815 return 5;
1816 case QMetaType::QVector4D:
1817 case QMetaType::QMatrix4x4:
1818 return 6;
1819 case QMetaType::QVector3D:
1820 return 7;
1821 default:
1822 return convertibleScore(QMetaType());
1823 }
1824 } else if (actual.isNull()) {
1825 switch (conversionType) {
1826 case QMetaType::Nullptr:
1827 case QMetaType::VoidStar:
1828 case QMetaType::QObjectStar:
1829 case QMetaType::QJsonValue:
1830 return 0;
1831 default: {
1832 if (conversionMetaType.flags().testFlag(QMetaType::IsPointer))
1833 return 0;
1834 else
1835 return convertibleScore(QMetaType());
1836 }
1837 }
1838 } else if (const Object *obj = actual.as<Object>()) {
1839 if (const VariantObject *variantObject = obj->as<VariantObject>()) {
1840 return MatchVariant(conversionMetaType, [variantObject]() {
1841 return variantObject->d()->data().metaType();
1842 });
1843 }
1844
1845 if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
1846 switch (conversionType) {
1847 case QMetaType::QObjectStar:
1848 return 0;
1849 default:
1850 if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
1851 QObject *wrapped = wrapper->object();
1852 if (!wrapped)
1853 return 0;
1854 if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject()))
1855 return 0;
1856 }
1857 }
1858
1859 return convertibleScore(QMetaType::fromType<QObject *>());
1860 }
1861
1862 if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
1863 const QQmlType type = wrapper->d()->type();
1864 if (type.isSingleton()) {
1865 const QMetaType metaType = type.typeId();
1866 if (metaType == conversionMetaType)
1867 return 0;
1868
1869 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1870 && metaType.flags() & QMetaType::PointerToQObject
1871 && type.metaObject()->inherits(conversionMetaType.metaObject())) {
1872 return 0;
1873 }
1874 } else if (QObject *object = wrapper->object()) {
1875 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1876 && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) {
1877 return 0;
1878 }
1879 }
1880
1881 return convertibleScore(QMetaType());
1882 }
1883
1884 if (const Sequence *sequence = obj->as<Sequence>()) {
1885 const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(sequence);
1886 if (sequenceType == conversionMetaType)
1887 return 1;
1888
1889 return convertibleScore(sequenceType);
1890 }
1891
1892 if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
1893 return MatchVariant(conversionMetaType, [wrapper]() {
1894 return wrapper->d()->isVariant()
1895 ? wrapper->toVariant().metaType()
1896 : wrapper->type();
1897 });
1898 }
1899
1900 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1901 return 0;
1902
1903 switch (conversionType) {
1904 case QMetaType::QJsonObject:
1905 case QMetaType::QVariantMap:
1906 return 5;
1907 default:
1908 break;
1909 }
1910
1911 }
1912
1913 return convertibleScore(QMetaType());
1914}
1915
1916static int numDefinedArguments(CallData *callArgs)
1917{
1918 int numDefinedArguments = callArgs->argc();
1919 while (numDefinedArguments > 0
1920 && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) {
1921 --numDefinedArguments;
1922 }
1923 return numDefinedArguments;
1924}
1925
1926static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
1927{
1928 const QMetaObject *metaObject = object.metaObject();
1929 const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
1930 return indexOfClassInfo != -1
1931 && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
1932}
1933
1937{
1938 const auto constructReturnValue = [&](QMetaType returnType, void *returnValue) {
1939 // If we construct a value in place, we mustn't initialize the memory before that.
1942 };
1943
1944 const auto convertReturnValue
1946
1947 // In contrast to other invocations of convertAndCall() we do not want to create
1948 // a URL object from a QUrl here like metaTypeFromJS does. This is for compatibility.
1949 // URL objects are proper, specified, objects that behave different from our variant
1950 // objects when it comes to equality comparisons.
1952 ? engine->fromVariant(*reinterpret_cast<const QVariant *>(returnValue))
1954
1955 const auto typeFlags = returnType.flags();
1958 } else if (typeFlags & QMetaType::PointerToQObject) {
1959 // We consider QObjects returned from invokables as owned by the QML engine unless
1960 // explicitly configured otherwise.
1961 if (QObject *object = *reinterpret_cast<QObject **>(returnValue))
1963 }
1964 return result;
1965 };
1966
1967 const auto conversionErrorHandler = [&](QMetaType argumentType, void *argument, int ii) {
1968 // We've checked the number of arguments before
1969 Q_ASSERT(ii < callArgs->argc());
1970
1972 // We have an engine here. So we can conjure up a QJSManagedValue for anything.
1973 *static_cast<QJSManagedValue *>(argument)
1975 return true;
1976 }
1977
1979 && callArgs->args[ii].isUndefined()) {
1980 // TODO: QObjectMethod silently converts undefined to null when you pass undefined
1981 // to a method that accepts some QObject-derived. This is wrong because
1982 // undefined is certainly not null. We accept it for compatibility.
1983 *static_cast<QObject **>(argument) = nullptr;
1984 return true;
1985 }
1986
1987 // TODO: QObjectMethod is allowed to use QVariant conversion.
1988 // This is wrong because we can't see the converters at compile time.
1989 // It only exists for compatibility with old versions of Qt.
1993 return true;
1994
1995 qWarning() << QString::fromLatin1("Could not convert argument %1 from %2 to %3")
1996 .arg(ii)
1998 .arg(argumentType.name());
1999 const StackTrace stack = engine->stackTrace();
2000 for (const StackFrame &frame : stack) {
2001 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source + (frame.line > 0
2002 ? (QLatin1Char(':') + QString::number(frame.line))
2003 : QString());
2004
2005 }
2006
2008 qWarning() << "Passing incompatible arguments to signals is not supported.";
2010 }
2011
2013 "Passing incompatible arguments to C++ functions from JavaScript is not allowed."));
2014 return false;
2015 };
2016
2018
2019 const auto handleTooManyArguments = [&](int expectedArguments) {
2021 engine->throwError(QStringLiteral("Too many arguments"));
2022 return false;
2023 }
2024
2025 const auto stackTrace = engine->stackTrace();
2026 if (stackTrace.isEmpty()) {
2028 << "When matching arguments for "
2029 << object.className() << "::" << data.name(object.metaObject()) << "():";
2030 } else {
2031 const StackFrame frame = stackTrace.first();
2033 + (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
2034 : QString());
2035 }
2036
2037 qWarning().noquote() << QStringLiteral("Too many arguments, ignoring %1")
2039 return true;
2040 };
2041
2043
2044 if (data.hasArguments()) {
2046
2047 const bool ok = data.isConstructor()
2052 if (!ok)
2054
2055 const int expectedArgumentCount = storage.size() - 1;
2057 return engine->throwError(QLatin1String("Insufficient arguments"));
2058
2061 return Encode::undefined();
2062 }
2063
2064 return QV4::convertAndCall(
2067 [&](void **argData, const QMetaType *types, int argc) {
2068 Q_UNUSED(types);
2069 Q_UNUSED(argc);
2072 }
2073
2075 return Encode::undefined();
2076
2078 if (!returnType.isValid())
2080
2081 return QV4::convertAndCall(
2082 engine, &returnType, 1, nullptr, 0,
2083 [&](void **argData, const QMetaType *types, int argc) {
2084 Q_UNUSED(types);
2085 Q_UNUSED(argc);
2088}
2089
2090/*
2091Resolve the overloaded method to call. The algorithm works conceptually like this:
2092 1. Resolve the set of overloads it is *possible* to call.
2093 Impossible overloads include those that have too many parameters or have parameters
2094 of unknown type.
2095 2. Filter the set of overloads to only contain those with the closest number of
2096 parameters.
2097 For example, if we are called with 3 parameters and there are 2 overloads that
2098 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
2099 3. Find the best remaining overload based on its match score.
2100 If two or more overloads have the same match score, return the last one. The match
2101 score is constructed by adding the matchScore() result for each of the parameters.
2102*/
2106{
2107 const int argumentCount = callArgs->argc();
2109
2110 const QQmlPropertyData *best = nullptr;
2114
2117
2118 for (int i = 0; i < methodCount; ++i) {
2119 const QQmlPropertyData *attempt = methods + i;
2120
2126 qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
2127 }
2128
2129 // QQmlV4Function overrides anything that doesn't provide the exact number of arguments
2130 int methodParameterScore = 1;
2131 // QQmlV4Function overrides the "no idea" option, which is 10
2132 int maxMethodMatchScore = 9;
2133 // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments
2135
2136 if (!attempt->isV4Function()) {
2138 int methodArgumentCount = 0;
2139 if (attempt->hasArguments()) {
2140 if (attempt->isConstructor()) {
2141 if (!object.constructorParameterTypes(*attempt, &storage, nullptr)) {
2142 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2143 continue;
2144 }
2145 } else {
2146 if (!object.methodParameterTypes(*attempt, &storage, nullptr)) {
2147 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2148 continue;
2149 }
2150 }
2152 }
2153
2155 qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
2156 continue; // We don't have sufficient arguments to call this method
2157 }
2158
2160 ? 0
2163 qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
2164 continue; // We already have a better option
2165 }
2166
2169 for (int ii = 0; ii < methodArgumentCount; ++ii) {
2170 const int score = MatchScore((v = Value::fromStaticValue(callArgs->args[ii])),
2171 storage[ii]);
2174 }
2175 }
2176
2181 best = attempt;
2185 qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
2186 << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
2187 << "bestSumMatchScore" << bestSumMatchScore << "\n";
2188 } else {
2189 qCDebug(lcOverloadResolution) << "did not update best\n"
2190 << "bestParameterScore" << bestParameterScore << "\t"
2191 << "methodParameterScore" << methodParameterScore << "\n"
2192 << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
2193 << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
2194 << "bestSumMatchScore" << bestSumMatchScore << "\t"
2195 << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
2196 }
2197
2198 if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
2199 qCDebug(lcOverloadResolution, "perfect match");
2200 break; // We can't get better than that
2201 }
2202
2203 };
2204
2205 if (best && best->isValid()) {
2206 return best;
2207 } else {
2208 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
2209 for (int i = 0; i < methodCount; ++i) {
2214 error += u"\n " + QString::fromUtf8(m.methodSignature());
2215 }
2216
2218 return nullptr;
2219 }
2220}
2221
2222static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
2223{
2224 if (required == QMetaType::fromType<QVariant>()
2225 || required == QMetaType::fromType<QJSValue>()
2226 || required == QMetaType::fromType<QJSManagedValue>()) {
2227 return true;
2228 }
2229
2230 if (data) {
2231 if (passed == QMetaType::fromType<QVariant>())
2232 passed = static_cast<const QVariant *>(data)->metaType();
2233 else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
2234 passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
2235 }
2236
2237 if (passed == required)
2238 return true;
2239
2240 if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
2241 switch (passed.id()) {
2242 case QMetaType::UnknownType:
2243 case QMetaType::Nullptr:
2244 case QMetaType::Bool:
2245 case QMetaType::Int:
2246 case QMetaType::Double:
2247 case QMetaType::QString:
2248 return true;
2249 default:
2250 break;
2251 }
2252 }
2253
2254 return false;
2255}
2256
2258 const QMetaMethod &method, void **argv, int argc, const QMetaType *types)
2259{
2260 if (types[0].isValid() && !ExactMatch(method.returnMetaType(), types[0], nullptr))
2261 return false;
2262
2263 if (method.parameterCount() != argc)
2264 return false;
2265
2266 for (int i = 0; i < argc; ++i) {
2267 if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1]))
2268 return false;
2269 }
2270
2271 return true;
2272}
2273
2276 void **argv, int argc, const QMetaType *types)
2277{
2278 // We only accept exact matches here. Everything else goes through the JavaScript conversion.
2279 for (int i = 0; i < methodCount; ++i) {
2280 const QQmlPropertyData *attempt = methods + i;
2282 return attempt;
2283 }
2284
2285 return nullptr;
2286}
2287
2296
2306
2310{
2312
2314 if (cloneFrom->wrapper) {
2316 if (ref) {
2318 } else {
2319 // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what
2320 // value we should operate on. Without knowledge of the property the value
2321 // was read from, we cannot load the value from the given object.
2322 return Encode::undefined();
2323 }
2324 }
2325
2327 valueScope,
2330
2332
2333 Q_ASSERT(method->d()->methods == nullptr);
2334 switch (cloneFrom->methodCount) {
2335 case 0:
2336 Q_ASSERT(cloneFrom->methods == nullptr);
2337 break;
2338 case 1:
2340 == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
2341 method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
2342 *method->d()->methods = *cloneFrom->methods;
2343 break;
2344 default:
2345 Q_ASSERT(cloneFrom->methods != nullptr);
2349 break;
2350 }
2351
2352 return method.asReturnedValue();
2353}
2354
2356{
2360}
2361
2363{
2365
2367 return valueWrapper->metaObject();
2368 if (QObject *self = object())
2369 return self->metaObject();
2370
2371 return nullptr;
2372}
2373
2375{
2377
2379 return objectWrapper->object();
2381 return typeWrapper->object();
2382 return nullptr;
2383}
2384
2385bool Heap::QObjectMethod::isDetached() const
2386{
2387 if (!wrapper)
2388 return true;
2389
2392 return valueWrapper->d()->object() == nullptr;
2393
2394 return false;
2395}
2396
2398{
2401 return objectWrapper->object() == o;
2403 return typeWrapper->object() == o;
2404
2408 return qobject->object() == o;
2410 return type->object() == o;
2411
2412 // Attached to some nested value type or sequence object
2413 return false;
2414 }
2415
2416 return false;
2417}
2418
2420 const QMetaObject *thisMeta) const
2421{
2422 // Check that the metaobject matches.
2423
2424 if (!thisMeta) {
2425 // You can only get a detached method via a lookup, and then you have a thisObject.
2427 return Included;
2428 }
2429
2430 const auto check = [&](const QMetaObject *included) {
2435 "%s:%d: Calling C++ methods with 'this' objects different from the one "
2436 "they were retrieved from is broken, due to historical reasons. The "
2437 "original object is used as 'this' object. You can allow the given "
2438 "'this' object to be used by setting "
2439 "'pragma NativeMethodBehavior: AcceptThisObject'",
2441 return Included;
2442 }
2443
2444 // destroy() and toString() can be called on all QObjects, but not on gadgets.
2445 if (index < 0)
2447
2448 // Find the base type the method belongs to.
2450 while (true) {
2451 if (included == thisMeta)
2452 return Explicit;
2453
2454 if (methodOffset <= index)
2456
2460 };
2461
2463 };
2464
2465 if (const QMetaObject *meta = metaObject())
2466 return check(meta);
2467
2468 // If the QObjectMethod is detached, we can only have gotten here via a lookup.
2469 // The lookup checks that the QQmlPropertyCache matches.
2470 return Explicit;
2471}
2472
2474{
2476 return QStringLiteral("destroy");
2477 else if (index == QV4::QObjectMethod::ToStringMethod)
2478 return QStringLiteral("toString");
2479
2480 const QMetaObject *mo = metaObject();
2481 if (!mo)
2482 return QString();
2483
2484 int methodOffset = mo->methodOffset();
2485 while (methodOffset > index) {
2486 mo = mo->superClass();
2488 }
2489
2490 return "%1::%2"_L1.arg(QLatin1StringView{mo->className()},
2492}
2493
2495{
2496 if (methods) {
2497 Q_ASSERT(methodCount > 0);
2498 return;
2499 }
2500
2501 const QMetaObject *mo = metaObject();
2502
2503 if (!mo)
2504 mo = thisMeta;
2505
2506 Q_ASSERT(mo);
2507
2508 int methodOffset = mo->methodOffset();
2509 while (methodOffset > index) {
2510 mo = mo->superClass();
2512 }
2516 dummy.load(method);
2519 // Look for overloaded methods
2521 for (int ii = index - 1; ii >= methodOffset; --ii) {
2522 if (methodName == mo->method(ii).name()) {
2523 method = mo->method(ii);
2524 dummy.load(method);
2526 }
2527 }
2528 if (resolvedMethods.size() > 1) {
2532 } else {
2533 methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod);
2535 methodCount = 1;
2536 }
2537
2538 Q_ASSERT(methodCount > 0);
2539}
2540
2547
2549 ExecutionEngine *engine, QObject *o, const Value *args, int argc) const
2550{
2551 method_destroy(engine, o, argc > 0 ? args[0].toInt32() : 0);
2552 return Encode::undefined();
2553}
2554
2556{
2557 if (!o)
2558 return true;
2559
2561 engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2562 return false;
2563 }
2564
2565 if (delay > 0)
2567 else
2568 o->deleteLater();
2569
2570 return true;
2571}
2572
2574 const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2575{
2576 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2578}
2579
2581 const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
2582{
2583 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2585}
2586
2588{
2590
2591 const QMetaObject *thisMeta = nullptr;
2592
2593 QObject *o = nullptr;
2595 if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
2596 thisMeta = w->metaObject();
2597 o = w->object();
2598 } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
2599 thisMeta = w->metaObject();
2600 o = w->object();
2601 } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
2602 thisMeta = w->metaObject();
2603 valueWrapper = w->d();
2604 }
2605
2607 if (o && o == d()->object()) {
2609 // Nothing to do; objects are the same. This should be common
2610 } else if (valueWrapper && valueWrapper == d()->wrapper) {
2612 // Nothing to do; gadgets are the same. This should be somewhat common
2613 } else {
2615 if (mode == Heap::QObjectMethod::Invalid) {
2616 v4->throwError(QLatin1String("Cannot call method %1 on %2").arg(
2617 d()->name(), thisObject->toQStringNoThrow()));
2618 return Encode::undefined();
2619 }
2620 }
2621
2622 QQmlObjectOrGadget object = [&](){
2623 if (mode == Heap::QObjectMethod::Included) {
2624 QV4::Scope scope(v4);
2628 return QQmlObjectOrGadget(type->object());
2630 valueWrapper = value->d();
2632 }
2633 Q_UNREACHABLE();
2634 } else {
2635 if (o)
2636 return QQmlObjectOrGadget(o);
2637
2642 }
2643 }();
2644
2645 if (object.isNull())
2646 return Encode::undefined();
2647
2648 if (d()->index == DestroyMethod)
2649 return method_destroy(v4, object.qObject(), argv, argc);
2650 else if (d()->index == ToStringMethod)
2651 return method_toString(v4, object.qObject());
2652
2654
2655 Scope scope(v4);
2658
2659 const QQmlPropertyData *method = d()->methods;
2660
2661 // If we call the method, we have to write back any value type references afterwards.
2662 // The method might change the value.
2663 const auto doCall = [&](const auto &call) {
2664 if (!method->isConstant()) {
2668 return rv->asReturnedValue();
2669 }
2670 }
2671
2672 return call();
2673 };
2674
2675 if (d()->methodCount != 1) {
2676 Q_ASSERT(d()->methodCount > 0);
2678 if (method == nullptr)
2679 return Encode::undefined();
2680 }
2681
2682 if (method->isV4Function()) {
2683 return doCall([&]() {
2687
2688 void *args[] = { nullptr, &funcptr };
2690
2691 return rv->asReturnedValue();
2692 });
2693 }
2694
2695 return doCall([&]() { return callPrecise(object, *method, v4, callData); });
2696}
2697
2699{
2700 constexpr int parameterCount() const { return 0; }
2701 constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
2702 constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
2703};
2704
2706 QObject *thisObject, void **argv, const QMetaType *types, int argc) const
2707{
2709
2710 const QMetaObject *thisMeta = nullptr;
2712
2713 if (thisObject) {
2715 } else {
2719 }
2720
2721 QQmlObjectOrGadget object = [&](){
2722 if (thisObject)
2724
2725 Scope scope(v4);
2728
2733 }();
2734
2735 if (object.isNull())
2736 return;
2737
2738 if (d()->index == DestroyMethod) {
2739 // method_destroy will use at most one argument
2741 v4, thisObject, argv, types, std::min(argc, 1),
2742 [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
2744 return method_destroy(v4, object.qObject(), argv, argc);
2745 });
2746 return;
2747 }
2748
2749 if (d()->index == ToStringMethod) {
2753 [v4, thisMeta, object](void **argv, int) {
2754 if (!argv[0])
2755 return;
2756 *static_cast<QString *>(argv[0])
2758 });
2759 return;
2760 }
2761
2763
2764 const QQmlPropertyData *method = d()->methods;
2765 if (d()->methodCount != 1) {
2766 Q_ASSERT(d()->methodCount > 0);
2768 }
2769
2770 if (!method || method->isV4Function()) {
2773 [this](const Value *thisObject, const Value *argv, int argc) {
2774 return callInternal(thisObject, argv, argc);
2775 });
2776 } else {
2780 [v4, object, valueWrapper, method](void **argv, int argc) {
2781 Q_UNUSED(argc);
2782
2783 // If we call the method, we have to write back any value type references afterwards.
2784 // The method might change the value.
2786 if (!method->isConstant()) {
2789 }
2790
2791 // If the method returns a QObject* we need to track it on the JS heap
2792 // (if it's destructible).
2793 QObject *qobjectPtr = nullptr;
2795 if (argv[0]) {
2797 qobjectPtr = *static_cast<QObject **>(argv[0]);
2798 } else if (resultType == QMetaType::fromType<QVariant>()) {
2799 const QVariant *result = static_cast<const QVariant *>(argv[0]);
2802 qobjectPtr = *static_cast<QObject *const *>(result->data());
2803 }
2804 }
2805
2806 if (qobjectPtr) {
2809 ddata->indestructible = false;
2811 }
2812 }
2813 });
2814 }
2815}
2816
2818
2819void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
2820{
2821 Object::init();
2822 this->signalIndex = signalIndex;
2823 setObject(object);
2824}
2825
2827
2829{
2833 << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
2834 "not call it directly. Make it a proper function and call "
2835 "that or emit the signal.")
2837
2838 Scope scope(engine());
2841 scope.engine,
2842 static_cast<Heap::QObjectWrapper *>(nullptr),
2843 signalIndex()));
2844
2845 return method->call(thisObject, argv, argc);
2846}
2847
2862
2863
2866{
2867 const QObjectBiPointer key = it.key();
2868 const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
2869 disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
2870 return QHash<QObjectBiPointer, WeakValue>::erase(it);
2871}
2872
2873void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
2874{
2875 QHash<QObjectBiPointer, WeakValue>::remove(object);
2876 QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object));
2877}
2878
2879} // namespace QV4
2880
2881QT_END_NAMESPACE
2882
2883#include "moc_qv4qobjectwrapper_p.cpp"
QHash< QObjectBiPointer, QV4::WeakValue >::Iterator Iterator
Definition qjsvalue.h:24
static int MatchScore(const Value &actual, QMetaType conversionMetaType)
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 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")
#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