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

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