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