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
4#include "qv4qobjectwrapper_p.h"
5
6#include <private/qjsvalue_p.h>
7
8#include <private/qqmlbinding_p.h>
9#include <private/qqmlbuiltinfunctions_p.h>
10#include <private/qqmlengine_p.h>
11#include <private/qqmlobjectorgadget_p.h>
12#include <private/qqmlpropertybinding_p.h>
13#include <private/qqmlscriptstring_p.h>
14#include <private/qqmlsignalnames_p.h>
15#include <private/qqmltypewrapper_p.h>
16#include <private/qqmlvaluetypewrapper_p.h>
17#include <private/qqmlvmemetaobject_p.h>
18
19#include <private/qv4arraybuffer_p.h>
20#include <private/qv4compileddata_p.h>
21#include <private/qv4dateobject_p.h>
22#include <private/qv4functionobject_p.h>
23#include <private/qv4identifiertable_p.h>
24#include <private/qv4jscall_p.h>
25#include <private/qv4jsonobject_p.h>
26#include <private/qv4lookup_p.h>
27#include <private/qv4mm_p.h>
28#include <private/qv4regexpobject_p.h>
29#include <private/qv4runtime_p.h>
30#include <private/qv4scopedvalue_p.h>
31#include <private/qv4sequenceobject_p.h>
32#include <private/qv4variantobject_p.h>
33
34#include <QtCore/qjsonarray.h>
35#include <QtCore/qjsonobject.h>
36#include <QtCore/qjsonvalue.h>
37#include <QtCore/qloggingcategory.h>
38#include <QtCore/qmetaobject.h>
39#include <QtCore/qqueue.h>
40#include <QtCore/qtimer.h>
41#include <QtCore/qtypes.h>
42#include <QtCore/qvarlengtharray.h>
43
44#include <vector>
45
46#if QT_CONFIG(qml_itemmodel)
47#include <QtCore/qabstractitemmodel.h>
48#endif
49
50QT_BEGIN_NAMESPACE
51
52Q_LOGGING_CATEGORY(lcBuiltinsBindingRemoval, "qt.qml.binding.removal", QtWarningMsg)
53Q_LOGGING_CATEGORY(lcObjectConnect, "qt.qml.object.connect", QtWarningMsg)
54Q_LOGGING_CATEGORY(lcOverloadResolution, "qt.qml.overloadresolution", QtWarningMsg)
55Q_LOGGING_CATEGORY(lcMethodBehavior, "qt.qml.method.behavior")
56Q_LOGGING_CATEGORY(lcSignalHandler, "qt.qml.signalhandler")
57
58// The code in this file does not violate strict aliasing, but GCC thinks it does
59// so turn off the warnings for us to have a clean build
60QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
61
62using namespace Qt::StringLiterals;
63
64namespace QV4 {
65
66QPair<QObject *, int> QObjectMethod::extractQtMethod(const FunctionObject *function)
67{
68 ExecutionEngine *v4 = function->engine();
69 if (v4) {
70 Scope scope(v4);
71 Scoped<QObjectMethod> method(scope, function->as<QObjectMethod>());
72 if (method)
73 return qMakePair(value1: method->object(), value2: method->methodIndex());
74 }
75
76 return qMakePair(value1: (QObject *)nullptr, value2: -1);
77}
78
79static QPair<QObject *, int> extractQtSignal(const Value &value)
80{
81 if (value.isObject()) {
82 ExecutionEngine *v4 = value.as<Object>()->engine();
83 Scope scope(v4);
84 ScopedFunctionObject function(scope, value);
85 if (function)
86 return QObjectMethod::extractQtMethod(function);
87
88 Scoped<QmlSignalHandler> handler(scope, value);
89 if (handler)
90 return qMakePair(value1: handler->object(), value2: handler->signalIndex());
91 }
92
93 return qMakePair(value1: (QObject *)nullptr, value2: -1);
94}
95
96static Heap::ReferenceObject::Flags referenceFlags(
97 ExecutionEngine *v4,
98 const QQmlPropertyData &property)
99{
100 Heap::ReferenceObject::Flags flags = Heap::ReferenceObject::NoFlag;
101 if (CppStackFrame *stackFrame = v4->currentStackFrame) {
102 if (stackFrame->v4Function->executableCompilationUnit()->valueTypesAreCopied())
103 flags |= Heap::ReferenceObject::EnforcesLocation;
104 }
105
106 if (property.isWritable())
107 flags |= Heap::ReferenceObject::CanWriteBack;
108
109 return flags;
110}
111
112static ReturnedValue loadProperty(
113 ExecutionEngine *v4, Heap::Object *wrapper,
114 QObject *object, const QQmlPropertyData &property)
115{
116 Q_ASSERT(!property.isFunction());
117 Scope scope(v4);
118
119 const QMetaType propMetaType = property.propType();
120 if (property.isQObject()) {
121 QObject *rv = nullptr;
122 property.readProperty(target: object, property: &rv);
123 if (propMetaType.flags().testFlag(flag: QMetaType::IsConst))
124 return QObjectWrapper::wrapConst(engine: v4, object: rv);
125 else
126 return QObjectWrapper::wrap(engine: v4, object: rv);
127 }
128
129 if (property.isQList() && propMetaType.flags().testFlag(flag: QMetaType::IsQmlList))
130 return QmlListWrapper::create(engine: v4, object, propId: property.coreIndex(), propType: propMetaType);
131
132 const auto encodeSimple = [&](auto v) {
133 property.readProperty(target: object, property: &v);
134 return Encode(v);
135 };
136
137 const auto encodeInt = [&](auto v) {
138 property.readProperty(target: object, property: &v);
139 return Encode(int(v));
140 };
141
142 const auto encodeDouble = [&](auto v) {
143 property.readProperty(target: object, property: &v);
144 return Encode(double(v));
145 };
146
147 const auto encodeDate = [&](auto v) {
148 property.readProperty(target: object, property: &v);
149 return Encode(v4->newDateObject(
150 v, wrapper, property.coreIndex(), referenceFlags(v4: scope.engine, property)));
151 };
152
153 const auto encodeString = [&](auto v) {
154 property.readProperty(target: object, property: &v);
155 return v4->newString(v)->asReturnedValue();
156 };
157
158 const auto encodeSequence = [&](QMetaSequence metaSequence) {
159 // Pass nullptr as data. It's lazy-loaded.
160 return QV4::SequencePrototype::newSequence(
161 engine: v4, type: propMetaType, metaSequence, data: nullptr,
162 object: wrapper, propertyIndex: property.coreIndex(), flags: referenceFlags(v4: scope.engine, property));
163 };
164
165
166 switch (property.isEnum() ? propMetaType.underlyingType().id() : propMetaType.id()) {
167 case QMetaType::UnknownType:
168 case QMetaType::Void:
169 return Encode::undefined();
170 case QMetaType::Nullptr:
171 case QMetaType::VoidStar:
172 return Encode::null();
173 case QMetaType::Int:
174 return encodeSimple(int());
175 case QMetaType::Bool:
176 return encodeSimple(bool());
177 case QMetaType::QString:
178 return encodeString(QString());
179 case QMetaType::QByteArray: {
180 QByteArray v;
181 property.readProperty(target: object, property: &v);
182 return v4->newArrayBuffer(array: v)->asReturnedValue();
183 }
184 case QMetaType::QChar:
185 return encodeString(QChar());
186 case QMetaType::Char16:
187 return encodeString(char16_t());
188 case QMetaType::UInt:
189 return encodeSimple(uint());
190 case QMetaType::Float:
191 return encodeSimple(float());
192 case QMetaType::Double:
193 return encodeSimple(double());
194 case QMetaType::Short:
195 return encodeInt(short());
196 case QMetaType::UShort:
197 return encodeInt(ushort());
198 case QMetaType::Char:
199 return encodeInt(char());
200 case QMetaType::UChar:
201 return encodeInt(uchar());
202 case QMetaType::SChar:
203 return encodeInt(qint8());
204 case QMetaType::Long:
205 return encodeDouble(long());
206 case QMetaType::ULong:
207 return encodeDouble(ulong());
208 case QMetaType::LongLong:
209 return encodeDouble(qlonglong());
210 case QMetaType::ULongLong:
211 return encodeDouble(qulonglong());
212 case QMetaType::QDateTime:
213 return encodeDate(QDateTime());
214 case QMetaType::QDate:
215 return encodeDate(QDate());
216 case QMetaType::QTime:
217 return encodeDate(QTime());
218#if QT_CONFIG(regularexpression)
219 case QMetaType::QRegularExpression: {
220 QRegularExpression v;
221 property.readProperty(target: object, property: &v);
222 return Encode(v4->newRegExpObject(re: v));
223 }
224#endif
225 case QMetaType::QVariantMap: {
226 QVariantMap v;
227 property.readProperty(target: object, property: &v);
228 return scope.engine->fromData(
229 type: propMetaType, ptr: &v, parent: wrapper, property: property.coreIndex(), flags: referenceFlags(v4, property));
230 }
231 case QMetaType::QJsonValue: {
232 QJsonValue v;
233 property.readProperty(target: object, property: &v);
234 return QV4::JsonObject::fromJsonValue(engine: v4, value: v);
235 }
236 case QMetaType::QJsonObject: {
237 QJsonObject v;
238 property.readProperty(target: object, property: &v);
239 return QV4::JsonObject::fromJsonObject(engine: v4, object: v);
240 }
241 case QMetaType::QJsonArray:
242 return encodeSequence(QMetaSequence::fromContainer<QJsonArray>());
243 case QMetaType::QStringList:
244 return encodeSequence(QMetaSequence::fromContainer<QStringList>());
245 case QMetaType::QVariantList:
246 return encodeSequence(QMetaSequence::fromContainer<QVariantList>());
247 case QMetaType::QUrl: {
248 // ### Qt7: We really want this to be a JS URL object, but that would break things.
249 QUrl v;
250 property.readProperty(target: object, property: &v);
251 return Encode(v4->newVariantObject(type: propMetaType, data: &v));
252 }
253 case QMetaType::QPixmap:
254 case QMetaType::QImage: {
255 // Scarce value types
256 QVariant v(propMetaType);
257 property.readProperty(target: object, property: v.data());
258 return Encode(v4->newVariantObject(type: propMetaType, data: v.constData()));
259 }
260 default:
261 break;
262 }
263
264 if (propMetaType == QMetaType::fromType<QJSValue>()) {
265 QJSValue v;
266 property.readProperty(target: object, property: &v);
267 return QJSValuePrivate::convertToReturnedValue(e: v4, jsval: v);
268 }
269
270 if (property.isQVariant()) {
271 // We have to read the property even if it's a lazy-loaded reference object.
272 // Without reading it, we wouldn't know its inner type.
273 QVariant v;
274 property.readProperty(target: object, property: &v);
275 return scope.engine->fromVariant(
276 variant: v, parent: wrapper, property: property.coreIndex(),
277 flags: referenceFlags(v4: scope.engine, property) | Heap::ReferenceObject::IsVariant);
278 }
279
280 if (!propMetaType.isValid()) {
281 QMetaProperty p = object->metaObject()->property(index: property.coreIndex());
282 qWarning(msg: "QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
283 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
284 return Encode::undefined();
285 }
286
287 // TODO: For historical reasons we don't enforce locations for reference objects here.
288 // Once we do, we can eager load and use the fromVariant() below.
289 // Then the extra checks for value types and sequences can be dropped.
290
291 if (QQmlMetaType::isValueType(type: propMetaType)) {
292 if (const QMetaObject *valueTypeMetaObject
293 = QQmlMetaType::metaObjectForValueType(type: propMetaType)) {
294 // Lazy loaded value type reference. Pass nullptr as data.
295 return QQmlValueTypeWrapper::create(
296 engine: v4, data: nullptr, metaObject: valueTypeMetaObject, type: propMetaType, object: wrapper,
297 property: property.coreIndex(), flags: referenceFlags(v4: scope.engine, property));
298 }
299 }
300
301 // See if it's a sequence type.
302 const QQmlType qmlType = QQmlMetaType::qmlListType(metaType: propMetaType);
303 if (qmlType.isSequentialContainer())
304 return encodeSequence(qmlType.listMetaSequence());
305
306 QVariant v(propMetaType);
307 property.readProperty(target: object, property: v.data());
308 return scope.engine->fromVariant(
309 variant: v, parent: wrapper, property: property.coreIndex(), flags: referenceFlags(v4: scope.engine, property));
310}
311
312void QObjectWrapper::initializeBindings(ExecutionEngine *engine)
313{
314 engine->functionPrototype()->defineDefaultProperty(QStringLiteral("connect"), code: method_connect);
315 engine->functionPrototype()->defineDefaultProperty(QStringLiteral("disconnect"), code: method_disconnect);
316}
317
318const QQmlPropertyData *QObjectWrapper::findProperty(
319 const QQmlRefPointer<QQmlContextData> &qmlContext, String *name,
320 Flags flags, QQmlPropertyData *local) const
321{
322 return findProperty(o: d()->object(), qmlContext, name, flags, local);
323}
324
325const QQmlPropertyData *QObjectWrapper::findProperty(
326 QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext,
327 String *name, Flags flags, QQmlPropertyData *local)
328{
329 Q_UNUSED(flags);
330
331 QQmlData *ddata = QQmlData::get(object: o, create: false);
332 const QQmlPropertyData *result = nullptr;
333 if (ddata && ddata->propertyCache)
334 result = ddata->propertyCache->property(key: name, object: o, context: qmlContext);
335 else
336 result = QQmlPropertyCache::property(o, name, qmlContext, local);
337 return result;
338}
339
340ReturnedValue QObjectWrapper::getProperty(
341 ExecutionEngine *engine, Heap::Object *wrapper, QObject *object,
342 const QQmlPropertyData *property, Flags flags)
343{
344 QQmlData::flushPendingBinding(object, coreIndex: property->coreIndex());
345
346 if (property->isFunction() && !property->isVarProperty()) {
347 if (property->isVMEFunction()) {
348 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: object);
349 Q_ASSERT(vmemo);
350 return vmemo->vmeMethod(index: property->coreIndex());
351 } else if (property->isV4Function()) {
352 return QObjectMethod::create(
353 engine, wrapper: (flags & AttachMethods) ? wrapper : nullptr, index: property->coreIndex());
354 } else if (property->isSignalHandler()) {
355 QmlSignalHandler::initProto(v4: engine);
356 return engine->memoryManager->allocate<QmlSignalHandler>(
357 args&: object, args: property->coreIndex())->asReturnedValue();
358 } else {
359 return QObjectMethod::create(
360 engine, wrapper: (flags & AttachMethods) ? wrapper : nullptr, index: property->coreIndex());
361 }
362 }
363
364 QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(e: engine->qmlEngine()) : nullptr;
365
366 if (ep && ep->propertyCapture && !property->isConstant())
367 if (!property->isBindable() || ep->propertyCapture->expression->mustCaptureBindableProperty())
368 ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
369
370 if (property->isVarProperty()) {
371 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: object);
372 Q_ASSERT(vmemo);
373 return vmemo->vmeProperty(index: property->coreIndex());
374 } else {
375 return loadProperty(v4: engine, wrapper, object, property: *property);
376 }
377}
378
379static OptionalReturnedValue getDestroyOrToStringMethod(
380 ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
381{
382 int index = 0;
383 if (name->equals(other: v4->id_destroy()))
384 index = QObjectMethod::DestroyMethod;
385 else if (name->equals(other: v4->id_toString()))
386 index = QObjectMethod::ToStringMethod;
387 else
388 return OptionalReturnedValue();
389
390 if (hasProperty)
391 *hasProperty = true;
392 return OptionalReturnedValue(QObjectMethod::create(engine: v4, wrapper: qobj, index));
393}
394
395static OptionalReturnedValue getPropertyFromImports(
396 ExecutionEngine *v4, String *name, const QQmlRefPointer<QQmlContextData> &qmlContext,
397 QObject *qobj, bool *hasProperty = nullptr)
398{
399 if (!qmlContext || !qmlContext->imports())
400 return OptionalReturnedValue();
401
402 if (hasProperty)
403 *hasProperty = true;
404
405 if (QQmlTypeLoader *typeLoader = v4->typeLoader()) {
406 QQmlTypeNameCache::Result r = qmlContext->imports()->query(key: name, typeLoader);
407
408 if (!r.isValid())
409 return OptionalReturnedValue();
410
411 if (r.scriptIndex != -1) {
412 return OptionalReturnedValue(Encode::undefined());
413 } else if (r.type.isValid()) {
414 return OptionalReturnedValue(
415 QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
416 } else if (r.importNamespace) {
417 return OptionalReturnedValue(QQmlTypeWrapper::create(
418 v4, qobj, qmlContext->imports(), r.importNamespace,
419 Heap::QQmlTypeWrapper::ExcludeEnums));
420 }
421 Q_UNREACHABLE_RETURN(OptionalReturnedValue());
422 } else {
423 return OptionalReturnedValue();
424 }
425}
426
427ReturnedValue QObjectWrapper::getQmlProperty(
428 const QQmlRefPointer<QQmlContextData> &qmlContext, String *name,
429 QObjectWrapper::Flags flags, bool *hasProperty) const
430{
431 // Keep this code in sync with ::virtualResolveLookupGetter
432
433 if (QQmlData::wasDeleted(object: d()->object())) {
434 if (hasProperty)
435 *hasProperty = false;
436 return Encode::undefined();
437 }
438
439 ExecutionEngine *v4 = engine();
440
441 if (auto methodValue = getDestroyOrToStringMethod(v4, name, qobj: d(), hasProperty))
442 return *methodValue;
443
444 QQmlPropertyData local;
445 const QQmlPropertyData *result = findProperty(qmlContext, name, flags, local: &local);
446
447 if (!result) {
448 // Check for attached properties
449 if ((flags & IncludeImports) && name->startsWithUpper()) {
450 if (auto importProperty = getPropertyFromImports(
451 v4, name, qmlContext, qobj: d()->object(), hasProperty))
452 return *importProperty;
453 }
454 return Object::virtualGet(m: this, id: name->propertyKey(), receiver: this, hasProperty);
455 }
456
457 QQmlData *ddata = QQmlData::get(object: d()->object(), create: false);
458
459 if ((flags & CheckRevision) && result->hasRevision()) {
460 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(data: result)) {
461 if (hasProperty)
462 *hasProperty = false;
463 return Encode::undefined();
464 }
465 }
466
467 if (hasProperty)
468 *hasProperty = true;
469
470 return getProperty(engine: v4, wrapper: d(), object: d()->object(), property: result, flags);
471}
472
473ReturnedValue QObjectWrapper::getQmlProperty(
474 ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
475 Heap::Object *wrapper, QObject *object, String *name, QObjectWrapper::Flags flags,
476 bool *hasProperty, const QQmlPropertyData **property)
477{
478 if (QQmlData::wasDeleted(object)) {
479 if (hasProperty)
480 *hasProperty = false;
481 return Encode::null();
482 }
483
484 if (auto methodValue = getDestroyOrToStringMethod(v4: engine, name, qobj: wrapper, hasProperty))
485 return *methodValue;
486
487 QQmlData *ddata = QQmlData::get(object, create: false);
488 QQmlPropertyData local;
489 const QQmlPropertyData *result = findProperty(o: object, qmlContext, name, flags, local: &local);
490
491 if (result) {
492 if ((flags & QObjectWrapper::CheckRevision) && result->hasRevision()) {
493 if (ddata && ddata->propertyCache
494 && !ddata->propertyCache->isAllowedInRevision(data: result)) {
495 if (hasProperty)
496 *hasProperty = false;
497 return Encode::undefined();
498 }
499 }
500
501 if (hasProperty)
502 *hasProperty = true;
503
504 if (property && result != &local)
505 *property = result;
506
507 return getProperty(engine, wrapper, object, property: result, flags);
508 } else {
509 // Check if this object is already wrapped.
510 if (!ddata || (ddata->jsWrapper.isUndefined() &&
511 (ddata->jsEngineId == 0 || // Nobody owns the QObject
512 !ddata->hasTaintedV4Object))) { // Someone else has used the QObject, but it isn't tainted
513
514 // Not wrapped. Last chance: try query QObjectWrapper's prototype.
515 // If it can't handle this, then there is no point
516 // to wrap the QObject just to look at an empty set of JS props.
517 Object *proto = QObjectWrapper::defaultPrototype(e: engine);
518 return proto->get(name, hasProperty);
519 }
520 }
521
522 // If we get here, we must already be wrapped (which implies a ddata).
523 // There's no point wrapping again, as there wouldn't be any new props.
524 Q_ASSERT(ddata);
525
526 Scope scope(engine);
527 Scoped<QObjectWrapper> rewrapped(scope, wrap(engine, object));
528 if (!rewrapped) {
529 if (hasProperty)
530 *hasProperty = false;
531 return Encode::null();
532 }
533 return rewrapped->getQmlProperty(qmlContext, name, flags, hasProperty);
534}
535
536
537bool QObjectWrapper::setQmlProperty(
538 ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, QObject *object,
539 String *name, QObjectWrapper::Flags flags, const Value &value)
540{
541 if (QQmlData::wasDeleted(object))
542 return false;
543
544 QQmlPropertyData local;
545 const QQmlPropertyData *result = QQmlPropertyCache::property(object, name, qmlContext, &local);
546 if (!result)
547 return false;
548
549 if ((flags & QObjectWrapper::CheckRevision) && result->hasRevision()) {
550 QQmlData *ddata = QQmlData::get(object);
551 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(data: result))
552 return false;
553 }
554
555 setProperty(engine, object, property: result, value);
556 return true;
557}
558
559/*!
560 \internal
561 If an QObjectWrapper is created via wrap, then it needs to be stored somewhere.
562 Otherwise, the garbage collector will immediately collect it if it is already
563 past the "mark QObjectWrapper's" phase (note that QObjectWrapper are marked
564 by iterating over a list of all QObjectWrapper, and then checking if the
565 wrapper fulfills some conditions).
566 However, sometimes we don't really want to keep a reference to the wrapper,
567 but just want to make sure that it exists (and we know that the wrapper
568 already fulfills the conditions to be kept alive). Then ensureWrapper
569 can be used, which creates the wrapper and ensures that it is also
570 marked.
571 */
572void QObjectWrapper::ensureWrapper(ExecutionEngine *engine, QObject *object)
573{
574 QV4::Scope scope(engine);
575 QV4::Scoped<QV4::QObjectWrapper> wrapper {scope, QV4::QObjectWrapper::wrap(engine, object)};
576 QV4::WriteBarrier::markCustom(engine, markFunction: [&wrapper](QV4::MarkStack *ms) {
577 wrapper->mark(markStack: ms);
578 });
579}
580
581void QObjectWrapper::setProperty(
582 ExecutionEngine *engine, QObject *object,
583 const QQmlPropertyData *property, const Value &value)
584{
585 if (!property->isWritable() && !property->isQList()) {
586 QString error = QLatin1String("Cannot assign to read-only property \"") +
587 property->name(object) + QLatin1Char('\"');
588 engine->throwTypeError(message: error);
589 return;
590 }
591
592 Scope scope(engine);
593 if (ScopedFunctionObject f(scope, value); f) {
594 if (f->as<QQmlTypeWrapper>()) {
595 // Ignore. It's probably a singleton or an attached type.
596 } else if (!f->isBinding()) {
597 const bool isAliasToAllowed = [&]() {
598 if (property->isAlias()) {
599 const QQmlPropertyIndex originalIndex(property->coreIndex(), -1);
600 auto [targetObject, targetIndex] = QQmlPropertyPrivate::findAliasTarget(baseObject: object, baseIndex: originalIndex);
601 Q_ASSERT(targetObject);
602 const QQmlPropertyCache *targetCache
603 = QQmlData::get(object: targetObject)->propertyCache.data();
604 Q_ASSERT(targetCache);
605 const QQmlPropertyData *targetProperty
606 = targetCache->property(index: targetIndex.coreIndex());
607 object = targetObject;
608 property = targetProperty;
609 return targetProperty->isVarProperty() || targetProperty->propType() == QMetaType::fromType<QJSValue>();
610 } else {
611 return false;
612 }
613 }();
614 if (!isAliasToAllowed && !property->isVarProperty()
615 && property->propType() != QMetaType::fromType<QJSValue>()) {
616 // assigning a JS function to a non var or QJSValue property or is not allowed.
617 QString error = QLatin1String("Cannot assign JavaScript function to ");
618 if (!QMetaType(property->propType()).name())
619 error += QLatin1String("[unknown property type]");
620 else
621 error += QLatin1String(QMetaType(property->propType()).name());
622 scope.engine->throwError(message: error);
623 return;
624 }
625 } else {
626
627 QQmlRefPointer<QQmlContextData> callingQmlContext = scope.engine->callingQmlContext();
628 Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
629 Scoped<JavaScriptFunctionObject> f(scope, bindingFunction->bindingFunction());
630 ScopedContext ctx(scope, f->scope());
631
632 // binding assignment.
633 if (property->isBindable()) {
634 const QQmlPropertyIndex idx(property->coreIndex(), /*not a value type*/-1);
635 auto [targetObject, targetIndex] = QQmlPropertyPrivate::findAliasTarget(baseObject: object, baseIndex: idx);
636 QUntypedPropertyBinding binding;
637 if (f->isBoundFunction()) {
638 auto boundFunction = static_cast<BoundFunction *>(f.getPointer());
639 binding = QQmlPropertyBinding::createFromBoundFunction(pd: property, function: boundFunction, obj: object, ctxt: callingQmlContext,
640 scope: ctx, target: targetObject, targetIndex);
641 } else {
642 binding = QQmlPropertyBinding::create(pd: property, function: f->function(), obj: object, ctxt: callingQmlContext,
643 scope: ctx, target: targetObject, targetIndex);
644 }
645 QUntypedBindable bindable;
646 void *argv = {&bindable};
647 // indirect metacall in case interceptors are installed
648 targetObject->metaObject()->metacall(targetObject, QMetaObject::BindableProperty, targetIndex.coreIndex(), &argv);
649 bool ok = bindable.setBinding(binding);
650 if (!ok) {
651 auto error = QStringLiteral("Failed to set binding on %1::%2.").
652 arg(args: QString::fromUtf8(utf8: object->metaObject()->className()), args: property->name(object));
653 scope.engine->throwError(message: error);
654 }
655 } else {
656 QQmlBinding *newBinding = QQmlBinding::create(property, function: f->function(), obj: object, ctxt: callingQmlContext, scope: ctx);
657 newBinding->setSourceLocation(bindingFunction->currentLocation());
658 if (f->isBoundFunction())
659 newBinding->setBoundFunction(static_cast<BoundFunction *>(f.getPointer()));
660 newBinding->setTarget(object, *property, valueType: nullptr);
661 QQmlPropertyPrivate::setBinding(binding: newBinding);
662 }
663 return;
664 }
665 }
666
667 if (Q_UNLIKELY(lcBuiltinsBindingRemoval().isInfoEnabled())) {
668 if (auto binding = QQmlPropertyPrivate::binding(object, index: QQmlPropertyIndex(property->coreIndex()))) {
669 const auto stackFrame = engine->currentStackFrame;
670 switch (binding->kind()) {
671 case QQmlAbstractBinding::QmlBinding: {
672 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
673 qCInfo(lcBuiltinsBindingRemoval,
674 "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
675 object->metaObject()->className(), qPrintable(property->name(object)),
676 qPrintable(stackFrame->source()), stackFrame->lineNumber(),
677 qPrintable(qmlBinding->expressionIdentifier()));
678 break;
679 }
680 case QQmlAbstractBinding::ValueTypeProxy:
681 case QQmlAbstractBinding::PropertyToPropertyBinding: {
682 qCInfo(lcBuiltinsBindingRemoval,
683 "Overwriting binding on %s::%s at %s:%d",
684 object->metaObject()->className(), qPrintable(property->name(object)),
685 qPrintable(stackFrame->source()), stackFrame->lineNumber());
686 break;
687 }
688 }
689 }
690 }
691 QQmlPropertyPrivate::removeBinding(o: object, index: QQmlPropertyIndex(property->coreIndex()));
692
693 if (property->isVarProperty()) {
694 // allow assignment of "special" values (null, undefined, function) to var properties
695 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: object);
696 Q_ASSERT(vmemo);
697 vmemo->setVMEProperty(index: property->coreIndex(), v: value);
698 return;
699 }
700
701#define PROPERTY_STORE(cpptype, value) \
702 cpptype o = value; \
703 int status = -1; \
704 int flags = 0; \
705 void *argv[] = { &o, 0, &status, &flags }; \
706 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
707
708 const QMetaType propType = property->propType();
709 // functions are already handled, except for the QJSValue case
710 Q_ASSERT(!value.as<FunctionObject>()
711 || value.as<QV4::QQmlTypeWrapper>()
712 || propType == QMetaType::fromType<QJSValue>());
713
714 if (value.isNull() && property->isQObject()) {
715 PROPERTY_STORE(QObject*, nullptr);
716 } else if (value.isUndefined() && property->isResettable()) {
717 void *a[] = { nullptr };
718 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex(), a);
719 } else if (value.isUndefined() && propType == QMetaType::fromType<QVariant>()) {
720 PROPERTY_STORE(QVariant, QVariant());
721 } else if (value.isUndefined() && propType == QMetaType::fromType<QJsonValue>()) {
722 PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
723 } else if (propType == QMetaType::fromType<QJSValue>()) {
724 PROPERTY_STORE(QJSValue, QJSValuePrivate::fromReturnedValue(value.asReturnedValue()));
725 } else if (value.isUndefined() && propType != QMetaType::fromType<QQmlScriptString>()) {
726 QString error = QLatin1String("Cannot assign [undefined] to ");
727 if (!propType.name())
728 error += QLatin1String("[unknown property type]");
729 else
730 error += QLatin1String(propType.name());
731 scope.engine->throwError(message: error);
732 return;
733 } else if (propType == QMetaType::fromType<int>() && value.isNumber()) {
734 PROPERTY_STORE(int, value.toInt32());
735 } else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) {
736 PROPERTY_STORE(qreal, qreal(value.asDouble()));
737 } else if (propType == QMetaType::fromType<float>() && value.isNumber()) {
738 PROPERTY_STORE(float, float(value.asDouble()));
739 } else if (propType == QMetaType::fromType<double>() && value.isNumber()) {
740 PROPERTY_STORE(double, double(value.asDouble()));
741 } else if (propType == QMetaType::fromType<QString>() && value.isString()) {
742 PROPERTY_STORE(QString, value.toQStringNoThrow());
743 } else if (property->isVarProperty()) {
744 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: object);
745 Q_ASSERT(vmemo);
746 vmemo->setVMEProperty(index: property->coreIndex(), v: value);
747 } else if (propType == QMetaType::fromType<QQmlScriptString>()
748 && (value.isUndefined() || value.isPrimitive())) {
749 QQmlScriptString ss(value.toQStringNoThrow(), nullptr /* context */, object);
750 if (value.isNumber()) {
751 ss.d->numberValue = value.toNumber();
752 ss.d->isNumberLiteral = true;
753 } else if (value.isString()) {
754 ss.d->script = CompiledData::Binding::escapedString(string: ss.d->script);
755 ss.d->isStringLiteral = true;
756 }
757 PROPERTY_STORE(QQmlScriptString, ss);
758 } else {
759 QVariant v;
760 if (property->isQList() && propType.flags().testFlag(flag: QMetaType::IsQmlList))
761 v = ExecutionEngine::toVariant(value, typeHint: QMetaType::fromType<QList<QObject *> >());
762 else
763 v = ExecutionEngine::toVariant(value, typeHint: propType);
764
765 QQmlRefPointer<QQmlContextData> callingQmlContext = scope.engine->callingQmlContext();
766 if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) {
767 const char *valueType = (v.userType() == QMetaType::UnknownType)
768 ? "an unknown type"
769 : QMetaType(v.userType()).name();
770
771 const char *targetTypeName = propType.name();
772 if (!targetTypeName)
773 targetTypeName = "an unregistered type";
774
775 QString error = QLatin1String("Cannot assign ") +
776 QLatin1String(valueType) +
777 QLatin1String(" to ") +
778 QLatin1String(targetTypeName);
779 scope.engine->throwError(message: error);
780 return;
781 }
782 }
783}
784
785ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *object)
786{
787 Q_ASSERT(!QQmlData::wasDeleted(object));
788
789 QQmlData *ddata = QQmlData::get(object, create: true);
790 if (!ddata)
791 return Encode::undefined();
792
793 Scope scope(engine);
794
795 if (ddata->jsWrapper.isUndefined() &&
796 (ddata->jsEngineId == engine->m_engineId || // We own the QObject
797 ddata->jsEngineId == 0 || // No one owns the QObject
798 !ddata->hasTaintedV4Object)) { // Someone else has used the QObject, but it isn't tainted
799
800 ScopedValue rv(scope, create(engine, object));
801 ddata->jsWrapper.set(engine: scope.engine, value: rv);
802 ddata->jsEngineId = engine->m_engineId;
803 return rv->asReturnedValue();
804
805 } else {
806 // If this object is tainted, we have to check to see if it is in our
807 // tainted object list
808 ScopedObject alternateWrapper(scope, (Object *)nullptr);
809 if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
810 alternateWrapper = engine->m_multiplyWrappedQObjects->value(key: object);
811
812 // If our tainted handle doesn't exist or has been collected, and there isn't
813 // a handle in the ddata, we can assume ownership of the ddata->jsWrapper
814 if (ddata->jsWrapper.isUndefined() && !alternateWrapper) {
815 ScopedValue result(scope, create(engine, object));
816 ddata->jsWrapper.set(engine: scope.engine, value: result);
817 ddata->jsEngineId = engine->m_engineId;
818 return result->asReturnedValue();
819 }
820
821 if (!alternateWrapper) {
822 alternateWrapper = create(engine, object);
823 if (!engine->m_multiplyWrappedQObjects)
824 engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap;
825 engine->m_multiplyWrappedQObjects->insert(key: object, value: alternateWrapper->d());
826 ddata->hasTaintedV4Object = true;
827 }
828
829 return alternateWrapper.asReturnedValue();
830 }
831}
832
833ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObject *object)
834{
835 const QObject *constObject = object;
836
837 QQmlData *ddata = QQmlData::get(object, create: true);
838
839 Scope scope(engine);
840 ScopedObject constWrapper(scope);
841 if (engine->m_multiplyWrappedQObjects && ddata->hasConstWrapper)
842 constWrapper = engine->m_multiplyWrappedQObjects->value(key: constObject);
843
844 if (!constWrapper) {
845 constWrapper = create(engine, object);
846 constWrapper->setInternalClass(constWrapper->internalClass()->cryopreserved());
847 if (!engine->m_multiplyWrappedQObjects)
848 engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap;
849 engine->m_multiplyWrappedQObjects->insert(key: constObject, value: constWrapper->d());
850 ddata->hasConstWrapper = true;
851 }
852
853 return constWrapper.asReturnedValue();
854}
855
856void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
857{
858 if (QQmlData::wasDeleted(object))
859 return;
860
861 QQmlData *ddata = QQmlData::get(object);
862 if (!ddata)
863 return;
864
865 const ExecutionEngine *engine = markStack->engine();
866 if (ddata->jsEngineId == engine->m_engineId)
867 ddata->jsWrapper.markOnce(markStack);
868 else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
869 engine->m_multiplyWrappedQObjects->mark(key: object, markStack);
870 if (ddata->hasConstWrapper) {
871 Q_ASSERT(engine->m_multiplyWrappedQObjects);
872 engine->m_multiplyWrappedQObjects->mark(key: static_cast<const QObject *>(object), markStack);
873 }
874}
875
876void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value)
877{
878 setProperty(engine, object: d()->object(), propertyIndex, value);
879}
880
881void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value)
882{
883 Q_ASSERT(propertyIndex < 0xffff);
884 Q_ASSERT(propertyIndex >= 0);
885
886 if (QQmlData::wasDeleted(object))
887 return;
888 QQmlData *ddata = QQmlData::get(object, /*create*/false);
889 if (!ddata)
890 return;
891
892 Q_ASSERT(ddata->propertyCache);
893 const QQmlPropertyData *property = ddata->propertyCache->property(index: propertyIndex);
894 Q_ASSERT(property); // We resolved this property earlier, so it better exist!
895 return setProperty(engine, object, property, value);
896}
897
898bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
899{
900 Q_ASSERT(a->as<QObjectWrapper>());
901 const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a);
902 if (const QQmlTypeWrapper *qmlTypeWrapper = b->as<QQmlTypeWrapper>())
903 return qmlTypeWrapper->object() == aobjectWrapper->object();
904
905 // We can have a const and a non-const wrapper for the same object.
906 const QObjectWrapper *bobjectWrapper = b->as<QObjectWrapper>();
907 return bobjectWrapper && aobjectWrapper->object() == bobjectWrapper->object();
908}
909
910ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
911{
912 if (QQmlPropertyCache::ConstPtr cache = QQmlData::ensurePropertyCache(object)) {
913 ReturnedValue result = Encode::null();
914 void *args[] = { &result, &engine };
915 if (cache->callJSFactoryMethod(object, args))
916 return result;
917 }
918 return (engine->memoryManager->allocate<QObjectWrapper>(args&: object))->asReturnedValue();
919}
920
921ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
922{
923 if (!id.isString())
924 return Object::virtualGet(m, id, receiver, hasProperty);
925
926 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
927 Scope scope(that);
928 ScopedString n(scope, id.asStringOrSymbol());
929 QQmlRefPointer<QQmlContextData> qmlContext = that->engine()->callingQmlContext();
930 return that->getQmlProperty(qmlContext, name: n, flags: IncludeImports | AttachMethods, hasProperty);
931}
932
933bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
934{
935 if (!id.isString())
936 return Object::virtualPut(m, id, value, receiver);
937
938 Scope scope(m);
939 QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
940 ScopedString name(scope, id.asStringOrSymbol());
941
942 if (that->internalClass()->isFrozen()) {
943 QString error = QLatin1String("Cannot assign to property \"") +
944 name->toQString() + QLatin1String("\" of read-only object");
945 scope.engine->throwError(message: error);
946 return false;
947 }
948
949 if (scope.hasException() || QQmlData::wasDeleted(object: that->d()->object()))
950 return false;
951
952 QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext();
953 if (!setQmlProperty(engine: scope.engine, qmlContext, object: that->d()->object(), name, flags: NoFlag, value)) {
954 QQmlData *ddata = QQmlData::get(object: that->d()->object());
955 // Types created by QML are not extensible at run-time, but for other QObjects we can store them
956 // as regular JavaScript properties, like on JavaScript objects.
957 if (ddata && ddata->context) {
958 QString error = QLatin1String("Cannot assign to non-existent property \"") +
959 name->toQString() + QLatin1Char('\"');
960 scope.engine->throwError(message: error);
961 return false;
962 } else {
963 return Object::virtualPut(m, id, value, receiver);
964 }
965 }
966
967 return true;
968}
969
970PropertyAttributes QObjectWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
971{
972 if (id.isString()) {
973 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
974 const QObject *thatObject = that->d()->object();
975 if (!QQmlData::wasDeleted(object: thatObject)) {
976 Scope scope(m);
977 ScopedString n(scope, id.asStringOrSymbol());
978 QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext();
979 QQmlPropertyData local;
980 if (that->findProperty(qmlContext, name: n, flags: NoFlag, local: &local)
981 || n->equals(other: scope.engine->id_destroy()) || n->equals(other: scope.engine->id_toString())) {
982 if (p) {
983 // ### probably not the fastest implementation
984 bool hasProperty;
985 p->value = that->getQmlProperty(
986 qmlContext, name: n, flags: IncludeImports | AttachMethods, hasProperty: &hasProperty);
987 }
988 return Attr_Data;
989 }
990 }
991 }
992
993 return Object::virtualGetOwnProperty(m, id, p);
994}
995
996struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
997{
998 int propertyIndex = 0;
999 ~QObjectWrapperOwnPropertyKeyIterator() override = default;
1000 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
1001
1002private:
1003 QSet<QByteArray> m_alreadySeen;
1004};
1005
1006PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
1007{
1008 // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
1009 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal(signal: "destroyed(QObject*)");
1010 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal(signal: "destroyed()");
1011 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot(slot: "deleteLater()");
1012
1013 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(o);
1014
1015 QObject *thatObject = that->d()->object();
1016 if (thatObject && !QQmlData::wasDeleted(object: thatObject)) {
1017 const QMetaObject *mo = thatObject->metaObject();
1018 // These indices don't apply to gadgets, so don't block them.
1019 const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
1020 const int propertyCount = mo->propertyCount();
1021 if (propertyIndex < propertyCount) {
1022 ExecutionEngine *thatEngine = that->engine();
1023 Scope scope(thatEngine);
1024 const QMetaProperty property = mo->property(index: propertyIndex);
1025 ScopedString propName(scope, thatEngine->newString(s: QString::fromUtf8(utf8: property.name())));
1026 ++propertyIndex;
1027 if (attrs)
1028 *attrs= Attr_Data;
1029 if (pd) {
1030 QQmlPropertyData local;
1031 local.load(property);
1032 pd->value = that->getProperty(
1033 engine: thatEngine, wrapper: that->d(), object: thatObject, property: &local,
1034 flags: QObjectWrapper::AttachMethods);
1035 }
1036 return propName->toPropertyKey();
1037 }
1038 const int methodCount = mo->methodCount();
1039 while (propertyIndex < propertyCount + methodCount) {
1040 Q_ASSERT(propertyIndex >= propertyCount);
1041 int index = propertyIndex - propertyCount;
1042 const QMetaMethod method = mo->method(index);
1043 ++propertyIndex;
1044 if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
1045 continue;
1046 // filter out duplicates due to overloads:
1047 if (m_alreadySeen.contains(value: method.name()))
1048 continue;
1049 else
1050 m_alreadySeen.insert(value: method.name());
1051 ExecutionEngine *thatEngine = that->engine();
1052 Scope scope(thatEngine);
1053 ScopedString methodName(scope, thatEngine->newString(s: QString::fromUtf8(ba: method.name())));
1054 if (attrs)
1055 *attrs = Attr_Data;
1056 if (pd) {
1057 QQmlPropertyData local;
1058 local.load(method);
1059 pd->value = that->getProperty(
1060 engine: thatEngine, wrapper: that->d(), object: thatObject, property: &local,
1061 flags: QObjectWrapper::AttachMethods);
1062 }
1063 return methodName->toPropertyKey();
1064 }
1065 }
1066
1067 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1068}
1069
1070OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
1071{
1072 *target = *m;
1073 return new QObjectWrapperOwnPropertyKeyIterator;
1074}
1075
1076ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
1077{
1078 // Keep this code in sync with ::getQmlProperty
1079 PropertyKey id = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->
1080 runtimeStrings[lookup->nameIndex]);
1081 if (!id.isString())
1082 return Object::virtualResolveLookupGetter(object, engine, lookup);
1083 Scope scope(engine);
1084
1085 const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
1086 ScopedString name(scope, id.asStringOrSymbol());
1087 QQmlRefPointer<QQmlContextData> qmlContext = engine->callingQmlContext();
1088
1089 QObject * const qobj = This->d()->object();
1090
1091 if (QQmlData::wasDeleted(object: qobj))
1092 return Encode::undefined();
1093
1094 QQmlData *ddata = QQmlData::get(object: qobj, create: false);
1095 if (auto methodValue = getDestroyOrToStringMethod(v4: engine, name, qobj: This->d())) {
1096 Scoped<QObjectMethod> method(scope, *methodValue);
1097 setupQObjectMethodLookup(
1098 lookup, ddata: ddata ? ddata : QQmlData::get(object: qobj, create: true), propertyData: nullptr, self: This, method: method->d());
1099 lookup->getter = Lookup::getterQObjectMethod;
1100 return method.asReturnedValue();
1101 }
1102
1103 if (!ddata || !ddata->propertyCache) {
1104 QQmlPropertyData local;
1105 const QQmlPropertyData *property = QQmlPropertyCache::property(
1106 qobj, name, qmlContext, &local);
1107 return property
1108 ? getProperty(engine, wrapper: This->d(), object: qobj, property,
1109 flags: lookup->forCall ? NoFlag : AttachMethods)
1110 : Encode::undefined();
1111 }
1112 const QQmlPropertyData *property = ddata->propertyCache->property(key: name.getPointer(), object: qobj, context: qmlContext);
1113
1114 if (!property) {
1115 // Check for attached properties
1116 if (name->startsWithUpper()) {
1117 if (auto importProperty = getPropertyFromImports(v4: engine, name, qmlContext, qobj))
1118 return *importProperty;
1119 }
1120 return Object::virtualResolveLookupGetter(object, engine, lookup);
1121 }
1122
1123 if (property->isFunction()
1124 && !property->isVarProperty()
1125 && !property->isVMEFunction() // Handled by QObjectLookup
1126 && !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too
1127 QV4::Heap::QObjectMethod *method = nullptr;
1128 setupQObjectMethodLookup(lookup, ddata, propertyData: property, self: This, method);
1129 lookup->getter = Lookup::getterQObjectMethod;
1130 return lookup->getter(lookup, engine, *object);
1131 }
1132
1133 setupQObjectLookup(lookup, ddata, propertyData: property, self: This);
1134 lookup->getter = Lookup::getterQObject;
1135 return lookup->getter(lookup, engine, *object);
1136}
1137
1138ReturnedValue QObjectWrapper::lookupAttached(
1139 Lookup *l, ExecutionEngine *engine, const Value &object)
1140{
1141 if (&QObjectWrapper::lookupAttached == &Lookup::getterGeneric) {
1142 // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
1143 // equal. As a result, the pointers are the same, which wreaks havoc on the logic that
1144 // decides how to retrieve the property.
1145 qFatal(msg: "Your C++ compiler is broken.");
1146 }
1147
1148 // This getter marks the presence of a lookup for an attached object.
1149 // It falls back to the generic lookup when run through the interpreter, but AOT-compiled
1150 // code can get clever with it.
1151 return Lookup::getterGeneric(l, engine, object);
1152}
1153
1154bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
1155 const Value &value)
1156{
1157 return Object::virtualResolveLookupSetter(object, engine, lookup, value);
1158}
1159
1160int QObjectWrapper::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
1161{
1162 QObjectWrapper *wrapper = object->as<QObjectWrapper>();
1163 Q_ASSERT(wrapper);
1164
1165 if (QObject *qObject = wrapper->object())
1166 return QMetaObject::metacall(qObject, call, index, a);
1167
1168 return 0;
1169}
1170
1171QString QObjectWrapper::objectToString(
1172 ExecutionEngine *engine, const QMetaObject *metaObject, QObject *object)
1173{
1174 if (!metaObject)
1175 return QLatin1String("null");
1176
1177 if (!object)
1178 return QString::fromUtf8(utf8: metaObject->className()) + QLatin1String("(0x0)");
1179
1180 const int id = metaObject->indexOfMethod(method: "toString()");
1181 if (id >= 0) {
1182 const QMetaMethod method = metaObject->method(index: id);
1183 const QMetaType returnType = method.returnMetaType();
1184 QVariant result(returnType);
1185 method.invoke(object, returnValue: QGenericReturnArgument(returnType.name(), result.data()));
1186 if (result.metaType() == QMetaType::fromType<QString>())
1187 return result.toString();
1188 QV4::Scope scope(engine);
1189 QV4::ScopedValue value(scope, engine->fromVariant(result));
1190 return value->toQString();
1191 }
1192
1193 QString result;
1194 result += QString::fromUtf8(utf8: metaObject->className()) +
1195 QLatin1String("(0x") + QString::number(quintptr(object), base: 16);
1196 QString objectName = object->objectName();
1197 if (!objectName.isEmpty())
1198 result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
1199 result += QLatin1Char(')');
1200 return result;
1201}
1202
1203struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
1204{
1205 PersistentValue function;
1206 PersistentValue thisObject;
1207 QMetaMethod signal;
1208 qsizetype maxNumArguments;
1209
1210 QObjectSlotDispatcher()
1211 : QtPrivate::QSlotObjectBase(&impl)
1212 {}
1213
1214 static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
1215 {
1216 switch (which) {
1217 case Destroy: {
1218 delete static_cast<QObjectSlotDispatcher*>(this_);
1219 }
1220 break;
1221 case Call: {
1222 if (QQmlData::wasDeleted(object: receiver))
1223 break;
1224
1225 QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
1226 ExecutionEngine *v4 = This->function.engine();
1227 // Might be that we're still connected to a signal that's emitted long
1228 // after the engine died. We don't track connections in a global list, so
1229 // we need this safeguard.
1230 if (!v4)
1231 break;
1232
1233 QQmlMetaObject::ArgTypeStorage<9> storage;
1234 QQmlMetaObject::methodParameterTypes(method: This->signal, argStorage: &storage, unknownTypeError: nullptr);
1235
1236 const qsizetype argCount = std::min(a: storage.size(), b: This->maxNumArguments);
1237
1238 Scope scope(v4);
1239 ScopedFunctionObject f(scope, This->function.value());
1240
1241 JSCallArguments jsCallData(scope, argCount);
1242 *jsCallData.thisObject = This->thisObject.isUndefined()
1243 ? v4->globalObject->asReturnedValue()
1244 : This->thisObject.value();
1245 for (qsizetype ii = 0; ii < argCount; ++ii) {
1246 QMetaType type = storage[ii];
1247 if (type == QMetaType::fromType<QVariant>()) {
1248 jsCallData.args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1249 } else {
1250 jsCallData.args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
1251 }
1252 }
1253
1254 f->call(data: jsCallData);
1255 if (scope.hasException()) {
1256 QQmlError error = v4->catchExceptionAsQmlError();
1257 if (error.description().isEmpty()) {
1258 ScopedString name(scope, f->name());
1259 error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(a: name->toQString()));
1260 }
1261 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
1262 QQmlEnginePrivate::get(e: qmlEngine)->warning(error);
1263 } else {
1264 QMessageLogger(error.url().toString().toLatin1().constData(),
1265 error.line(), nullptr).warning().noquote()
1266 << error.toString();
1267 }
1268 }
1269 }
1270 break;
1271 case Compare: {
1272 QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
1273 if (connection->function.isUndefined()) {
1274 *ret = false;
1275 return;
1276 }
1277
1278 // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
1279 // for the new-style QObject::connect. Here we use the engine pointer as sentinel
1280 // to distinguish those type of QSlotObjectBase connections from our QML connections.
1281 ExecutionEngine *v4 = reinterpret_cast<ExecutionEngine*>(metaArgs[0]);
1282 if (v4 != connection->function.engine()) {
1283 *ret = false;
1284 return;
1285 }
1286
1287 Scope scope(v4);
1288 ScopedValue function(scope, *reinterpret_cast<Value*>(metaArgs[1]));
1289 ScopedValue thisObject(scope, *reinterpret_cast<Value*>(metaArgs[2]));
1290 QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
1291 int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
1292
1293 if (slotIndexToDisconnect != -1) {
1294 // This is a QObject function wrapper
1295 if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1296 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(x: *connection->thisObject.valueRef(), y: thisObject))) {
1297
1298 ScopedFunctionObject f(scope, connection->function.value());
1299 QPair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(function: f);
1300 if (connectedFunctionData.first == receiverToDisconnect &&
1301 connectedFunctionData.second == slotIndexToDisconnect) {
1302 *ret = true;
1303 return;
1304 }
1305 }
1306 } else {
1307 // This is a normal JS function
1308 if (RuntimeHelpers::strictEqual(x: *connection->function.valueRef(), y: function) &&
1309 connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1310 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(x: *connection->thisObject.valueRef(), y: thisObject))) {
1311 *ret = true;
1312 return;
1313 }
1314 }
1315
1316 *ret = false;
1317 }
1318 break;
1319 case NumOperations:
1320 break;
1321 }
1322 };
1323};
1324
1325ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1326{
1327 Scope scope(b);
1328
1329 if (argc == 0)
1330 THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
1331
1332 QPair<QObject *, int> signalInfo = extractQtSignal(value: *thisObject);
1333 QObject *signalObject = signalInfo.first;
1334 int signalIndex = signalInfo.second; // in method range, not signal range!
1335
1336 if (signalIndex < 0)
1337 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1338
1339 if (!signalObject)
1340 THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1341
1342 auto signalMetaMethod = signalObject->metaObject()->method(index: signalIndex);
1343 if (signalMetaMethod.methodType() != QMetaMethod::Signal)
1344 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1345
1346 ScopedFunctionObject f(scope);
1347 ScopedValue object (scope, Encode::undefined());
1348
1349 if (argc == 1) {
1350 f = argv[0];
1351 } else if (argc >= 2) {
1352 object = argv[0];
1353 f = argv[1];
1354 }
1355
1356 if (!f)
1357 THROW_GENERIC_ERROR("Function.prototype.connect: target is not a function");
1358
1359 if (!object->isUndefined() && !object->isObject())
1360 THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object");
1361
1362 QObjectSlotDispatcher *slot = new QObjectSlotDispatcher;
1363 slot->signal = signalMetaMethod;
1364
1365 slot->thisObject.set(engine: scope.engine, value: object);
1366 slot->function.set(engine: scope.engine, value: f);
1367
1368 if (QQmlData *ddata = QQmlData::get(object: signalObject)) {
1369 if (const QQmlPropertyCache *propertyCache = ddata->propertyCache.data()) {
1370 QQmlPropertyPrivate::flushSignal(sender: signalObject, signal_index: propertyCache->methodIndexToSignalIndex(index: signalIndex));
1371 }
1372 }
1373
1374 QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(function: f); // align with disconnect
1375 QObject *receiver = nullptr;
1376
1377 if (functionData.first)
1378 receiver = functionData.first;
1379 else if (auto qobjectWrapper = object->as<QV4::QObjectWrapper>())
1380 receiver = qobjectWrapper->object();
1381 else if (auto typeWrapper = object->as<QV4::QQmlTypeWrapper>())
1382 receiver = typeWrapper->object();
1383
1384 if (receiver) {
1385 if (functionData.second == -1) {
1386 slot->maxNumArguments = std::numeric_limits<qsizetype>::max();
1387 } else {
1388 // This means we are connecting to QObjectMethod which complains about extra arguments.
1389 Heap::QObjectMethod *d = static_cast<Heap::QObjectMethod *>(f->d());
1390 d->ensureMethodsCache(thisMeta: receiver->metaObject());
1391 slot->maxNumArguments = std::accumulate(first: d->methods, last: d->methods + d->methodCount, init: 0,
1392 binary_op: [](int a, const QQmlPropertyData &b) {
1393 return std::max(a: a, b: b.metaMethod().parameterCount());
1394 });
1395 }
1396
1397 QObjectPrivate::connect(sender: signalObject, signal_index: signalIndex, receiver, slotObj: slot, type: Qt::AutoConnection);
1398 } else {
1399 slot->maxNumArguments = std::numeric_limits<qsizetype>::max();
1400 qCInfo(lcObjectConnect,
1401 "Could not find receiver of the connection, using sender as receiver. Disconnect "
1402 "explicitly (or delete the sender) to make sure the connection is removed.");
1403 QObjectPrivate::connect(sender: signalObject, signal_index: signalIndex, receiver: signalObject, slotObj: slot, type: Qt::AutoConnection);
1404 }
1405
1406 RETURN_UNDEFINED();
1407}
1408
1409ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1410{
1411 Scope scope(b);
1412
1413 if (argc == 0)
1414 THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
1415
1416 QPair<QObject *, int> signalInfo = extractQtSignal(value: *thisObject);
1417 QObject *signalObject = signalInfo.first;
1418 int signalIndex = signalInfo.second;
1419
1420 if (signalIndex == -1)
1421 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1422
1423 if (!signalObject)
1424 THROW_GENERIC_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1425
1426 if (signalIndex < 0 || signalObject->metaObject()->method(index: signalIndex).methodType() != QMetaMethod::Signal)
1427 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1428
1429 ScopedFunctionObject functionValue(scope);
1430 ScopedValue functionThisValue(scope, Encode::undefined());
1431
1432 if (argc == 1) {
1433 functionValue = argv[0];
1434 } else if (argc >= 2) {
1435 functionThisValue = argv[0];
1436 functionValue = argv[1];
1437 }
1438
1439 if (!functionValue)
1440 THROW_GENERIC_ERROR("Function.prototype.disconnect: target is not a function");
1441
1442 if (!functionThisValue->isUndefined() && !functionThisValue->isObject())
1443 THROW_GENERIC_ERROR("Function.prototype.disconnect: target this is not an object");
1444
1445 QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(function: functionValue);
1446
1447 void *a[] = {
1448 scope.engine,
1449 functionValue.ptr,
1450 functionThisValue.ptr,
1451 functionData.first,
1452 &functionData.second
1453 };
1454
1455 QObject *receiver = nullptr;
1456
1457 if (functionData.first)
1458 receiver = functionData.first;
1459 else if (auto qobjectWrapper = functionThisValue->as<QV4::QObjectWrapper>())
1460 receiver = qobjectWrapper->object();
1461 else if (auto typeWrapper = functionThisValue->as<QV4::QQmlTypeWrapper>())
1462 receiver = typeWrapper->object();
1463
1464 if (receiver) {
1465 QObjectPrivate::disconnect(sender: signalObject, signal_index: signalIndex, receiver,
1466 slot: reinterpret_cast<void **>(&a));
1467 } else {
1468 QObjectPrivate::disconnect(sender: signalObject, signal_index: signalIndex, receiver: signalObject,
1469 slot: reinterpret_cast<void **>(&a));
1470 }
1471
1472 RETURN_UNDEFINED();
1473}
1474
1475static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack)
1476{
1477 QQueue<QObject *> queue;
1478 queue.append(l: parent->children());
1479
1480 while (!queue.isEmpty()) {
1481 QObject *child = queue.dequeue();
1482 if (!child)
1483 continue;
1484 QObjectWrapper::markWrapper(object: child, markStack);
1485 queue.append(l: child->children());
1486 }
1487}
1488
1489void Heap::QObjectWrapper::markObjects(Heap::Base *that, MarkStack *markStack)
1490{
1491 QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
1492
1493 if (QObject *o = This->object()) {
1494 if (QQmlData *ddata = QQmlData::get(object: o)) {
1495 if (ddata->hasVMEMetaObject) {
1496 if (QQmlVMEMetaObject *vme
1497 = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) {
1498 vme->mark(markStack);
1499 }
1500 }
1501
1502 if (ddata->hasConstWrapper) {
1503 Scope scope(that->internalClass->engine);
1504 Q_ASSERT(scope.engine->m_multiplyWrappedQObjects);
1505
1506 Scoped<QV4::QObjectWrapper> constWrapper(
1507 scope,
1508 scope.engine->m_multiplyWrappedQObjects->value(
1509 key: static_cast<const QObject *>(o)));
1510
1511 Q_ASSERT(constWrapper);
1512
1513 if (This == constWrapper->d()) {
1514 // We've got the const wrapper. Also mark the non-const one
1515 if (ddata->jsEngineId == scope.engine->m_engineId)
1516 ddata->jsWrapper.markOnce(markStack);
1517 else
1518 scope.engine->m_multiplyWrappedQObjects->mark(key: o, markStack);
1519 } else {
1520 // We've got the non-const wrapper. Also mark the const one.
1521 constWrapper->mark(markStack);
1522 }
1523 }
1524 }
1525
1526 // Children usually don't need to be marked, the gc keeps them alive.
1527 // But in the rare case of a "floating" QObject without a parent that
1528 // _gets_ marked (we've been called here!) then we also need to
1529 // propagate the marking down to the children recursively.
1530 if (!o->parent())
1531 markChildQObjectsRecursively(parent: o, markStack);
1532 }
1533
1534 Object::markObjects(base: that, stack: markStack);
1535}
1536
1537void QObjectWrapper::destroyObject(bool lastCall)
1538{
1539 Heap::QObjectWrapper *h = d();
1540 Q_ASSERT(h->internalClass);
1541
1542 if (QObject *o = h->object()) {
1543 QQmlData *ddata = QQmlData::get(object: o, create: false);
1544 if (ddata) {
1545 if (!o->parent() && !ddata->indestructible) {
1546 if (ddata && ddata->ownContext) {
1547 Q_ASSERT(ddata->ownContext.data() == ddata->context);
1548 ddata->ownContext->deepClearContextObject(contextObject: o);
1549 ddata->ownContext.reset();
1550 ddata->context = nullptr;
1551 }
1552
1553 // This object is notionally destroyed now. It might still live until the next
1554 // event loop iteration, but it won't need its connections, CU, or deferredData
1555 // anymore.
1556
1557 ddata->isQueuedForDeletion = true;
1558 ddata->disconnectNotifiers(doDelete: QQmlData::DeleteNotifyList::No);
1559 ddata->compilationUnit.reset();
1560
1561 qDeleteAll(c: ddata->deferredData);
1562 ddata->deferredData.clear();
1563
1564 if (lastCall)
1565 delete o;
1566 else
1567 o->deleteLater();
1568 } else {
1569 // If the object is C++-owned, we still have to release the weak reference we have
1570 // to it.
1571 ddata->jsWrapper.clear();
1572 if (lastCall && ddata->propertyCache)
1573 ddata->propertyCache.reset();
1574 }
1575 }
1576 }
1577
1578 h->destroy();
1579}
1580
1581
1582DEFINE_OBJECT_VTABLE(QObjectWrapper);
1583
1584namespace {
1585
1586template<typename A, typename B, typename C, typename D, typename E, typename F, typename G>
1587class MaxSizeOf7 {
1588 template<typename Z, typename X>
1589 struct SMax {
1590 char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
1591 };
1592public:
1593 static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, G> > > > > >);
1594};
1595
1596struct CallArgument {
1597 Q_DISABLE_COPY_MOVE(CallArgument);
1598
1599 CallArgument() = default;
1600 ~CallArgument() { cleanup(); }
1601
1602 inline void *dataPtr();
1603
1604 inline void initAsType(QMetaType type);
1605 inline bool fromValue(QMetaType type, ExecutionEngine *, const Value &);
1606 inline ReturnedValue toValue(ExecutionEngine *);
1607
1608private:
1609 // QVariantWrappedType denotes that we're storing a QVariant, but we mean
1610 // the type inside the QVariant, not QVariant itself.
1611 enum { QVariantWrappedType = -1 };
1612
1613 inline void cleanup();
1614
1615 template <class T, class M>
1616 bool fromContainerValue(const Value &object, M CallArgument::*member);
1617
1618 union {
1619 float floatValue;
1620 double doubleValue;
1621 quint32 intValue;
1622 bool boolValue;
1623 QObject *qobjectPtr;
1624 std::vector<int> *stdVectorIntPtr;
1625 std::vector<qreal> *stdVectorRealPtr;
1626 std::vector<bool> *stdVectorBoolPtr;
1627 std::vector<QString> *stdVectorQStringPtr;
1628 std::vector<QUrl> *stdVectorQUrlPtr;
1629#if QT_CONFIG(qml_itemmodel)
1630 std::vector<QModelIndex> *stdVectorQModelIndexPtr;
1631#endif
1632
1633 char allocData[MaxSizeOf7<QVariant,
1634 QString,
1635 QList<QObject *>,
1636 QJSValue,
1637 QJsonArray,
1638 QJsonObject,
1639 QJsonValue>::Size];
1640 qint64 q_for_alignment;
1641 };
1642
1643 // Pointers to allocData
1644 union {
1645 QString *qstringPtr;
1646 QByteArray *qbyteArrayPtr;
1647 QVariant *qvariantPtr;
1648 QList<QObject *> *qlistPtr;
1649 QJSValue *qjsValuePtr;
1650 QJsonArray *jsonArrayPtr;
1651 QJsonObject *jsonObjectPtr;
1652 QJsonValue *jsonValuePtr;
1653 };
1654
1655 int type = QMetaType::UnknownType;
1656};
1657}
1658
1659static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMetaType returnType, int argCount,
1660 const QMetaType *argTypes, ExecutionEngine *engine, CallData *callArgs,
1661 QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
1662{
1663 if (argCount > 0) {
1664 // Convert all arguments.
1665 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1666 args[0].initAsType(type: returnType);
1667 for (int ii = 0; ii < argCount; ++ii) {
1668 if (!args[ii + 1].fromValue(type: argTypes[ii], engine,
1669 callArgs->args[ii].asValue<Value>())) {
1670 qWarning() << QString::fromLatin1(ba: "Could not convert argument %1 at").arg(a: ii);
1671 const StackTrace stack = engine->stackTrace();
1672 for (const StackFrame &frame : stack) {
1673 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source
1674 + (frame.line > 0
1675 ? (QLatin1Char(':') + QString::number(frame.line))
1676 : QString());
1677
1678 }
1679
1680 const bool is_signal =
1681 object.metaObject()->method(index).methodType() == QMetaMethod::Signal;
1682 if (is_signal) {
1683 qWarning() << "Passing incompatible arguments to signals is not supported.";
1684 } else {
1685 return engine->throwTypeError(
1686 message: QLatin1String("Passing incompatible arguments to C++ functions from "
1687 "JavaScript is not allowed."));
1688 }
1689 }
1690 }
1691 QVarLengthArray<void *, 9> argData(args.size());
1692 for (int ii = 0; ii < args.size(); ++ii)
1693 argData[ii] = args[ii].dataPtr();
1694
1695 object.metacall(type: callType, index, argv: argData.data());
1696
1697 return args[0].toValue(engine);
1698
1699 } else if (returnType != QMetaType::fromType<void>()) {
1700
1701 CallArgument arg;
1702 arg.initAsType(type: returnType);
1703
1704 void *args[] = { arg.dataPtr() };
1705
1706 object.metacall(type: callType, index, argv: args);
1707
1708 return arg.toValue(engine);
1709
1710 } else {
1711
1712 void *args[] = { nullptr };
1713 object.metacall(type: callType, index, argv: args);
1714 return Encode::undefined();
1715
1716 }
1717}
1718
1719template<typename Retrieve>
1720int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
1721 if (conversionMetaType == QMetaType::fromType<QVariant>())
1722 return 0;
1723
1724 const QMetaType type = retrieve();
1725 if (type == conversionMetaType)
1726 return 0;
1727
1728 if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) {
1729 if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(metaObject: conversionMetaObject))
1730 return 1;
1731 }
1732
1733 if (QMetaType::canConvert(fromType: type, toType: conversionMetaType)) {
1734 if (conversionMetaType == QMetaType::fromType<QJSValue>()
1735 || conversionMetaType == QMetaType::fromType<double>()
1736 || conversionMetaType == QMetaType::fromType<QString>()) {
1737 // Unspecific conversions receive lower score. You can convert anything
1738 // to QString or double via toString() and valueOf(), respectively.
1739 // And anything can be wrapped into QJSValue, but that's inefficient.
1740 return 6;
1741 }
1742
1743 // We have an explicitly defined conversion method to a non-boring type.
1744 return 5;
1745 }
1746
1747 return 10;
1748};
1749
1750/*
1751 Returns the match score for converting \a actual to be of type \a conversionType. A
1752 zero score means "perfect match" whereas a higher score is worse.
1753
1754 The conversion table is copied out of the \l QScript::callQtMethod() function.
1755*/
1756static int MatchScore(const Value &actual, QMetaType conversionMetaType)
1757{
1758 const int conversionType = conversionMetaType.id();
1759 const auto convertibleScore = [&](QMetaType actualType) {
1760 // There are a number of things we can do in JavaScript to subvert this, but
1761 // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
1762 if (!QMetaType::canConvert(fromType: actualType, toType: conversionMetaType))
1763 return 10;
1764
1765 // You can convert anything to QJSValue, but that's inefficient.
1766 // If we have a better option, we should use it.
1767 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1768 return 9;
1769
1770 // You can also convert anything to QVariant, but that's also suboptimal.
1771 // You can convert anything to string or double via toString() and valueOf().
1772 // Those are also rather unspecific.
1773 switch (conversionType) {
1774 case QMetaType::QVariant:
1775 case QMetaType::Double:
1776 case QMetaType::QString:
1777 return 9;
1778 default:
1779 break;
1780 }
1781
1782 // We have an explicitly defined conversion method to a non-boring type.
1783 return 8;
1784 };
1785
1786 if (actual.isNumber()) {
1787 switch (conversionType) {
1788 case QMetaType::Double:
1789 return 0;
1790 case QMetaType::Float:
1791 return 1;
1792 case QMetaType::LongLong:
1793 case QMetaType::ULongLong:
1794 return 2;
1795 case QMetaType::Long:
1796 case QMetaType::ULong:
1797 return 3;
1798 case QMetaType::Int:
1799 case QMetaType::UInt:
1800 return 4;
1801 case QMetaType::Short:
1802 case QMetaType::UShort:
1803 return 5;
1804 break;
1805 case QMetaType::Char:
1806 case QMetaType::UChar:
1807 return 6;
1808 case QMetaType::QJsonValue:
1809 return 5;
1810 default:
1811 return convertibleScore(actual.isInteger()
1812 ? QMetaType::fromType<int>()
1813 : QMetaType::fromType<double>());
1814 }
1815 } else if (actual.isString()) {
1816 switch (conversionType) {
1817 case QMetaType::QString:
1818 return 0;
1819 case QMetaType::QJsonValue:
1820 return 5;
1821 case QMetaType::QUrl:
1822 return 6; // we like to convert strings to URLs in QML
1823 case QMetaType::Double:
1824 case QMetaType::Float:
1825 case QMetaType::LongLong:
1826 case QMetaType::ULongLong:
1827 case QMetaType::Int:
1828 case QMetaType::UInt:
1829 case QMetaType::Short:
1830 case QMetaType::UShort:
1831 case QMetaType::Char:
1832 case QMetaType::UChar:
1833 // QMetaType can natively convert strings to numbers.
1834 // However, in the general case it's of course extremely lossy.
1835 return 10;
1836 default:
1837 return convertibleScore(QMetaType::fromType<QString>());
1838 }
1839 } else if (actual.isBoolean()) {
1840 switch (conversionType) {
1841 case QMetaType::Bool:
1842 return 0;
1843 case QMetaType::QJsonValue:
1844 return 5;
1845 default:
1846 return convertibleScore(QMetaType::fromType<bool>());
1847 }
1848 } else if (actual.as<DateObject>()) {
1849 switch (conversionType) {
1850 case QMetaType::QDateTime:
1851 return 0;
1852 case QMetaType::QDate:
1853 return 1;
1854 case QMetaType::QTime:
1855 return 2;
1856 default:
1857 return convertibleScore(QMetaType::fromType<QDateTime>());
1858 }
1859 } else if (actual.as<RegExpObject>()) {
1860 switch (conversionType) {
1861#if QT_CONFIG(regularexpression)
1862 case QMetaType::QRegularExpression:
1863 return 0;
1864 default:
1865 return convertibleScore(QMetaType::fromType<QRegularExpression>());
1866#else
1867 default:
1868 return convertibleScore(QMetaType());
1869#endif
1870 }
1871 } else if (actual.as<ArrayBuffer>()) {
1872 switch (conversionType) {
1873 case QMetaType::QByteArray:
1874 return 0;
1875 default:
1876 return convertibleScore(QMetaType::fromType<QByteArray>());
1877 }
1878 } else if (actual.as<ArrayObject>()) {
1879 switch (conversionType) {
1880 case QMetaType::QJsonArray:
1881 return 3;
1882 case QMetaType::QStringList:
1883 case QMetaType::QVariantList:
1884 return 5;
1885 case QMetaType::QVector4D:
1886 case QMetaType::QMatrix4x4:
1887 return 6;
1888 case QMetaType::QVector3D:
1889 return 7;
1890 default:
1891 return convertibleScore(QMetaType());
1892 }
1893 } else if (actual.isNull()) {
1894 switch (conversionType) {
1895 case QMetaType::Nullptr:
1896 case QMetaType::VoidStar:
1897 case QMetaType::QObjectStar:
1898 case QMetaType::QJsonValue:
1899 return 0;
1900 default: {
1901 if (conversionMetaType.flags().testFlag(flag: QMetaType::IsPointer))
1902 return 0;
1903 else
1904 return convertibleScore(QMetaType());
1905 }
1906 }
1907 } else if (const Object *obj = actual.as<Object>()) {
1908 if (const VariantObject *variantObject = obj->as<VariantObject>()) {
1909 return MatchVariant(conversionMetaType, retrieve: [variantObject]() {
1910 return variantObject->d()->data().metaType();
1911 });
1912 }
1913
1914 if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) {
1915 switch (conversionType) {
1916 case QMetaType::QObjectStar:
1917 return 0;
1918 default:
1919 if (conversionMetaType.flags() & QMetaType::PointerToQObject) {
1920 QObject *wrapped = wrapper->object();
1921 if (!wrapped)
1922 return 0;
1923 if (qmlobject_can_cpp_cast(object: wrapped, mo: conversionMetaType.metaObject()))
1924 return 0;
1925 }
1926 }
1927
1928 return convertibleScore(QMetaType::fromType<QObject *>());
1929 }
1930
1931 if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
1932 const QQmlType type = wrapper->d()->type();
1933 if (type.isSingleton()) {
1934 const QMetaType metaType = type.typeId();
1935 if (metaType == conversionMetaType)
1936 return 0;
1937
1938 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1939 && metaType.flags() & QMetaType::PointerToQObject
1940 && type.metaObject()->inherits(metaObject: conversionMetaType.metaObject())) {
1941 return 0;
1942 }
1943 } else if (QObject *object = wrapper->object()) {
1944 if (conversionMetaType.flags() & QMetaType::PointerToQObject
1945 && qmlobject_can_cpp_cast(object, mo: conversionMetaType.metaObject())) {
1946 return 0;
1947 }
1948 }
1949
1950 return convertibleScore(QMetaType());
1951 }
1952
1953 if (const Sequence *sequence = obj->as<Sequence>()) {
1954 const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(object: sequence);
1955 if (sequenceType == conversionMetaType)
1956 return 1;
1957
1958 return convertibleScore(sequenceType);
1959 }
1960
1961 if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
1962 return MatchVariant(conversionMetaType, retrieve: [wrapper]() {
1963 return wrapper->d()->isVariant()
1964 ? wrapper->toVariant().metaType()
1965 : wrapper->type();
1966 });
1967 }
1968
1969 if (conversionMetaType == QMetaType::fromType<QJSValue>())
1970 return 0;
1971
1972 switch (conversionType) {
1973 case QMetaType::QJsonObject:
1974 case QMetaType::QVariantMap:
1975 return 5;
1976 default:
1977 break;
1978 }
1979
1980 }
1981
1982 return convertibleScore(QMetaType());
1983}
1984
1985static int numDefinedArguments(CallData *callArgs)
1986{
1987 int numDefinedArguments = callArgs->argc();
1988 while (numDefinedArguments > 0
1989 && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) {
1990 --numDefinedArguments;
1991 }
1992 return numDefinedArguments;
1993}
1994
1995static bool requiresStrictArguments(const QQmlObjectOrGadget &object)
1996{
1997 const QMetaObject *metaObject = object.metaObject();
1998 const int indexOfClassInfo = metaObject->indexOfClassInfo(name: "QML.StrictArguments");
1999 return indexOfClassInfo != -1
2000 && metaObject->classInfo(index: indexOfClassInfo).value() == QByteArrayView("true");
2001}
2002
2003ReturnedValue QObjectMethod::callPrecise(
2004 const QQmlObjectOrGadget &object, const QQmlPropertyData &data, ExecutionEngine *engine,
2005 CallData *callArgs, QMetaObject::Call callType)
2006{
2007 QByteArray unknownTypeError;
2008
2009 QMetaType returnType = object.methodReturnType(data, unknownTypeError: &unknownTypeError);
2010
2011 if (!returnType.isValid()) {
2012 return engine->throwError(message: QLatin1String("Unknown method return type: ")
2013 + QLatin1String(unknownTypeError));
2014 }
2015
2016 auto handleTooManyArguments = [&](int expectedArguments) {
2017 if (requiresStrictArguments(object)) {
2018 engine->throwError(QStringLiteral("Too many arguments"));
2019 return false;
2020 }
2021
2022 const auto stackTrace = engine->stackTrace();
2023 if (stackTrace.isEmpty()) {
2024 qWarning().nospace().noquote()
2025 << "When matching arguments for "
2026 << object.className() << "::" << data.name(metaObject: object.metaObject()) << "():";
2027 } else {
2028 const StackFrame frame = stackTrace.first();
2029 qWarning().noquote() << frame.function + QLatin1Char('@') + frame.source
2030 + (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line))
2031 : QString());
2032 }
2033
2034 qWarning().noquote() << QStringLiteral("Too many arguments, ignoring %1")
2035 .arg(a: callArgs->argc() - expectedArguments);
2036 return true;
2037 };
2038
2039 const int definedArgumentCount = numDefinedArguments(callArgs);
2040
2041 if (data.hasArguments()) {
2042
2043 QQmlMetaObject::ArgTypeStorage<9> storage;
2044
2045 bool ok = false;
2046 if (data.isConstructor())
2047 ok = object.constructorParameterTypes(index: data.coreIndex(), dummy: &storage, unknownTypeError: &unknownTypeError);
2048 else
2049 ok = object.methodParameterTypes(index: data.coreIndex(), argStorage: &storage, unknownTypeError: &unknownTypeError);
2050
2051 if (!ok) {
2052 return engine->throwError(message: QLatin1String("Unknown method parameter type: ")
2053 + QLatin1String(unknownTypeError));
2054 }
2055
2056 if (storage.size() > callArgs->argc()) {
2057 QString error = QLatin1String("Insufficient arguments");
2058 return engine->throwError(message: error);
2059 }
2060
2061 if (storage.size() < definedArgumentCount) {
2062 if (!handleTooManyArguments(storage.size()))
2063 return Encode::undefined();
2064
2065 }
2066
2067 return CallMethod(object, index: data.coreIndex(), returnType, argCount: storage.size(), argTypes: storage.constData(), engine, callArgs, callType);
2068
2069 } else {
2070 if (definedArgumentCount > 0 && !handleTooManyArguments(0))
2071 return Encode::undefined();
2072
2073 return CallMethod(object, index: data.coreIndex(), returnType, argCount: 0, argTypes: nullptr, engine, callArgs, callType);
2074 }
2075}
2076
2077/*
2078Resolve the overloaded method to call. The algorithm works conceptually like this:
2079 1. Resolve the set of overloads it is *possible* to call.
2080 Impossible overloads include those that have too many parameters or have parameters
2081 of unknown type.
2082 2. Filter the set of overloads to only contain those with the closest number of
2083 parameters.
2084 For example, if we are called with 3 parameters and there are 2 overloads that
2085 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
2086 3. Find the best remaining overload based on its match score.
2087 If two or more overloads have the same match score, return the last one. The match
2088 score is constructed by adding the matchScore() result for each of the parameters.
2089*/
2090const QQmlPropertyData *QObjectMethod::resolveOverloaded(
2091 const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount,
2092 ExecutionEngine *engine, CallData *callArgs)
2093{
2094 const int argumentCount = callArgs->argc();
2095 const int definedArgumentCount = numDefinedArguments(callArgs);
2096
2097 const QQmlPropertyData *best = nullptr;
2098 int bestParameterScore = INT_MAX;
2099 int bestMaxMatchScore = INT_MAX;
2100 int bestSumMatchScore = INT_MAX;
2101
2102 Scope scope(engine);
2103 ScopedValue v(scope);
2104
2105 for (int i = 0; i < methodCount; ++i) {
2106 const QQmlPropertyData *attempt = methods + i;
2107
2108 if (lcOverloadResolution().isDebugEnabled()) {
2109 const QQmlPropertyData &candidate = methods[i];
2110 const QMetaMethod m = candidate.isConstructor()
2111 ? object.metaObject()->constructor(index: candidate.coreIndex())
2112 : object.metaObject()->method(index: candidate.coreIndex());
2113 qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature();
2114 }
2115
2116 // QQmlV4Function overrides anything that doesn't provide the exact number of arguments
2117 int methodParameterScore = 1;
2118 // QQmlV4Function overrides the "no idea" option, which is 10
2119 int maxMethodMatchScore = 9;
2120 // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments
2121 int sumMethodMatchScore = bestSumMatchScore;
2122
2123 if (!attempt->isV4Function()) {
2124 QQmlMetaObject::ArgTypeStorage<9> storage;
2125 int methodArgumentCount = 0;
2126 if (attempt->hasArguments()) {
2127 if (attempt->isConstructor()) {
2128 if (!object.constructorParameterTypes(index: attempt->coreIndex(), dummy: &storage, unknownTypeError: nullptr)) {
2129 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2130 continue;
2131 }
2132 } else {
2133 if (!object.methodParameterTypes(index: attempt->coreIndex(), argStorage: &storage, unknownTypeError: nullptr)) {
2134 qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types");
2135 continue;
2136 }
2137 }
2138 methodArgumentCount = storage.size();
2139 }
2140
2141 if (methodArgumentCount > argumentCount) {
2142 qCDebug(lcOverloadResolution, "rejected, insufficient arguments");
2143 continue; // We don't have sufficient arguments to call this method
2144 }
2145
2146 methodParameterScore = (definedArgumentCount == methodArgumentCount)
2147 ? 0
2148 : (definedArgumentCount - methodArgumentCount + 1);
2149 if (methodParameterScore > bestParameterScore) {
2150 qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore;
2151 continue; // We already have a better option
2152 }
2153
2154 maxMethodMatchScore = 0;
2155 sumMethodMatchScore = 0;
2156 for (int ii = 0; ii < methodArgumentCount; ++ii) {
2157 const int score = MatchScore(actual: (v = Value::fromStaticValue(staticValue: callArgs->args[ii])),
2158 conversionMetaType: storage[ii]);
2159 maxMethodMatchScore = qMax(a: maxMethodMatchScore, b: score);
2160 sumMethodMatchScore += score;
2161 }
2162 }
2163
2164 if (bestParameterScore > methodParameterScore || bestMaxMatchScore > maxMethodMatchScore
2165 || (bestParameterScore == methodParameterScore
2166 && bestMaxMatchScore == maxMethodMatchScore
2167 && bestSumMatchScore > sumMethodMatchScore)) {
2168 best = attempt;
2169 bestParameterScore = methodParameterScore;
2170 bestMaxMatchScore = maxMethodMatchScore;
2171 bestSumMatchScore = sumMethodMatchScore;
2172 qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n"
2173 << "bestMaxMatchScore" << bestMaxMatchScore << "\n"
2174 << "bestSumMatchScore" << bestSumMatchScore << "\n";
2175 } else {
2176 qCDebug(lcOverloadResolution) << "did not update best\n"
2177 << "bestParameterScore" << bestParameterScore << "\t"
2178 << "methodParameterScore" << methodParameterScore << "\n"
2179 << "bestMaxMatchScore" << bestMaxMatchScore << "\t"
2180 << "maxMethodMatchScore" << maxMethodMatchScore << "\n"
2181 << "bestSumMatchScore" << bestSumMatchScore << "\t"
2182 << "sumMethodMatchScore" << sumMethodMatchScore << "\n";
2183 }
2184
2185 if (bestParameterScore == 0 && bestMaxMatchScore == 0) {
2186 qCDebug(lcOverloadResolution, "perfect match");
2187 break; // We can't get better than that
2188 }
2189
2190 };
2191
2192 if (best && best->isValid()) {
2193 return best;
2194 } else {
2195 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
2196 for (int i = 0; i < methodCount; ++i) {
2197 const QQmlPropertyData &candidate = methods[i];
2198 const QMetaMethod m = candidate.isConstructor()
2199 ? object.metaObject()->constructor(index: candidate.coreIndex())
2200 : object.metaObject()->method(index: candidate.coreIndex());
2201 error += u"\n " + QString::fromUtf8(ba: m.methodSignature());
2202 }
2203
2204 engine->throwError(message: error);
2205 return nullptr;
2206 }
2207}
2208
2209static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
2210{
2211 if (required == QMetaType::fromType<QVariant>()
2212 || required == QMetaType::fromType<QJSValue>()
2213 || required == QMetaType::fromType<QJSManagedValue>()) {
2214 return true;
2215 }
2216
2217 if (data) {
2218 if (passed == QMetaType::fromType<QVariant>())
2219 passed = static_cast<const QVariant *>(data)->metaType();
2220 else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
2221 passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
2222 }
2223
2224 if (passed == required)
2225 return true;
2226
2227 if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
2228 switch (passed.id()) {
2229 case QMetaType::UnknownType:
2230 case QMetaType::Nullptr:
2231 case QMetaType::Bool:
2232 case QMetaType::Int:
2233 case QMetaType::Double:
2234 case QMetaType::QString:
2235 return true;
2236 default:
2237 break;
2238 }
2239 }
2240
2241 return false;
2242}
2243
2244bool QObjectMethod::isExactMatch(
2245 const QMetaMethod &method, void **argv, int argc, const QMetaType *types)
2246{
2247 if (types[0].isValid() && !ExactMatch(passed: method.returnMetaType(), required: types[0], data: nullptr))
2248 return false;
2249
2250 if (method.parameterCount() != argc)
2251 return false;
2252
2253 for (int i = 0; i < argc; ++i) {
2254 if (!ExactMatch(passed: types[i + 1], required: method.parameterMetaType(index: i), data: argv[i + 1]))
2255 return false;
2256 }
2257
2258 return true;
2259}
2260
2261const QQmlPropertyData *QObjectMethod::resolveOverloaded(
2262 const QQmlPropertyData *methods, int methodCount,
2263 void **argv, int argc, const QMetaType *types)
2264{
2265 // We only accept exact matches here. Everything else goes through the JavaScript conversion.
2266 for (int i = 0; i < methodCount; ++i) {
2267 const QQmlPropertyData *attempt = methods + i;
2268 if (isExactMatch(method: attempt->metaMethod(), argv, argc, types))
2269 return attempt;
2270 }
2271
2272 return nullptr;
2273}
2274
2275void CallArgument::cleanup()
2276{
2277 switch (type) {
2278 case QMetaType::QString:
2279 qstringPtr->~QString();
2280 break;
2281 case QMetaType::QByteArray:
2282 qbyteArrayPtr->~QByteArray();
2283 break;
2284 case QMetaType::QVariant:
2285 case QVariantWrappedType:
2286 qvariantPtr->~QVariant();
2287 break;
2288 case QMetaType::QJsonArray:
2289 jsonArrayPtr->~QJsonArray();
2290 break;
2291 case QMetaType::QJsonObject:
2292 jsonObjectPtr->~QJsonObject();
2293 break;
2294 case QMetaType::QJsonValue:
2295 jsonValuePtr->~QJsonValue();
2296 break;
2297 default:
2298 if (type == qMetaTypeId<QJSValue>()) {
2299 qjsValuePtr->~QJSValue();
2300 break;
2301 }
2302
2303 if (type == qMetaTypeId<QList<QObject *> >()) {
2304 qlistPtr->~QList<QObject *>();
2305 break;
2306 }
2307
2308 // The sequence types need no cleanup because we don't own them.
2309
2310 break;
2311 }
2312}
2313
2314void *CallArgument::dataPtr()
2315{
2316 switch (type) {
2317 case QMetaType::UnknownType:
2318 return nullptr;
2319 case QVariantWrappedType:
2320 return qvariantPtr->data();
2321 default:
2322 if (type == qMetaTypeId<std::vector<int>>())
2323 return stdVectorIntPtr;
2324 if (type == qMetaTypeId<std::vector<qreal>>())
2325 return stdVectorRealPtr;
2326 if (type == qMetaTypeId<std::vector<bool>>())
2327 return stdVectorBoolPtr;
2328 if (type == qMetaTypeId<std::vector<QString>>())
2329 return stdVectorQStringPtr;
2330 if (type == qMetaTypeId<std::vector<QUrl>>())
2331 return stdVectorQUrlPtr;
2332#if QT_CONFIG(qml_itemmodel)
2333 if (type == qMetaTypeId<std::vector<QModelIndex>>())
2334 return stdVectorQModelIndexPtr;
2335#endif
2336 break;
2337 }
2338
2339 return (void *)&allocData;
2340}
2341
2342void CallArgument::initAsType(QMetaType metaType)
2343{
2344 if (type != QMetaType::UnknownType)
2345 cleanup();
2346
2347 type = metaType.id();
2348 switch (type) {
2349 case QMetaType::Void:
2350 type = QMetaType::UnknownType;
2351 break;
2352 case QMetaType::UnknownType:
2353 case QMetaType::Int:
2354 case QMetaType::UInt:
2355 case QMetaType::Bool:
2356 case QMetaType::Double:
2357 case QMetaType::Float:
2358 break;
2359 case QMetaType::QObjectStar:
2360 qobjectPtr = nullptr;
2361 break;
2362 case QMetaType::QString:
2363 qstringPtr = new (&allocData) QString();
2364 break;
2365 case QMetaType::QVariant:
2366 qvariantPtr = new (&allocData) QVariant();
2367 break;
2368 case QMetaType::QJsonArray:
2369 jsonArrayPtr = new (&allocData) QJsonArray();
2370 break;
2371 case QMetaType::QJsonObject:
2372 jsonObjectPtr = new (&allocData) QJsonObject();
2373 break;
2374 case QMetaType::QJsonValue:
2375 jsonValuePtr = new (&allocData) QJsonValue();
2376 break;
2377 default: {
2378 if (metaType == QMetaType::fromType<QJSValue>()) {
2379 qjsValuePtr = new (&allocData) QJSValue();
2380 break;
2381 }
2382
2383 if (metaType == QMetaType::fromType<QList<QObject *>>()) {
2384 qlistPtr = new (&allocData) QList<QObject *>();
2385 break;
2386 }
2387
2388 type = QVariantWrappedType;
2389 qvariantPtr = new (&allocData) QVariant(metaType, (void *)nullptr);
2390 break;
2391 }
2392 }
2393}
2394
2395template <class T, class M>
2396bool CallArgument::fromContainerValue(const Value &value, M CallArgument::*member)
2397{
2398 if (const Sequence *sequence = value.as<Sequence>()) {
2399 if (T* ptr = static_cast<T *>(SequencePrototype::getRawContainerPtr(
2400 object: sequence, typeHint: QMetaType(type)))) {
2401 (this->*member) = ptr;
2402 return true;
2403 }
2404 }
2405 (this->*member) = nullptr;
2406 return false;
2407}
2408
2409bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const Value &value)
2410{
2411 if (type != QMetaType::UnknownType)
2412 cleanup();
2413
2414 type = metaType.id();
2415
2416 switch (type) {
2417 case QMetaType::Int:
2418 intValue = quint32(value.toInt32());
2419 return true;
2420 case QMetaType::UInt:
2421 intValue = quint32(value.toUInt32());
2422 return true;
2423 case QMetaType::Bool:
2424 boolValue = value.toBoolean();
2425 return true;
2426 case QMetaType::Double:
2427 doubleValue = double(value.toNumber());
2428 return true;
2429 case QMetaType::Float:
2430 floatValue = float(value.toNumber());
2431 return true;
2432 case QMetaType::QString:
2433 if (value.isNullOrUndefined())
2434 qstringPtr = new (&allocData) QString();
2435 else
2436 qstringPtr = new (&allocData) QString(value.toQStringNoThrow());
2437 return true;
2438 case QMetaType::QByteArray:
2439 qbyteArrayPtr = new (&allocData) QByteArray();
2440 ExecutionEngine::metaTypeFromJS(value, type: metaType, data: qbyteArrayPtr);
2441 return true;
2442 case QMetaType::QObjectStar:
2443 if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) {
2444 qobjectPtr = qobjectWrapper->object();
2445 return true;
2446 }
2447
2448 if (const QQmlTypeWrapper *qmlTypeWrapper = value.as<QQmlTypeWrapper>()) {
2449 if (qmlTypeWrapper->isSingleton()) {
2450 // Convert via QVariant below.
2451 // TODO: Can't we just do qobjectPtr = qmlTypeWrapper->object() instead?
2452 break;
2453 } else if (QObject *obj = qmlTypeWrapper->object()) {
2454 // attached object case
2455 qobjectPtr = obj;
2456 return true;
2457 }
2458
2459 // If this is a plain type wrapper without an instance,
2460 // then we got a namespace, and that's a type error
2461 type = QMetaType::UnknownType;
2462 return false;
2463 }
2464
2465 qobjectPtr = nullptr;
2466 return value.isNullOrUndefined(); // null and undefined are nullptr
2467 case QMetaType::QVariant:
2468 qvariantPtr = new (&allocData) QVariant(ExecutionEngine::toVariant(value, typeHint: QMetaType {}));
2469 return true;
2470 case QMetaType::QJsonArray: {
2471 Scope scope(engine);
2472 ScopedObject o(scope, value);
2473 jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(o));
2474 return true;
2475 }
2476 case QMetaType::QJsonObject: {
2477 Scope scope(engine);
2478 ScopedObject o(scope, value);
2479 jsonObjectPtr = new (&allocData) QJsonObject(JsonObject::toJsonObject(o));
2480 return true;
2481 }
2482 case QMetaType::QJsonValue:
2483 jsonValuePtr = new (&allocData) QJsonValue(JsonObject::toJsonValue(value));
2484 return true;
2485 case QMetaType::Void:
2486 type = QMetaType::UnknownType;
2487 // TODO: This only doesn't leak because a default constructed QVariant doesn't allocate.
2488 *qvariantPtr = QVariant();
2489 return true;
2490 default:
2491 if (type == qMetaTypeId<QJSValue>()) {
2492 qjsValuePtr = new (&allocData) QJSValue;
2493 Scope scope(engine);
2494 ScopedValue v(scope, value);
2495 QJSValuePrivate::setValue(jsval: qjsValuePtr, v);
2496 return true;
2497 }
2498
2499 if (type == qMetaTypeId<QList<QObject*> >()) {
2500 qlistPtr = new (&allocData) QList<QObject *>();
2501 Scope scope(engine);
2502 ScopedArrayObject array(scope, value);
2503 if (array) {
2504 Scoped<QObjectWrapper> qobjectWrapper(scope);
2505
2506 uint length = array->getLength();
2507 qlistPtr->reserve(size: length);
2508 for (uint ii = 0; ii < length; ++ii) {
2509 QObject *o = nullptr;
2510 qobjectWrapper = array->get(idx: ii);
2511 if (!!qobjectWrapper)
2512 o = qobjectWrapper->object();
2513 qlistPtr->append(t: o);
2514 }
2515 return true;
2516 }
2517
2518 if (const auto sequence = value.as<QV4::Sequence>()) {
2519 QV4::ReferenceObject::readReference(ref: sequence->d());
2520 uint length = sequence->size();
2521 if (sequence->d()->listType() == QMetaType::fromType<QList<QObject *>>()) {
2522 *qlistPtr = *static_cast<QList<QObject *> *>(sequence->getRawContainerPtr());
2523 } else {
2524 qlistPtr->reserve(size: length);
2525 Scoped<QObjectWrapper> qobjectWrapper(scope);
2526 for (uint ii = 0; ii < length; ++ii) {
2527 QObject *o = nullptr;
2528 qobjectWrapper = sequence->get(idx: ii);
2529 if (!!qobjectWrapper)
2530 o = qobjectWrapper->object();
2531 qlistPtr->append(t: o);
2532 }
2533 }
2534 return true;
2535 }
2536
2537 if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) {
2538 qlistPtr->append(t: qobjectWrapper->object());
2539 return true;
2540 }
2541
2542 if (const QmlListWrapper *listWrapper = value.as<QmlListWrapper>()) {
2543 *qlistPtr = listWrapper->d()->property()->toList<QList<QObject *>>();
2544 return true;
2545 }
2546
2547 qlistPtr->append(t: nullptr);
2548 return value.isNullOrUndefined();
2549 }
2550
2551 if (metaType.flags() & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) {
2552 // You can assign null or undefined to any pointer. The result is a nullptr.
2553 if (value.isNullOrUndefined()) {
2554 qvariantPtr = new (&allocData) QVariant(metaType, nullptr);
2555 return true;
2556 }
2557 break;
2558 }
2559
2560 if (type == qMetaTypeId<std::vector<int>>()) {
2561 if (fromContainerValue<std::vector<int>>(value, member: &CallArgument::stdVectorIntPtr))
2562 return true;
2563 } else if (type == qMetaTypeId<std::vector<qreal>>()) {
2564 if (fromContainerValue<std::vector<qreal>>(value, member: &CallArgument::stdVectorRealPtr))
2565 return true;
2566 } else if (type == qMetaTypeId<std::vector<bool>>()) {
2567 if (fromContainerValue<std::vector<bool>>(value, member: &CallArgument::stdVectorBoolPtr))
2568 return true;
2569 } else if (type == qMetaTypeId<std::vector<QString>>()) {
2570 if (fromContainerValue<std::vector<QString>>(value, member: &CallArgument::stdVectorQStringPtr))
2571 return true;
2572 } else if (type == qMetaTypeId<std::vector<QUrl>>()) {
2573 if (fromContainerValue<std::vector<QUrl>>(value, member: &CallArgument::stdVectorQUrlPtr))
2574 return true;
2575#if QT_CONFIG(qml_itemmodel)
2576 } else if (type == qMetaTypeId<std::vector<QModelIndex>>()) {
2577 if (fromContainerValue<std::vector<QModelIndex>>(
2578 value, member: &CallArgument::stdVectorQModelIndexPtr)) {
2579 return true;
2580 }
2581#endif
2582 }
2583 break;
2584 }
2585
2586 // Convert via QVariant through the QML engine.
2587 qvariantPtr = new (&allocData) QVariant(metaType);
2588 type = QVariantWrappedType;
2589
2590 if (ExecutionEngine::metaTypeFromJS(value, type: metaType, data: qvariantPtr->data()))
2591 return true;
2592
2593 const QVariant v = ExecutionEngine::toVariant(value, typeHint: metaType);
2594 return QMetaType::convert(fromType: v.metaType(), from: v.constData(), toType: metaType, to: qvariantPtr->data());
2595}
2596
2597ReturnedValue CallArgument::toValue(ExecutionEngine *engine)
2598{
2599 switch (type) {
2600 case QMetaType::Int:
2601 return Encode(int(intValue));
2602 case QMetaType::UInt:
2603 return Encode((uint)intValue);
2604 case QMetaType::Bool:
2605 return Encode(boolValue);
2606 case QMetaType::Double:
2607 return Encode(doubleValue);
2608 case QMetaType::Float:
2609 return Encode(floatValue);
2610 case QMetaType::QString:
2611 return Encode(engine->newString(s: *qstringPtr));
2612 case QMetaType::QByteArray:
2613 return Encode(engine->newArrayBuffer(array: *qbyteArrayPtr));
2614 case QMetaType::QObjectStar:
2615 if (qobjectPtr)
2616 QQmlData::get(object: qobjectPtr, create: true)->setImplicitDestructible();
2617 return QObjectWrapper::wrap(engine, object: qobjectPtr);
2618 case QMetaType::QJsonArray:
2619 return JsonObject::fromJsonArray(engine, array: *jsonArrayPtr);
2620 case QMetaType::QJsonObject:
2621 return JsonObject::fromJsonObject(engine, object: *jsonObjectPtr);
2622 case QMetaType::QJsonValue:
2623 return JsonObject::fromJsonValue(engine, value: *jsonValuePtr);
2624 case QMetaType::QVariant:
2625 case QVariantWrappedType: {
2626 Scope scope(engine);
2627 ScopedValue rv(scope, scope.engine->fromVariant(*qvariantPtr));
2628 Scoped<QObjectWrapper> qobjectWrapper(scope, rv);
2629 if (!!qobjectWrapper) {
2630 if (QObject *object = qobjectWrapper->object())
2631 QQmlData::get(object, create: true)->setImplicitDestructible();
2632 }
2633 return rv->asReturnedValue();
2634 }
2635 default:
2636 break;
2637 }
2638
2639 if (type == qMetaTypeId<QJSValue>()) {
2640 // The QJSValue can be passed around via dataPtr()
2641 QJSValuePrivate::manageStringOnV4Heap(e: engine, jsval: qjsValuePtr);
2642 return QJSValuePrivate::asReturnedValue(jsval: qjsValuePtr);
2643 }
2644
2645 if (type == qMetaTypeId<QList<QObject *> >()) {
2646 // XXX Can this be made more by using Array as a prototype and implementing
2647 // directly against QList<QObject*>?
2648 QList<QObject *> &list = *qlistPtr;
2649 Scope scope(engine);
2650 ScopedArrayObject array(scope, engine->newArrayObject());
2651 array->arrayReserve(n: list.size());
2652 ScopedValue v(scope);
2653 for (int ii = 0; ii < list.size(); ++ii)
2654 array->arrayPut(index: ii, value: (v = QObjectWrapper::wrap(engine, object: list.at(i: ii))));
2655 array->setArrayLengthUnchecked(list.size());
2656 return array.asReturnedValue();
2657 }
2658
2659 return Encode::undefined();
2660}
2661
2662ReturnedValue QObjectMethod::create(ExecutionEngine *engine, Heap::Object *wrapper, int index)
2663{
2664 Scope valueScope(engine);
2665 Scoped<QObjectMethod> method(
2666 valueScope,
2667 engine->memoryManager->allocate<QObjectMethod>(args&: engine, args&: wrapper, args&: index));
2668 return method.asReturnedValue();
2669}
2670
2671ReturnedValue QObjectMethod::create(
2672 ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *valueType, int index)
2673{
2674 Scope valueScope(engine);
2675 Scoped<QObjectMethod> method(
2676 valueScope,
2677 engine->memoryManager->allocate<QObjectMethod>(args&: engine, args&: valueType, args&: index));
2678 return method.asReturnedValue();
2679}
2680
2681ReturnedValue QObjectMethod::create(
2682 ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
2683 Heap::Object *wrapper, Heap::Object *object)
2684{
2685 Scope valueScope(engine);
2686
2687 Scoped<QQmlValueTypeWrapper> valueTypeWrapper(valueScope);
2688 if (cloneFrom->wrapper) {
2689 Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->wrapper);
2690 if (ref) {
2691 valueTypeWrapper = QQmlValueTypeWrapper::create(engine, cloneFrom: ref->d(), object: wrapper);
2692 } else {
2693 // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what
2694 // value we should operate on. Without knowledge of the property the value
2695 // was read from, we cannot load the value from the given object.
2696 return Encode::undefined();
2697 }
2698 }
2699
2700 Scoped<QObjectMethod> method(
2701 valueScope,
2702 engine->memoryManager->allocate<QV4::QObjectMethod>(
2703 args&: engine, args: valueTypeWrapper ? valueTypeWrapper->d() : object, args&: cloneFrom->index));
2704
2705 method->d()->methodCount = cloneFrom->methodCount;
2706
2707 Q_ASSERT(method->d()->methods == nullptr);
2708 switch (cloneFrom->methodCount) {
2709 case 0:
2710 Q_ASSERT(cloneFrom->methods == nullptr);
2711 break;
2712 case 1:
2713 Q_ASSERT(cloneFrom->methods
2714 == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod));
2715 method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod);
2716 *method->d()->methods = *cloneFrom->methods;
2717 break;
2718 default:
2719 Q_ASSERT(cloneFrom->methods != nullptr);
2720 method->d()->methods = new QQmlPropertyData[cloneFrom->methodCount];
2721 memcpy(dest: method->d()->methods, src: cloneFrom->methods,
2722 n: cloneFrom->methodCount * sizeof(QQmlPropertyData));
2723 break;
2724 }
2725
2726 return method.asReturnedValue();
2727}
2728
2729void Heap::QObjectMethod::init(QV4::ExecutionEngine *engine, Object *object, int methodIndex)
2730{
2731 Heap::FunctionObject::init(engine);
2732 wrapper.set(e: engine, newVal: object);
2733 index = methodIndex;
2734}
2735
2736const QMetaObject *Heap::QObjectMethod::metaObject() const
2737{
2738 Scope scope(internalClass->engine);
2739
2740 if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
2741 return valueWrapper->metaObject();
2742 if (QObject *self = object())
2743 return self->metaObject();
2744
2745 return nullptr;
2746}
2747
2748QObject *Heap::QObjectMethod::object() const
2749{
2750 Scope scope(internalClass->engine);
2751
2752 if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
2753 return objectWrapper->object();
2754 if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
2755 return typeWrapper->object();
2756 return nullptr;
2757}
2758
2759bool Heap::QObjectMethod::isDetached() const
2760{
2761 if (!wrapper)
2762 return true;
2763
2764 QV4::Scope scope(internalClass->engine);
2765 if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
2766 return valueWrapper->d()->object() == nullptr;
2767
2768 return false;
2769}
2770
2771bool Heap::QObjectMethod::isAttachedTo(QObject *o) const
2772{
2773 QV4::Scope scope(internalClass->engine);
2774 if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
2775 return objectWrapper->object() == o;
2776 if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
2777 return typeWrapper->object() == o;
2778
2779 if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper) {
2780 QV4::Scope scope(wrapper->internalClass->engine);
2781 if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, valueWrapper->d()->object()); qobject)
2782 return qobject->object() == o;
2783 if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, valueWrapper->d()->object()); type)
2784 return type->object() == o;
2785
2786 // Attached to some nested value type or sequence object
2787 return false;
2788 }
2789
2790 return false;
2791}
2792
2793Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
2794 const QMetaObject *thisMeta) const
2795{
2796 // Check that the metaobject matches.
2797
2798 if (!thisMeta) {
2799 // You can only get a detached method via a lookup, and then you have a thisObject.
2800 Q_ASSERT(wrapper);
2801 return Included;
2802 }
2803
2804 const auto check = [&](const QMetaObject *included) {
2805 const auto stackFrame = internalClass->engine->currentStackFrame;
2806 if (stackFrame && !stackFrame->v4Function->executableCompilationUnit()
2807 ->nativeMethodsAcceptThisObjects()) {
2808 qCWarning(lcMethodBehavior,
2809 "%s:%d: Calling C++ methods with 'this' objects different from the one "
2810 "they were retrieved from is broken, due to historical reasons. The "
2811 "original object is used as 'this' object. You can allow the given "
2812 "'this' object to be used by setting "
2813 "'pragma NativeMethodBehavior: AcceptThisObject'",
2814 qPrintable(stackFrame->source()), stackFrame->lineNumber());
2815 return Included;
2816 }
2817
2818 // destroy() and toString() can be called on all QObjects, but not on gadgets.
2819 if (index < 0)
2820 return thisMeta->inherits(metaObject: &QObject::staticMetaObject) ? Explicit : Invalid;
2821
2822 // Find the base type the method belongs to.
2823 int methodOffset = included->methodOffset();
2824 while (true) {
2825 if (included == thisMeta)
2826 return Explicit;
2827
2828 if (methodOffset <= index)
2829 return thisMeta->inherits(metaObject: included) ? Explicit : Invalid;
2830
2831 included = included->superClass();
2832 Q_ASSERT(included);
2833 methodOffset -= QMetaObjectPrivate::get(metaobject: included)->methodCount;
2834 };
2835
2836 Q_UNREACHABLE_RETURN(Invalid);
2837 };
2838
2839 if (const QMetaObject *meta = metaObject())
2840 return check(meta);
2841
2842 // If the QObjectMethod is detached, we can only have gotten here via a lookup.
2843 // The lookup checks that the QQmlPropertyCache matches.
2844 return Explicit;
2845}
2846
2847QString Heap::QObjectMethod::name() const
2848{
2849 if (index == QV4::QObjectMethod::DestroyMethod)
2850 return QStringLiteral("destroy");
2851 else if (index == QV4::QObjectMethod::ToStringMethod)
2852 return QStringLiteral("toString");
2853
2854 const QMetaObject *mo = metaObject();
2855 if (!mo)
2856 return QString();
2857
2858 int methodOffset = mo->methodOffset();
2859 while (methodOffset > index) {
2860 mo = mo->superClass();
2861 methodOffset -= QMetaObjectPrivate::get(metaobject: mo)->methodCount;
2862 }
2863
2864 return "%1::%2"_L1.arg(args: QLatin1StringView{mo->className()},
2865 args: QLatin1StringView{mo->method(index).name()});
2866}
2867
2868void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
2869{
2870 if (methods) {
2871 Q_ASSERT(methodCount > 0);
2872 return;
2873 }
2874
2875 const QMetaObject *mo = metaObject();
2876
2877 if (!mo)
2878 mo = thisMeta;
2879
2880 Q_ASSERT(mo);
2881
2882 int methodOffset = mo->methodOffset();
2883 while (methodOffset > index) {
2884 mo = mo->superClass();
2885 methodOffset -= QMetaObjectPrivate::get(metaobject: mo)->methodCount;
2886 }
2887 QVarLengthArray<QQmlPropertyData, 9> resolvedMethods;
2888 QQmlPropertyData dummy;
2889 QMetaMethod method = mo->method(index);
2890 dummy.load(method);
2891 dummy.setMetaObject(mo);
2892 resolvedMethods.append(t: dummy);
2893 // Look for overloaded methods
2894 QByteArray methodName = method.name();
2895 for (int ii = index - 1; ii >= methodOffset; --ii) {
2896 if (methodName == mo->method(index: ii).name()) {
2897 method = mo->method(index: ii);
2898 dummy.load(method);
2899 resolvedMethods.append(t: dummy);
2900 }
2901 }
2902 if (resolvedMethods.size() > 1) {
2903 methods = new QQmlPropertyData[resolvedMethods.size()];
2904 memcpy(dest: methods, src: resolvedMethods.data(), n: resolvedMethods.size()*sizeof(QQmlPropertyData));
2905 methodCount = resolvedMethods.size();
2906 } else {
2907 methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod);
2908 *methods = resolvedMethods.at(idx: 0);
2909 methodCount = 1;
2910 }
2911
2912 Q_ASSERT(methodCount > 0);
2913}
2914
2915ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, QObject *o) const
2916{
2917 return engine->newString(
2918 s: QObjectWrapper::objectToString(
2919 engine, metaObject: o ? o->metaObject() : d()->metaObject(), object: o))->asReturnedValue();
2920}
2921
2922ReturnedValue QObjectMethod::method_destroy(
2923 ExecutionEngine *engine, QObject *o, const Value *args, int argc) const
2924{
2925 if (!o)
2926 return Encode::undefined();
2927
2928 if (QQmlData::keepAliveDuringGarbageCollection(object: o))
2929 return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2930
2931 int delay = 0;
2932 if (argc > 0)
2933 delay = args[0].toUInt32();
2934
2935 if (delay > 0)
2936 QTimer::singleShot(msec: delay, receiver: o, SLOT(deleteLater()));
2937 else
2938 o->deleteLater();
2939
2940 return Encode::undefined();
2941}
2942
2943ReturnedValue QObjectMethod::virtualCall(
2944 const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2945{
2946 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2947 return This->callInternal(thisObject, argv, argc);
2948}
2949
2950void QObjectMethod::virtualCallWithMetaTypes(
2951 const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
2952{
2953 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2954 This->callInternalWithMetaTypes(thisObject, argv, types, argc);
2955}
2956
2957ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const
2958{
2959 ExecutionEngine *v4 = engine();
2960
2961 const QMetaObject *thisMeta = nullptr;
2962
2963 QObject *o = nullptr;
2964 Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
2965 if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
2966 thisMeta = w->metaObject();
2967 o = w->object();
2968 } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
2969 thisMeta = w->metaObject();
2970 o = w->object();
2971 } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
2972 thisMeta = w->metaObject();
2973 valueWrapper = w->d();
2974 }
2975
2976 Heap::QObjectMethod::ThisObjectMode mode = Heap::QObjectMethod::Invalid;
2977 if (o && o == d()->object()) {
2978 mode = Heap::QObjectMethod::Explicit;
2979 // Nothing to do; objects are the same. This should be common
2980 } else if (valueWrapper && valueWrapper == d()->wrapper) {
2981 mode = Heap::QObjectMethod::Explicit;
2982 // Nothing to do; gadgets are the same. This should be somewhat common
2983 } else {
2984 mode = d()->checkThisObject(thisMeta);
2985 if (mode == Heap::QObjectMethod::Invalid) {
2986 v4->throwError(message: QLatin1String("Cannot call method %1 on %2").arg(
2987 args: d()->name(), args: thisObject->toQStringNoThrow()));
2988 return Encode::undefined();
2989 }
2990 }
2991
2992 QQmlObjectOrGadget object = [&](){
2993 if (mode == Heap::QObjectMethod::Included) {
2994 QV4::Scope scope(v4);
2995 if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, d()->wrapper); qobject)
2996 return QQmlObjectOrGadget(qobject->object());
2997 if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, d()->wrapper); type)
2998 return QQmlObjectOrGadget(type->object());
2999 if (QV4::Scoped<QV4::QQmlValueTypeWrapper> value(scope, d()->wrapper); value) {
3000 valueWrapper = value->d();
3001 return QQmlObjectOrGadget(valueWrapper->metaObject(), valueWrapper->gadgetPtr());
3002 }
3003 Q_UNREACHABLE();
3004 } else {
3005 if (o)
3006 return QQmlObjectOrGadget(o);
3007
3008 Q_ASSERT(valueWrapper);
3009 if (!valueWrapper->enforcesLocation())
3010 QV4::ReferenceObject::readReference(ref: valueWrapper);
3011 return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr());
3012 }
3013 }();
3014
3015 if (object.isNull())
3016 return Encode::undefined();
3017
3018 if (d()->index == DestroyMethod)
3019 return method_destroy(engine: v4, o: object.qObject(), args: argv, argc);
3020 else if (d()->index == ToStringMethod)
3021 return method_toString(engine: v4, o: object.qObject());
3022
3023 d()->ensureMethodsCache(thisMeta);
3024
3025 Scope scope(v4);
3026 JSCallData cData(thisObject, argv, argc);
3027 CallData *callData = cData.callData(scope);
3028
3029 const QQmlPropertyData *method = d()->methods;
3030
3031 // If we call the method, we have to write back any value type references afterwards.
3032 // The method might change the value.
3033 const auto doCall = [&](const auto &call) {
3034 if (!method->isConstant()) {
3035 if (valueWrapper && valueWrapper->isReference()) {
3036 ScopedValue rv(scope, call());
3037 valueWrapper->writeBack();
3038 return rv->asReturnedValue();
3039 }
3040 }
3041
3042 return call();
3043 };
3044
3045 if (d()->methodCount != 1) {
3046 Q_ASSERT(d()->methodCount > 0);
3047 method = resolveOverloaded(object, methods: d()->methods, methodCount: d()->methodCount, engine: v4, callArgs: callData);
3048 if (method == nullptr)
3049 return Encode::undefined();
3050 }
3051
3052 if (method->isV4Function()) {
3053 return doCall([&]() {
3054 ScopedValue rv(scope, Value::undefinedValue());
3055 QQmlV4Function func(callData, rv, v4);
3056 QQmlV4FunctionPtr funcptr = &func;
3057
3058 void *args[] = { nullptr, &funcptr };
3059 object.metacall(type: QMetaObject::InvokeMetaMethod, index: method->coreIndex(), argv: args);
3060
3061 return rv->asReturnedValue();
3062 });
3063 }
3064
3065 return doCall([&]() { return callPrecise(object, data: *method, engine: v4, callArgs: callData); });
3066}
3067
3068struct ToStringMetaMethod
3069{
3070 constexpr int parameterCount() const { return 0; }
3071 constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
3072 constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
3073};
3074
3075void QObjectMethod::callInternalWithMetaTypes(
3076 QObject *thisObject, void **argv, const QMetaType *types, int argc) const
3077{
3078 ExecutionEngine *v4 = engine();
3079
3080 const QMetaObject *thisMeta = nullptr;
3081 Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
3082
3083 if (thisObject) {
3084 thisMeta = thisObject->metaObject();
3085 } else {
3086 Q_ASSERT(Value::fromHeapObject(d()->wrapper).as<QQmlValueTypeWrapper>());
3087 valueWrapper = d()->wrapper.cast<Heap::QQmlValueTypeWrapper>();
3088 thisMeta = valueWrapper->metaObject();
3089 }
3090
3091 QQmlObjectOrGadget object = [&](){
3092 if (thisObject)
3093 return QQmlObjectOrGadget(thisObject);
3094
3095 Scope scope(v4);
3096 Scoped<QQmlValueTypeWrapper> wrapper(scope, d()->wrapper);
3097 Q_ASSERT(wrapper);
3098
3099 Heap::QQmlValueTypeWrapper *valueWrapper = wrapper->d();
3100 if (!valueWrapper->enforcesLocation())
3101 QV4::ReferenceObject::readReference(ref: valueWrapper);
3102 return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr());
3103 }();
3104
3105 if (object.isNull())
3106 return;
3107
3108 if (d()->index == DestroyMethod) {
3109 // method_destroy will use at most one argument
3110 QV4::convertAndCall(
3111 engine: v4, thisObject, a: argv, types, argc: std::min(a: argc, b: 1),
3112 call: [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
3113 Q_UNUSED(thisObject);
3114 return method_destroy(engine: v4, o: object.qObject(), args: argv, argc);
3115 });
3116 return;
3117 }
3118
3119 if (d()->index == ToStringMethod) {
3120 const ToStringMetaMethod metaMethod;
3121 QV4::coerceAndCall(
3122 engine: v4, typedFunction: &metaMethod, argv, types, argc,
3123 call: [v4, thisMeta, object](void **argv, int) {
3124 *static_cast<QString *>(argv[0])
3125 = QObjectWrapper::objectToString(engine: v4, metaObject: thisMeta, object: object.qObject());
3126 });
3127 return;
3128 }
3129
3130 d()->ensureMethodsCache(thisMeta);
3131
3132 const QQmlPropertyData *method = d()->methods;
3133 if (d()->methodCount != 1) {
3134 Q_ASSERT(d()->methodCount > 0);
3135 method = resolveOverloaded(methods: d()->methods, methodCount: d()->methodCount, argv, argc, types);
3136 }
3137
3138 if (!method || method->isV4Function()) {
3139 QV4::convertAndCall(
3140 engine: v4, thisObject, a: argv, types, argc,
3141 call: [this](const Value *thisObject, const Value *argv, int argc) {
3142 return callInternal(thisObject, argv, argc);
3143 });
3144 } else {
3145 const QMetaMethod metaMethod = method->metaMethod();
3146 QV4::coerceAndCall(
3147 engine: v4, typedFunction: &metaMethod, argv, types, argc,
3148 call: [v4, object, valueWrapper, method](void **argv, int argc) {
3149 Q_UNUSED(argc);
3150
3151 // If we call the method, we have to write back any value type references afterwards.
3152 // The method might change the value.
3153 object.metacall(type: QMetaObject::InvokeMetaMethod, index: method->coreIndex(), argv);
3154 if (!method->isConstant()) {
3155 if (valueWrapper && valueWrapper->isReference())
3156 valueWrapper->writeBack();
3157 }
3158
3159 // If the method returns a QObject* we need to track it on the JS heap
3160 // (if it's destructible).
3161 QObject *qobjectPtr = nullptr;
3162 const QMetaType resultType = method->propType();
3163 if (argv[0]) {
3164 if (resultType.flags() & QMetaType::PointerToQObject) {
3165 qobjectPtr = *static_cast<QObject **>(argv[0]);
3166 } else if (resultType == QMetaType::fromType<QVariant>()) {
3167 const QVariant *result = static_cast<const QVariant *>(argv[0]);
3168 const QMetaType variantType = result->metaType();
3169 if (variantType.flags() & QMetaType::PointerToQObject)
3170 qobjectPtr = *static_cast<QObject *const *>(result->data());
3171 }
3172 }
3173
3174 if (qobjectPtr) {
3175 QQmlData *ddata = QQmlData::get(object: qobjectPtr, create: true);
3176 if (!ddata->explicitIndestructibleSet) {
3177 ddata->indestructible = false;
3178 QObjectWrapper::ensureWrapper(engine: v4, object: qobjectPtr);
3179 }
3180 }
3181 });
3182 }
3183}
3184
3185DEFINE_OBJECT_VTABLE(QObjectMethod);
3186
3187void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
3188{
3189 Object::init();
3190 this->signalIndex = signalIndex;
3191 setObject(object);
3192}
3193
3194DEFINE_OBJECT_VTABLE(QmlSignalHandler);
3195
3196ReturnedValue QmlSignalHandler::call(const Value *thisObject, const Value *argv, int argc) const
3197{
3198 const QString handlerName = QQmlSignalNames::signalNameToHandlerName(
3199 signal: object()->metaObject()->method(index: signalIndex()).name());
3200 qCWarning(lcSignalHandler).noquote()
3201 << QStringLiteral("Property '%1' of object %2 is a signal handler. You should "
3202 "not call it directly. Make it a proper function and call "
3203 "that or emit the signal.")
3204 .arg(args: handlerName, args: thisObject->toQStringNoThrow());
3205
3206 Scope scope(engine());
3207 Scoped<QObjectMethod> method(
3208 scope, QObjectMethod::create(
3209 engine: scope.engine,
3210 wrapper: static_cast<Heap::QObjectWrapper *>(nullptr),
3211 index: signalIndex()));
3212
3213 return method->call(thisObject, argv, argc);
3214}
3215
3216void QmlSignalHandler::initProto(ExecutionEngine *engine)
3217{
3218 if (engine->signalHandlerPrototype()->d_unchecked())
3219 return;
3220
3221 Scope scope(engine);
3222 ScopedObject o(scope, engine->newObject());
3223 ScopedString connect(scope, engine->newIdentifier(QStringLiteral("connect")));
3224 ScopedString disconnect(scope, engine->newIdentifier(QStringLiteral("disconnect")));
3225 o->put(name: connect, v: ScopedValue(scope, engine->functionPrototype()->get(name: connect)));
3226 o->put(name: disconnect, v: ScopedValue(scope, engine->functionPrototype()->get(name: disconnect)));
3227
3228 engine->jsObjects[ExecutionEngine::SignalHandlerProto] = o->d();
3229}
3230
3231
3232MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(
3233 MultiplyWrappedQObjectMap::Iterator it)
3234{
3235 const QObjectBiPointer key = it.key();
3236 const QObject *obj = key.isT1() ? key.asT1() : key.asT2();
3237 disconnect(sender: obj, signal: &QObject::destroyed, receiver: this, slot: &MultiplyWrappedQObjectMap::removeDestroyedObject);
3238 return QHash<QObjectBiPointer, WeakValue>::erase(it);
3239}
3240
3241void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
3242{
3243 QHash<QObjectBiPointer, WeakValue>::remove(key: object);
3244 QHash<QObjectBiPointer, WeakValue>::remove(key: static_cast<const QObject *>(object));
3245}
3246
3247} // namespace QV4
3248
3249QT_END_NAMESPACE
3250
3251#include "moc_qv4qobjectwrapper_p.cpp"
3252

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtdeclarative/src/qml/jsruntime/qv4qobjectwrapper.cpp