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
1507 QObject *o, const QQmlVMEMetaObject *vme,
1508 MemoryManager::ObjectsForCompilationUnit *recorded)
1509{
1510 for (; vme; vme = vme->parentVMEMetaObject()) {
1511 for (const auto &unit : recorded->compilationUnits) {
1512 if (vme->compilationUnit()->baseCompilationUnit() == unit) {
1513 recorded->objects.push_back(o);
1514 return;
1515 }
1516 }
1517 }
1518}
1519
1521{
1523
1524 QObject *o = static_cast<QObjectWrapper *>(that)->object();
1525 if (!o)
1526 return;
1527
1529 // Children usually don't need to be marked, the gc keeps them alive.
1530 // But in the rare case of a "floating" QObject without a parent that
1531 // _gets_ marked (we've been called here!) then we also need to
1532 // propagate the marking down to the children recursively.
1533 if (!o->parent())
1535 });
1536
1538 if (!ddata)
1539 return;
1540
1541 if (ddata->hasVMEMetaObject) {
1542 if (auto *vme = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) {
1543 vme->mark(markStack);
1546 }
1547 }
1548
1550 return;
1551
1552 // mark the const wrapper if our engine has interacted with it at some point
1555 scope, scope.engine->m_multiplyWrappedQObjects->value(static_cast<const QObject *>(o)));
1556
1557 if (!constWrapper)
1558 return;
1559
1560 if (that != constWrapper->d()) {
1561 // We've got the non-const wrapper. Also mark the const one.
1563 return;
1564 }
1565
1566 // We've got the const wrapper. Also mark the non-const one
1569 else
1571}
1572
1574{
1575 Heap::QObjectWrapper *h = d();
1577
1578 if (QObject *o = h->object()) {
1579 QQmlData *ddata = QQmlData::get(o, false);
1580 if (ddata) {
1581 if (!o->parent() && !ddata->indestructible) {
1582 if (ddata && ddata->ownContext) {
1586 ddata->context = nullptr;
1587 }
1588
1589 // This object is notionally destroyed now. It might still live until the next
1590 // event loop iteration, but it won't need its connections, CU, or deferredData
1591 // anymore.
1592
1593 ddata->isQueuedForDeletion = true;
1596
1599
1600 if (lastCall)
1601 delete o;
1602 else
1603 o->deleteLater();
1604 } else {
1605 // If the object is C++-owned, we still have to release the weak reference we have
1606 // to it. If the "main" wrapper is not ours, we should leave it alone, though.
1607 if (ddata->jsWrapper.as<QObjectWrapper>() == this)
1609 }
1610 }
1611 }
1612
1613 h->destroy();
1614}
1615
1616
1618
1619template<typename Retrieve>
1620int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
1621 if (conversionMetaType == QMetaType::fromType<QVariant>())
1622 return 0;
1623
1624 const QMetaType type = retrieve();
1625 if (type == conversionMetaType)
1626 return 0;
1627
1628 if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
1629 if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject))
1630 return 1;
1631 }
1632
1633 if (QMetaType::canConvert(type, conversionMetaType)) {
1634 if (conversionMetaType == QMetaType::fromType<QJSValue>()
1635 || conversionMetaType == QMetaType::fromType<double>()
1636 || conversionMetaType == QMetaType::fromType<QString>()) {
1637 // Unspecific conversions receive lower score. You can convert anything
1638 // to QString or double via toString() and valueOf(), respectively.
1639 // And anything can be wrapped into QJSValue, but that's inefficient.
1640 return 6;
1641 }
1642
1643 // We have an explicitly defined conversion method to a non-boring type.
1644 return 5;
1645 }
1646
1647 return 10;
1648};
1649
1650/*
1651 Returns the match score for converting \a actual to be of type \a conversionType. A
1652 zero score means "perfect match" whereas a higher score is worse.
1653
1654 The conversion table is copied out of the \l QScript::callQtMethod() function.
1655*/
1656static int MatchScore(const Value &actual, QMetaType conversionMetaType)
1657{
1658 const int conversionType = conversionMetaType.id();
1659 const auto convertibleScore = [&](QMetaType actualType) {
1660 // There are a number of things we can do in JavaScript to subvert this, but
1661 // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
1662 if (!QMetaType::canConvert(actualType, conversionMetaType))
1663 return 10;
1664
1665 // You can convert anything to QJSValue, but that's inefficient.
1666 // If we have a better option, we should use it.
1667 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1668 return 9;
1669
1670 // You can also convert anything to QVariant, but that's also suboptimal.
1671 // You can convert anything to string or double via toString() and valueOf().
1672 // Those are also rather unspecific.
1673 switch (conversionType) {
1674 case QMetaType::QVariant:
1675 case QMetaType::Double:
1676 case QMetaType::QString:
1677 return 9;
1678 default:
1679 break;
1680 }
1681
1682 // We have an explicitly defined conversion method to a non-boring type.
1683 return 8;
1684 };
1685
1686 if (actual.isNumber()) {
1687 switch (conversionType) {
1688 case QMetaType::Double:
1689 return 0;
1690 case QMetaType::Float:
1691 return 1;
1692 case QMetaType::LongLong:
1693 case QMetaType::ULongLong:
1694 return 2;
1695 case QMetaType::Long:
1696 case QMetaType::ULong:
1697 return 3;
1698 case QMetaType::Int:
1699 case QMetaType::UInt:
1700 return 4;
1701 case QMetaType::Short:
1702 case QMetaType::UShort:
1703 return 5;
1704 break;
1705 case QMetaType::Char:
1706 case QMetaType::UChar:
1707 return 6;
1708 case QMetaType::QJsonValue:
1709 return 5;
1710 default:
1711 return convertibleScore(actual.isInteger()
1712 ? QMetaType::fromType<int>()
1713 : QMetaType::fromType<double>());
1714 }
1715 } else if (actual.isString()) {
1716 switch (conversionType) {
1717 case QMetaType::QString:
1718 return 0;
1719 case QMetaType::QJsonValue:
1720 return 5;
1721 case QMetaType::QUrl:
1722 return 6; // we like to convert strings to URLs in QML
1723 case QMetaType::Double:
1724 case QMetaType::Float:
1725 case QMetaType::LongLong:
1726 case QMetaType::ULongLong:
1727 case QMetaType::Int:
1728 case QMetaType::UInt:
1729 case QMetaType::Short:
1730 case QMetaType::UShort:
1731 case QMetaType::Char:
1732 case QMetaType::UChar:
1733 // QMetaType can natively convert strings to numbers.
1734 // However, in the general case it's of course extremely lossy.
1735 return 10;
1736 default:
1737 return convertibleScore(QMetaType::fromType<QString>());
1738 }
1739 } else if (actual.isBoolean()) {
1740 switch (conversionType) {
1741 case QMetaType::Bool:
1742 return 0;
1743 case QMetaType::QJsonValue:
1744 return 5;
1745 default:
1746 return convertibleScore(QMetaType::fromType<bool>());
1747 }
1748 } else if (actual.as<DateObject>()) {
1749 switch (conversionType) {
1750 case QMetaType::QDateTime:
1751 return 0;
1752 case QMetaType::QDate:
1753 return 1;
1754 case QMetaType::QTime:
1755 return 2;
1756 default:
1757 return convertibleScore(QMetaType::fromType<QDateTime>());
1758 }
1759 } else if (actual.as<RegExpObject>()) {
1760 switch (conversionType) {
1761#if QT_CONFIG(regularexpression)
1762 case QMetaType::QRegularExpression:
1763 return 0;
1764 default:
1765 return convertibleScore(QMetaType::fromType<QRegularExpression>());
1766#else
1767 default:
1768 return convertibleScore(QMetaType());
1769#endif
1770 }
1771 } else if (actual.as<ArrayBuffer>()) {
1772 switch (conversionType) {
1773 case QMetaType::QByteArray:
1774 return 0;
1775 default:
1776 return convertibleScore(QMetaType::fromType<QByteArray>());
1777 }
1778 } else if (actual.as<ArrayObject>()) {
1779 switch (conversionType) {
1780 case QMetaType::QJsonArray:
1781 return 3;
1782 case QMetaType::QStringList:
1783 case QMetaType::QVariantList:
1784 return 5;
1785 case QMetaType::QVector4D:
1786 case QMetaType::QMatrix4x4:
1787 return 6;
1788 case QMetaType::QVector3D:
1789 return 7;
1790 default:
1791 return convertibleScore(QMetaType());
1792 }
1793 } else if (actual.isNull()) {
1794 switch (conversionType) {
1795 case QMetaType::Nullptr:
1796 case QMetaType::VoidStar:
1797 case QMetaType::QObjectStar:
1798 case QMetaType::QJsonValue:
1799 return 0;
1800 default: {
1801 if (conversionMetaType.flags().testFlag(QMetaType::IsPointer))
1802 return 0;
1803 else
1804 return convertibleScore(QMetaType());
1805 }
1806 }
1807 } else if (const Object *obj = actual.as<Object>()) {
1808 if (const VariantObject *variantObject = obj->as<VariantObject>()) {
1809 return MatchVariant(conversionMetaType, [variantObject]() {
1810 return variantObject->d()->data().metaType();
1811 });
1812 }
1813
1814 if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
1815 switch (conversionType) {
1816 case QMetaType::QObjectStar:
1817 return 0;
1818 default:
1819 if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
1820 QObject *wrapped = wrapper->object();
1821 if (!wrapped)
1822 return 0;
1823 if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject()))
1824 return 0;
1825 }
1826 }
1827
1828 return convertibleScore(QMetaType::fromType<QObject *>());
1829 }
1830
1831 if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
1832 const QQmlType type = wrapper->d()->type();
1833 if (type.isSingleton()) {
1834 const QMetaType metaType = type.typeId();
1835 if (metaType == conversionMetaType)
1836 return 0;
1837
1838 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1839 && metaType.flags() & QMetaType::PointerToQObject
1840 && type.metaObject()->inherits(conversionMetaType.metaObject())) {
1841 return 0;
1842 }
1843 } else if (QObject *object = wrapper->object()) {
1844 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1845 && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) {
1846 return 0;
1847 }
1848 }
1849
1850 return convertibleScore(QMetaType());
1851 }
1852
1853 if (const Sequence *sequence = obj->as<Sequence>()) {
1854 const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(sequence);
1855 if (sequenceType == conversionMetaType)
1856 return 1;
1857
1858 return convertibleScore(sequenceType);
1859 }
1860
1861 if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
1862 return MatchVariant(conversionMetaType, [wrapper]() {
1863 return wrapper->d()->isVariant()
1864 ? wrapper->toVariant().metaType()
1865 : wrapper->type();
1866 });
1867 }
1868
1869 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1870 return 0;
1871
1872 switch (conversionType) {
1873 case QMetaType::QJsonObject:
1874 case QMetaType::QVariantMap:
1875 return 5;
1876 default:
1877 break;
1878 }
1879
1880 }
1881
1882 return convertibleScore(QMetaType());
1883}
1884
1885static int numDefinedArguments(CallData *callArgs)
1886{
1887 int numDefinedArguments = callArgs->argc();
1888 while (numDefinedArguments > 0
1889 && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) {
1890 --numDefinedArguments;
1891 }
1892 return numDefinedArguments;
1893}
1894
1895static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
1896{
1897 const QMetaObject *metaObject = object.metaObject();
1898 const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments");
1899 return indexOfClassInfo != -1
1900 && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true");
1901}
1902
1906{
1907 const auto constructReturnValue = [&](QMetaType returnType, void *returnValue) {
1908 // If we construct a value in place, we mustn't initialize the memory before that.
1911 };
1912
1913 const auto convertReturnValue
1915
1916 // In contrast to other invocations of convertAndCall() we do not want to create
1917 // a URL object from a QUrl here like metaTypeFromJS does. This is for compatibility.
1918 // URL objects are proper, specified, objects that behave different from our variant
1919 // objects when it comes to equality comparisons.
1921 ? engine->fromVariant(*reinterpret_cast<const QVariant *>(returnValue))
1923
1924 const auto typeFlags = returnType.flags();
1927 } else if (typeFlags & QMetaType::PointerToQObject) {
1928 // We consider QObjects returned from invokables as owned by the QML engine unless
1929 // explicitly configured otherwise.
1930 if (QObject *object = *reinterpret_cast<QObject **>(returnValue))
1932 }
1933 return result;
1934 };
1935
1936 const auto conversionErrorHandler = [&](QMetaType argumentType, void *argument, int ii) {
1937 // We've checked the number of arguments before
1938 Q_ASSERT(ii < callArgs->argc());
1939
1941 // We have an engine here. So we can conjure up a QJSManagedValue for anything.
1942 *static_cast<QJSManagedValue *>(argument)
1944 return true;
1945 }
1946
1948 && callArgs->args[ii].isUndefined()) {
1949 // TODO: QObjectMethod silently converts undefined to null when you pass undefined
1950 // to a method that accepts some QObject-derived. This is wrong because
1951 // undefined is certainly not null. We accept it for compatibility.
1952 *static_cast<QObject **>(argument) = nullptr;
1953 return true;
1954 }
1955
1956 // TODO: QObjectMethod is allowed to use QVariant conversion.
1957 // This is wrong because we can't see the converters at compile time.
1958 // It only exists for compatibility with old versions of Qt.
1962 return true;
1963
1964 qWarning() << QString::fromLatin1("Could not convert argument %1 from %2 to %3")
1965 .arg(ii)
1967 .arg(argumentType.name());
1968 const StackTrace stack = engine->stackTrace();
1969 for (const StackFrame &frame : stack) {
1970 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source + (frame.line > 0
1971 ? (QLatin1Char(':') + QString::number(frame.line))
1972 : QString());
1973
1974 }
1975
1977 qWarning() << "Passing incompatible arguments to signals is not supported.";
1979 }
1980
1982 "Passing incompatible arguments to C++ functions from JavaScript is not allowed."));
1983 return false;
1984 };
1985
1987
1988 const auto handleTooManyArguments = [&](int expectedArguments) {
1990 engine->throwError(QStringLiteral("Too many arguments"));
1991 return false;
1992 }
1993
1994 const auto stackTrace = engine->stackTrace();
1995 if (stackTrace.isEmpty()) {
1997 << "When matching arguments for "
1998 << object.className() << "::" << data.name(object.metaObject()) << "():";
1999 } else {
2000 const StackFrame frame = stackTrace.first();
2002 + (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
2003 : QString());
2004 }
2005
2006 qWarning().noquote() << QStringLiteral("Too many arguments, ignoring %1")
2008 return true;
2009 };
2010
2012
2013 if (data.hasArguments()) {
2015
2016 const bool ok = data.isConstructor()
2021 if (!ok)
2023
2024 const int expectedArgumentCount = storage.size() - 1;
2026 return engine->throwError(QLatin1String("Insufficient arguments"));
2027
2030 return Encode::undefined();
2031 }
2032
2033 return QV4::convertAndCall(
2036 [&](void **argData, const QMetaType *types, int argc) {
2037 Q_UNUSED(types);
2038 Q_UNUSED(argc);
2041 }
2042
2044 return Encode::undefined();
2045
2047 if (!returnType.isValid())
2049
2050 return QV4::convertAndCall(
2051 engine, &returnType, 1, nullptr, 0,
2052 [&](void **argData, const QMetaType *types, int argc) {
2053 Q_UNUSED(types);
2054 Q_UNUSED(argc);
2057}
2058
2059/*
2060Resolve the overloaded method to call. The algorithm works conceptually like this:
2061 1. Resolve the set of overloads it is *possible* to call.
2062 Impossible overloads include those that have too many parameters or have parameters
2063 of unknown type.
2064 2. Filter the set of overloads to only contain those with the closest number of
2065 parameters.
2066 For example, if we are called with 3 parameters and there are 2 overloads that
2067 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
2068 3. Find the best remaining overload based on its match score.
2069 If two or more overloads have the same match score, return the last one. The match
2070 score is constructed by adding the matchScore() result for each of the parameters.
2071*/
2075{
2076 const int argumentCount = callArgs->argc();
2078
2079 const QQmlPropertyData *best = nullptr;
2083
2086
2087 for (int i = 0; i < methodCount; ++i) {
2088 const QQmlPropertyData *attempt = methods + i;
2089
2095 qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
2096 }
2097
2098 // QQmlV4Function overrides anything that doesn't provide the exact number of arguments
2099 int methodParameterScore = 1;
2100 // QQmlV4Function overrides the "no idea" option, which is 10
2101 int maxMethodMatchScore = 9;
2102 // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments
2104
2105 if (!attempt->isV4Function()) {
2107 int methodArgumentCount = 0;
2108 if (attempt->hasArguments()) {
2109 if (attempt->isConstructor()) {
2110 if (!object.constructorParameterTypes(*attempt, &storage, nullptr)) {
2111 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2112 continue;
2113 }
2114 } else {
2115 if (!object.methodParameterTypes(*attempt, &storage, nullptr)) {
2116 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2117 continue;
2118 }
2119 }
2121 }
2122
2124 qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
2125 continue; // We don't have sufficient arguments to call this method
2126 }
2127
2129 ? 0
2132 qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
2133 continue; // We already have a better option
2134 }
2135
2138 for (int ii = 0; ii < methodArgumentCount; ++ii) {
2139 const int score = MatchScore((v = Value::fromStaticValue(callArgs->args[ii])),
2140 storage[ii]);
2143 }
2144 }
2145
2150 best = attempt;
2154 qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
2155 << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
2156 << "bestSumMatchScore" << bestSumMatchScore << "\n";
2157 } else {
2158 qCDebug(lcOverloadResolution) << "did not update best\n"
2159 << "bestParameterScore" << bestParameterScore << "\t"
2160 << "methodParameterScore" << methodParameterScore << "\n"
2161 << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
2162 << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
2163 << "bestSumMatchScore" << bestSumMatchScore << "\t"
2164 << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
2165 }
2166
2167 if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
2168 qCDebug(lcOverloadResolution, "perfect match");
2169 break; // We can't get better than that
2170 }
2171
2172 };
2173
2174 if (best && best->isValid()) {
2175 return best;
2176 } else {
2177 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
2178 for (int i = 0; i < methodCount; ++i) {
2183 error += u"\n " + QString::fromUtf8(m.methodSignature());
2184 }
2185
2187 return nullptr;
2188 }
2189}
2190
2191static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
2192{
2193 if (required == QMetaType::fromType<QVariant>()
2194 || required == QMetaType::fromType<QJSValue>()
2195 || required == QMetaType::fromType<QJSManagedValue>()) {
2196 return true;
2197 }
2198
2199 if (data) {
2200 if (passed == QMetaType::fromType<QVariant>())
2201 passed = static_cast<const QVariant *>(data)->metaType();
2202 else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
2203 passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
2204 }
2205
2206 if (passed == required)
2207 return true;
2208
2209 if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
2210 switch (passed.id()) {
2211 case QMetaType::UnknownType:
2212 case QMetaType::Nullptr:
2213 case QMetaType::Bool:
2214 case QMetaType::Int:
2215 case QMetaType::Double:
2216 case QMetaType::QString:
2217 return true;
2218 default:
2219 break;
2220 }
2221 }
2222
2223 return false;
2224}
2225
2227 const QMetaMethod &method, void **argv, int argc, const QMetaType *types)
2228{
2229 if (types[0].isValid() && !ExactMatch(method.returnMetaType(), types[0], nullptr))
2230 return false;
2231
2232 if (method.parameterCount() != argc)
2233 return false;
2234
2235 for (int i = 0; i < argc; ++i) {
2236 if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1]))
2237 return false;
2238 }
2239
2240 return true;
2241}
2242
2245 void **argv, int argc, const QMetaType *types)
2246{
2247 // We only accept exact matches here. Everything else goes through the JavaScript conversion.
2248 for (int i = 0; i < methodCount; ++i) {
2249 const QQmlPropertyData *attempt = methods + i;
2251 return attempt;
2252 }
2253
2254 return nullptr;
2255}
2256
2265
2275
2279{
2281
2283 if (cloneFrom->wrapper) {
2285 if (ref) {
2287 } else {
2288 // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what
2289 // value we should operate on. Without knowledge of the property the value
2290 // was read from, we cannot load the value from the given object.
2291 return Encode::undefined();
2292 }
2293 }
2294
2296 valueScope,
2299
2301
2302 Q_ASSERT(method->d()->methods == nullptr);
2303 switch (cloneFrom->methodCount) {
2304 case 0:
2305 Q_ASSERT(cloneFrom->methods == nullptr);
2306 break;
2307 case 1:
2309 == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
2310 method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
2311 *method->d()->methods = *cloneFrom->methods;
2312 break;
2313 default:
2314 Q_ASSERT(cloneFrom->methods != nullptr);
2318 break;
2319 }
2320
2321 return method.asReturnedValue();
2322}
2323
2325{
2329}
2330
2332{
2334
2336 return valueWrapper->metaObject();
2337 if (QObject *self = object())
2338 return self->metaObject();
2339
2340 return nullptr;
2341}
2342
2344{
2346
2348 return objectWrapper->object();
2350 return typeWrapper->object();
2351 return nullptr;
2352}
2353
2354bool Heap::QObjectMethod::isDetached() const
2355{
2356 if (!wrapper)
2357 return true;
2358
2361 return valueWrapper->d()->object() == nullptr;
2362
2363 return false;
2364}
2365
2367{
2370 return objectWrapper->object() == o;
2372 return typeWrapper->object() == o;
2373
2377 return qobject->object() == o;
2379 return type->object() == o;
2380
2381 // Attached to some nested value type or sequence object
2382 return false;
2383 }
2384
2385 return false;
2386}
2387
2389 const QMetaObject *thisMeta) const
2390{
2391 // Check that the metaobject matches.
2392
2393 if (!thisMeta) {
2394 // You can only get a detached method via a lookup, and then you have a thisObject.
2396 return Included;
2397 }
2398
2399 const auto check = [&](const QMetaObject *included) {
2404 "%s:%d: Calling C++ methods with 'this' objects different from the one "
2405 "they were retrieved from is broken, due to historical reasons. The "
2406 "original object is used as 'this' object. You can allow the given "
2407 "'this' object to be used by setting "
2408 "'pragma NativeMethodBehavior: AcceptThisObject'",
2410 return Included;
2411 }
2412
2413 // destroy() and toString() can be called on all QObjects, but not on gadgets.
2414 if (index < 0)
2416
2417 // Find the base type the method belongs to.
2419 while (true) {
2420 if (included == thisMeta)
2421 return Explicit;
2422
2423 if (methodOffset <= index)
2425
2429 };
2430
2432 };
2433
2434 if (const QMetaObject *meta = metaObject())
2435 return check(meta);
2436
2437 // If the QObjectMethod is detached, we can only have gotten here via a lookup.
2438 // The lookup checks that the QQmlPropertyCache matches.
2439 return Explicit;
2440}
2441
2443{
2445 return QStringLiteral("destroy");
2446 else if (index == QV4::QObjectMethod::ToStringMethod)
2447 return QStringLiteral("toString");
2448
2449 const QMetaObject *mo = metaObject();
2450 if (!mo)
2451 return QString();
2452
2453 int methodOffset = mo->methodOffset();
2454 while (methodOffset > index) {
2455 mo = mo->superClass();
2457 }
2458
2459 return "%1::%2"_L1.arg(QLatin1StringView{mo->className()},
2461}
2462
2464{
2465 if (methods) {
2466 Q_ASSERT(methodCount > 0);
2467 return;
2468 }
2469
2470 const QMetaObject *mo = metaObject();
2471
2472 if (!mo)
2473 mo = thisMeta;
2474
2475 Q_ASSERT(mo);
2476
2477 int methodOffset = mo->methodOffset();
2478 while (methodOffset > index) {
2479 mo = mo->superClass();
2481 }
2485 dummy.load(method);
2488 // Look for overloaded methods
2490 for (int ii = index - 1; ii >= methodOffset; --ii) {
2491 if (methodName == mo->method(ii).name()) {
2492 method = mo->method(ii);
2493 dummy.load(method);
2495 }
2496 }
2497 if (resolvedMethods.size() > 1) {
2501 } else {
2502 methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod);
2504 methodCount = 1;
2505 }
2506
2507 Q_ASSERT(methodCount > 0);
2508}
2509
2516
2518 ExecutionEngine *engine, QObject *o, const Value *args, int argc) const
2519{
2520 method_destroy(engine, o, argc > 0 ? args[0].toInt32() : 0);
2521 return Encode::undefined();
2522}
2523
2525{
2526 if (!o)
2527 return true;
2528
2530 engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2531 return false;
2532 }
2533
2534 if (delay > 0)
2536 else
2537 o->deleteLater();
2538
2539 return true;
2540}
2541
2543 const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2544{
2545 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2547}
2548
2550 const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
2551{
2552 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2554}
2555
2557{
2559
2560 const QMetaObject *thisMeta = nullptr;
2561
2562 QObject *o = nullptr;
2564 if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
2565 thisMeta = w->metaObject();
2566 o = w->object();
2567 } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
2568 thisMeta = w->metaObject();
2569 o = w->object();
2570 } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
2571 thisMeta = w->metaObject();
2572 valueWrapper = w->d();
2573 }
2574
2576 if (o && o == d()->object()) {
2578 // Nothing to do; objects are the same. This should be common
2579 } else if (valueWrapper && valueWrapper == d()->wrapper) {
2581 // Nothing to do; gadgets are the same. This should be somewhat common
2582 } else {
2584 if (mode == Heap::QObjectMethod::Invalid) {
2585 v4->throwError(QLatin1String("Cannot call method %1 on %2").arg(
2586 d()->name(), thisObject->toQStringNoThrow()));
2587 return Encode::undefined();
2588 }
2589 }
2590
2591 QQmlObjectOrGadget object = [&](){
2592 if (mode == Heap::QObjectMethod::Included) {
2593 QV4::Scope scope(v4);
2597 return QQmlObjectOrGadget(type->object());
2599 valueWrapper = value->d();
2601 }
2602 Q_UNREACHABLE();
2603 } else {
2604 if (o)
2605 return QQmlObjectOrGadget(o);
2606
2611 }
2612 }();
2613
2614 if (object.isNull())
2615 return Encode::undefined();
2616
2617 if (d()->index == DestroyMethod)
2618 return method_destroy(v4, object.qObject(), argv, argc);
2619 else if (d()->index == ToStringMethod)
2620 return method_toString(v4, object.qObject());
2621
2623
2624 Scope scope(v4);
2627
2628 const QQmlPropertyData *method = d()->methods;
2629
2630 // If we call the method, we have to write back any value type references afterwards.
2631 // The method might change the value.
2632 const auto doCall = [&](const auto &call) {
2633 if (!method->isConstant()) {
2637 return rv->asReturnedValue();
2638 }
2639 }
2640
2641 return call();
2642 };
2643
2644 if (d()->methodCount != 1) {
2645 Q_ASSERT(d()->methodCount > 0);
2647 if (method == nullptr)
2648 return Encode::undefined();
2649 }
2650
2651 if (method->isV4Function()) {
2652 return doCall([&]() {
2656
2657 void *args[] = { nullptr, &funcptr };
2659
2660 return rv->asReturnedValue();
2661 });
2662 }
2663
2664 return doCall([&]() { return callPrecise(object, *method, v4, callData); });
2665}
2666
2668{
2669 constexpr int parameterCount() const { return 0; }
2670 constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
2671 constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
2672};
2673
2675 QObject *thisObject, void **argv, const QMetaType *types, int argc) const
2676{
2678
2679 const QMetaObject *thisMeta = nullptr;
2681
2682 if (thisObject) {
2684 } else {
2688 }
2689
2690 QQmlObjectOrGadget object = [&](){
2691 if (thisObject)
2693
2694 Scope scope(v4);
2697
2702 }();
2703
2704 if (object.isNull())
2705 return;
2706
2707 if (d()->index == DestroyMethod) {
2708 // method_destroy will use at most one argument
2710 v4, thisObject, argv, types, std::min(argc, 1),
2711 [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
2713 return method_destroy(v4, object.qObject(), argv, argc);
2714 });
2715 return;
2716 }
2717
2718 if (d()->index == ToStringMethod) {
2722 [v4, thisMeta, object](void **argv, int) {
2723 if (!argv[0])
2724 return;
2725 *static_cast<QString *>(argv[0])
2727 });
2728 return;
2729 }
2730
2732
2733 const QQmlPropertyData *method = d()->methods;
2734 if (d()->methodCount != 1) {
2735 Q_ASSERT(d()->methodCount > 0);
2737 }
2738
2739 if (!method || method->isV4Function()) {
2742 [this](const Value *thisObject, const Value *argv, int argc) {
2743 return callInternal(thisObject, argv, argc);
2744 });
2745 } else {
2749 [v4, object, valueWrapper, method](void **argv, int argc) {
2750 Q_UNUSED(argc);
2751
2752 // If we call the method, we have to write back any value type references afterwards.
2753 // The method might change the value.
2755 if (!method->isConstant()) {
2758 }
2759
2760 // If the method returns a QObject* we need to track it on the JS heap
2761 // (if it's destructible).
2762 QObject *qobjectPtr = nullptr;
2764 if (argv[0]) {
2766 qobjectPtr = *static_cast<QObject **>(argv[0]);
2767 } else if (resultType == QMetaType::fromType<QVariant>()) {
2768 const QVariant *result = static_cast<const QVariant *>(argv[0]);
2771 qobjectPtr = *static_cast<QObject *const *>(result->data());
2772 }
2773 }
2774
2775 if (qobjectPtr) {
2778 ddata->indestructible = false;
2780 }
2781 }
2782 });
2783 }
2784}
2785
2787
2788void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
2789{
2790 Object::init();
2791 this->signalIndex = signalIndex;
2792 setObject(object);
2793}
2794
2796
2798{
2802 << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
2803 "not call it directly. Make it a proper function and call "
2804 "that or emit the signal.")
2806
2807 Scope scope(engine());
2810 scope.engine,
2811 static_cast<Heap::QObjectWrapper *>(nullptr),
2812 signalIndex()));
2813
2814 return method->call(thisObject, argv, argc);
2815}
2816
2831
2832
2835{
2836 const QObjectBiPointer key = it.key();
2837 const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
2838 disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject);
2839 return QHash<QObjectBiPointer, WeakValue>::erase(it);
2840}
2841
2842void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
2843{
2844 QHash<QObjectBiPointer, WeakValue>::remove(object);
2845 QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object));
2846}
2847
2848} // namespace QV4
2849
2850QT_END_NAMESPACE
2851
2852#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 void recordObjectForCompilationUnits(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 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