1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qv4qobjectwrapper_p.h"
41
42#include <private/qqmlstaticmetaobject_p.h>
43#include <private/qqmlengine_p.h>
44#include <private/qqmlvmemetaobject_p.h>
45#include <private/qqmlbinding_p.h>
46#include <private/qjsvalue_p.h>
47#include <private/qqmlexpression_p.h>
48#include <private/qqmlglobal_p.h>
49#include <private/qqmltypewrapper_p.h>
50#include <private/qqmlvaluetypewrapper_p.h>
51#include <private/qqmllistwrapper_p.h>
52#include <private/qqmlbuiltinfunctions_p.h>
53
54#include <private/qv4arraybuffer_p.h>
55#include <private/qv4functionobject_p.h>
56#include <private/qv4runtime_p.h>
57#include <private/qv4variantobject_p.h>
58#include <private/qv4identifiertable_p.h>
59#include <private/qv4lookup_p.h>
60#include <private/qv4qmlcontext_p.h>
61
62#if QT_CONFIG(qml_sequence_object)
63#include <private/qv4sequenceobject_p.h>
64#endif
65
66#include <private/qv4objectproto_p.h>
67#include <private/qv4jsonobject_p.h>
68#include <private/qv4regexpobject_p.h>
69#include <private/qv4dateobject_p.h>
70#include <private/qv4scopedvalue_p.h>
71#include <private/qv4jscall_p.h>
72#include <private/qv4mm_p.h>
73#include <private/qqmlscriptstring_p.h>
74#include <private/qv4compileddata_p.h>
75
76#include <QtQml/qjsvalue.h>
77#include <QtCore/qjsonarray.h>
78#include <QtCore/qjsonobject.h>
79#include <QtCore/qjsonvalue.h>
80#include <QtCore/qvarlengtharray.h>
81#include <QtCore/qtimer.h>
82#include <QtCore/qatomic.h>
83#include <QtCore/qmetaobject.h>
84#if QT_CONFIG(qml_itemmodel)
85#include <QtCore/qabstractitemmodel.h>
86#endif
87#include <QtCore/qloggingcategory.h>
88
89#include <vector>
90QT_BEGIN_NAMESPACE
91
92Q_LOGGING_CATEGORY(lcBindingRemoval, "qt.qml.binding.removal", QtWarningMsg)
93
94// The code in this file does not violate strict aliasing, but GCC thinks it does
95// so turn off the warnings for us to have a clean build
96QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
97
98using namespace QV4;
99
100QPair<QObject *, int> QObjectMethod::extractQtMethod(const QV4::FunctionObject *function)
101{
102 QV4::ExecutionEngine *v4 = function->engine();
103 if (v4) {
104 QV4::Scope scope(v4);
105 QV4::Scoped<QObjectMethod> method(scope, function->as<QObjectMethod>());
106 if (method)
107 return qMakePair(x: method->object(), y: method->methodIndex());
108 }
109
110 return qMakePair(x: (QObject *)nullptr, y: -1);
111}
112
113static QPair<QObject *, int> extractQtSignal(const QV4::Value &value)
114{
115 if (value.isObject()) {
116 QV4::ExecutionEngine *v4 = value.as<QV4::Object>()->engine();
117 QV4::Scope scope(v4);
118 QV4::ScopedFunctionObject function(scope, value);
119 if (function)
120 return QObjectMethod::extractQtMethod(function);
121
122 QV4::Scoped<QV4::QmlSignalHandler> handler(scope, value);
123 if (handler)
124 return qMakePair(x: handler->object(), y: handler->signalIndex());
125 }
126
127 return qMakePair(x: (QObject *)nullptr, y: -1);
128}
129
130static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object,
131 const QQmlPropertyData &property)
132{
133 Q_ASSERT(!property.isFunction());
134 QV4::Scope scope(v4);
135
136 if (property.isQObject()) {
137 QObject *rv = nullptr;
138 property.readProperty(target: object, property: &rv);
139 return QV4::QObjectWrapper::wrap(engine: v4, object: rv);
140 } else if (property.isQList()) {
141 return QmlListWrapper::create(engine: v4, object, propId: property.coreIndex(), propType: property.propType());
142 } else if (property.propType() == QMetaType::QReal) {
143 qreal v = 0;
144 property.readProperty(target: object, property: &v);
145 return QV4::Encode(v);
146 } else if (property.propType() == QMetaType::Int || property.isEnum()) {
147 int v = 0;
148 property.readProperty(target: object, property: &v);
149 return QV4::Encode(v);
150 } else if (property.propType() == QMetaType::Bool) {
151 bool v = false;
152 property.readProperty(target: object, property: &v);
153 return QV4::Encode(v);
154 } else if (property.propType() == QMetaType::QString) {
155 QString v;
156 property.readProperty(target: object, property: &v);
157 return v4->newString(s: v)->asReturnedValue();
158 } else if (property.propType() == QMetaType::UInt) {
159 uint v = 0;
160 property.readProperty(target: object, property: &v);
161 return QV4::Encode(v);
162 } else if (property.propType() == QMetaType::Float) {
163 float v = 0;
164 property.readProperty(target: object, property: &v);
165 return QV4::Encode(v);
166 } else if (property.propType() == QMetaType::Double) {
167 double v = 0;
168 property.readProperty(target: object, property: &v);
169 return QV4::Encode(v);
170 } else if (property.propType() == qMetaTypeId<QJSValue>()) {
171 QJSValue v;
172 property.readProperty(target: object, property: &v);
173 return QJSValuePrivate::convertedToValue(e: v4, jsval: v);
174 } else if (property.isQVariant()) {
175 QVariant v;
176 property.readProperty(target: object, property: &v);
177
178 if (QQmlValueTypeFactory::isValueType(idx: v.userType())) {
179 if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type: v.userType()))
180 return QV4::QQmlValueTypeWrapper::create(engine: v4, object, property.coreIndex(), metaObject: valueTypeMetaObject, typeId: v.userType()); // VariantReference value-type.
181 }
182
183 return scope.engine->fromVariant(v);
184 } else if (QQmlValueTypeFactory::isValueType(idx: property.propType())) {
185 if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type: property.propType()))
186 return QV4::QQmlValueTypeWrapper::create(engine: v4, object, property.coreIndex(), metaObject: valueTypeMetaObject, typeId: property.propType());
187 } else {
188#if QT_CONFIG(qml_sequence_object)
189 // see if it's a sequence type
190 bool succeeded = false;
191 QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(engine: v4, sequenceTypeId: property.propType(), object, propertyIndex: property.coreIndex(), readOnly: !property.isWritable(), succeeded: &succeeded));
192 if (succeeded)
193 return retn->asReturnedValue();
194#endif
195 }
196
197 if (property.propType() == QMetaType::UnknownType) {
198 QMetaProperty p = object->metaObject()->property(index: property.coreIndex());
199 qWarning(msg: "QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
200 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
201 return QV4::Encode::undefined();
202 } else {
203 QVariant v(property.propType(), (void *)nullptr);
204 property.readProperty(target: object, property: v.data());
205 return scope.engine->fromVariant(v);
206 }
207}
208
209void QObjectWrapper::initializeBindings(ExecutionEngine *engine)
210{
211 engine->functionPrototype()->defineDefaultProperty(QStringLiteral("connect"), code: method_connect);
212 engine->functionPrototype()->defineDefaultProperty(QStringLiteral("disconnect"), code: method_disconnect);
213}
214
215QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const
216{
217 QObject *o = d()->object();
218 return findProperty(engine, o, qmlContext, name, revisionMode, local);
219}
220
221QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local)
222{
223 Q_UNUSED(revisionMode);
224
225 QQmlData *ddata = QQmlData::get(object: o, create: false);
226 QQmlPropertyData *result = nullptr;
227 if (ddata && ddata->propertyCache)
228 result = ddata->propertyCache->property(key: name, object: o, context: qmlContext);
229 else
230 result = QQmlPropertyCache::property(engine->jsEngine(), o, name, qmlContext, *local);
231 return result;
232}
233
234ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property)
235{
236 QQmlData::flushPendingBinding(o: object, propertyIndex: QQmlPropertyIndex(property->coreIndex()));
237
238 if (property->isFunction() && !property->isVarProperty()) {
239 if (property->isVMEFunction()) {
240 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: object);
241 Q_ASSERT(vmemo);
242 return vmemo->vmeMethod(index: property->coreIndex());
243 } else if (property->isV4Function()) {
244 Scope scope(engine);
245 ScopedContext global(scope, engine->qmlContext());
246 if (!global)
247 global = engine->rootContext();
248 return QV4::QObjectMethod::create(scope: global, object, index: property->coreIndex());
249 } else if (property->isSignalHandler()) {
250 QmlSignalHandler::initProto(v4: engine);
251 return engine->memoryManager->allocate<QV4::QmlSignalHandler>(args: object, args: property->coreIndex())->asReturnedValue();
252 } else {
253 ExecutionContext *global = engine->rootContext();
254 return QV4::QObjectMethod::create(scope: global, object, index: property->coreIndex());
255 }
256 }
257
258 QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(e: engine->qmlEngine()) : nullptr;
259
260 if (ep && ep->propertyCapture && !property->isConstant())
261 ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
262
263 if (property->isVarProperty()) {
264 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: object);
265 Q_ASSERT(vmemo);
266 return vmemo->vmeProperty(index: property->coreIndex());
267 } else {
268 return loadProperty(v4: engine, object, property: *property);
269 }
270}
271
272static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr)
273{
274 int index = 0;
275 if (name->equals(other: v4->id_destroy()))
276 index = QV4::QObjectMethod::DestroyMethod;
277 else if (name->equals(other: v4->id_toString()))
278 index = QV4::QObjectMethod::ToStringMethod;
279 else
280 return OptionalReturnedValue();
281
282 if (hasProperty)
283 *hasProperty = true;
284 ExecutionContext *global = v4->rootContext();
285 return OptionalReturnedValue(QV4::QObjectMethod::create(scope: global, object: qobj, index));
286}
287
288static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, QQmlContextData *qmlContext, QObject *qobj,
289 bool *hasProperty = nullptr)
290{
291 if (!qmlContext || !qmlContext->imports)
292 return OptionalReturnedValue();
293
294 QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
295
296 if (hasProperty)
297 *hasProperty = true;
298
299 if (!r.isValid())
300 return OptionalReturnedValue();
301
302 if (r.scriptIndex != -1) {
303 return OptionalReturnedValue(QV4::Encode::undefined());
304 } else if (r.type.isValid()) {
305 return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
306 } else if (r.importNamespace) {
307 return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj, qmlContext->imports, r.importNamespace,
308 Heap::QQmlTypeWrapper::ExcludeEnums));
309 }
310 Q_UNREACHABLE();
311 return OptionalReturnedValue();
312}
313
314ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode,
315 bool *hasProperty, bool includeImports) const
316{
317 // Keep this code in sync with ::virtualResolveLookupGetter
318
319 if (QQmlData::wasDeleted(object: d()->object())) {
320 if (hasProperty)
321 *hasProperty = false;
322 return QV4::Encode::undefined();
323 }
324
325 ExecutionEngine *v4 = engine();
326
327 if (auto methodValue = getDestroyOrToStringMethod(v4, name, qobj: d()->object(), hasProperty))
328 return *methodValue;
329
330 QQmlPropertyData local;
331 QQmlPropertyData *result = findProperty(engine: v4, qmlContext, name, revisionMode, local: &local);
332
333 if (!result) {
334 // Check for attached properties
335 if (includeImports && name->startsWithUpper()) {
336 if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, qobj: d()->object(), hasProperty))
337 return *importProperty;
338 }
339 return QV4::Object::virtualGet(m: this, id: name->propertyKey(), receiver: this, hasProperty);
340 }
341
342 QQmlData *ddata = QQmlData::get(object: d()->object(), create: false);
343
344 if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
345 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(data: result)) {
346 if (hasProperty)
347 *hasProperty = false;
348 return QV4::Encode::undefined();
349 }
350 }
351
352 if (hasProperty)
353 *hasProperty = true;
354
355 return getProperty(engine: v4, object: d()->object(), property: result);
356}
357
358ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property)
359{
360 if (QQmlData::wasDeleted(object)) {
361 if (hasProperty)
362 *hasProperty = false;
363 return QV4::Encode::null();
364 }
365
366 if (auto methodValue = getDestroyOrToStringMethod(v4: engine, name, qobj: object, hasProperty))
367 return *methodValue;
368
369 QQmlData *ddata = QQmlData::get(object, create: false);
370 QQmlPropertyData local;
371 QQmlPropertyData *result = findProperty(engine, o: object, qmlContext, name, revisionMode, local: &local);
372
373 if (result) {
374 if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
375 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(data: result)) {
376 if (hasProperty)
377 *hasProperty = false;
378 return QV4::Encode::undefined();
379 }
380 }
381
382 if (hasProperty)
383 *hasProperty = true;
384
385 if (property && result != &local)
386 *property = result;
387
388 return getProperty(engine, object, property: result);
389 } else {
390 // Check if this object is already wrapped.
391 if (!ddata || (ddata->jsWrapper.isUndefined() &&
392 (ddata->jsEngineId == 0 || // Nobody owns the QObject
393 !ddata->hasTaintedV4Object))) { // Someone else has used the QObject, but it isn't tainted
394
395 // Not wrapped. Last chance: try query QObjectWrapper's prototype.
396 // If it can't handle this, then there is no point
397 // to wrap the QObject just to look at an empty set of JS props.
398 QV4::Object *proto = QObjectWrapper::defaultPrototype(e: engine);
399 return proto->get(name, hasProperty);
400 }
401 }
402
403 // If we get here, we must already be wrapped (which implies a ddata).
404 // There's no point wrapping again, as there wouldn't be any new props.
405 Q_ASSERT(ddata);
406
407 QV4::Scope scope(engine);
408 QV4::Scoped<QObjectWrapper> wrapper(scope, wrap(engine, object));
409 if (!wrapper) {
410 if (hasProperty)
411 *hasProperty = false;
412 return QV4::Encode::null();
413 }
414 return wrapper->getQmlProperty(qmlContext, name, revisionMode, hasProperty);
415}
416
417
418bool QObjectWrapper::setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name,
419 QObjectWrapper::RevisionMode revisionMode, const Value &value)
420{
421 if (QQmlData::wasDeleted(object))
422 return false;
423
424 QQmlPropertyData local;
425 QQmlPropertyData *result = QQmlPropertyCache::property(engine->jsEngine(), object, name, qmlContext, local);
426 if (!result)
427 return false;
428
429 if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
430 QQmlData *ddata = QQmlData::get(object);
431 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(data: result))
432 return false;
433 }
434
435 setProperty(engine, object, property: result, value);
436 return true;
437}
438
439void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value)
440{
441 if (!property->isWritable() && !property->isQList()) {
442 QString error = QLatin1String("Cannot assign to read-only property \"") +
443 property->name(object) + QLatin1Char('\"');
444 engine->throwTypeError(message: error);
445 return;
446 }
447
448 QQmlBinding *newBinding = nullptr;
449 QV4::Scope scope(engine);
450 QV4::ScopedFunctionObject f(scope, value);
451 if (f) {
452 if (!f->isBinding()) {
453 if (!property->isVarProperty() && property->propType() != qMetaTypeId<QJSValue>()) {
454 // assigning a JS function to a non var or QJSValue property or is not allowed.
455 QString error = QLatin1String("Cannot assign JavaScript function to ");
456 if (!QMetaType::typeName(type: property->propType()))
457 error += QLatin1String("[unknown property type]");
458 else
459 error += QLatin1String(QMetaType::typeName(type: property->propType()));
460 scope.engine->throwError(message: error);
461 return;
462 }
463 } else {
464 // binding assignment.
465 QQmlContextData *callingQmlContext = scope.engine->callingQmlContext();
466
467 QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
468
469 QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
470 QV4::ScopedContext ctx(scope, bindingFunction->scope());
471 newBinding = QQmlBinding::create(property, function: f->function(), obj: object, ctxt: callingQmlContext, scope: ctx);
472 newBinding->setSourceLocation(bindingFunction->currentLocation());
473 if (f->isBoundFunction())
474 newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
475 newBinding->setTarget(object, *property, valueType: nullptr);
476 }
477 }
478
479 if (newBinding) {
480 QQmlPropertyPrivate::setBinding(binding: newBinding);
481 } else {
482 if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
483 if (auto binding = QQmlPropertyPrivate::binding(object, index: QQmlPropertyIndex(property->coreIndex()))) {
484 Q_ASSERT(!binding->isValueTypeProxy());
485 const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
486 const auto stackFrame = engine->currentStackFrame;
487 qCInfo(lcBindingRemoval,
488 "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
489 object->metaObject()->className(), qPrintable(property->name(object)),
490 qPrintable(stackFrame->source()), stackFrame->lineNumber(),
491 qPrintable(qmlBinding->expressionIdentifier()));
492 }
493 }
494 QQmlPropertyPrivate::removeBinding(o: object, index: QQmlPropertyIndex(property->coreIndex()));
495 }
496
497 if (!newBinding && property->isVarProperty()) {
498 // allow assignment of "special" values (null, undefined, function) to var properties
499 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: object);
500 Q_ASSERT(vmemo);
501 vmemo->setVMEProperty(index: property->coreIndex(), v: value);
502 return;
503 }
504
505#define PROPERTY_STORE(cpptype, value) \
506 cpptype o = value; \
507 int status = -1; \
508 int flags = 0; \
509 void *argv[] = { &o, 0, &status, &flags }; \
510 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
511
512 if (value.isNull() && property->isQObject()) {
513 PROPERTY_STORE(QObject*, nullptr);
514 } else if (value.isUndefined() && property->isResettable()) {
515 void *a[] = { nullptr };
516 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex(), a);
517 } else if (value.isUndefined() && property->propType() == qMetaTypeId<QVariant>()) {
518 PROPERTY_STORE(QVariant, QVariant());
519 } else if (value.isUndefined() && property->propType() == QMetaType::QJsonValue) {
520 PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
521 } else if (!newBinding && property->propType() == qMetaTypeId<QJSValue>()) {
522 PROPERTY_STORE(QJSValue, QJSValue(scope.engine, value.asReturnedValue()));
523 } else if (value.isUndefined() && property->propType() != qMetaTypeId<QQmlScriptString>()) {
524 QString error = QLatin1String("Cannot assign [undefined] to ");
525 if (!QMetaType::typeName(type: property->propType()))
526 error += QLatin1String("[unknown property type]");
527 else
528 error += QLatin1String(QMetaType::typeName(type: property->propType()));
529 scope.engine->throwError(message: error);
530 return;
531 } else if (value.as<FunctionObject>()) {
532 // this is handled by the binding creation above
533 } else if (property->propType() == QMetaType::Int && value.isNumber()) {
534 PROPERTY_STORE(int, value.asDouble());
535 } else if (property->propType() == QMetaType::QReal && value.isNumber()) {
536 PROPERTY_STORE(qreal, qreal(value.asDouble()));
537 } else if (property->propType() == QMetaType::Float && value.isNumber()) {
538 PROPERTY_STORE(float, float(value.asDouble()));
539 } else if (property->propType() == QMetaType::Double && value.isNumber()) {
540 PROPERTY_STORE(double, double(value.asDouble()));
541 } else if (property->propType() == QMetaType::QString && value.isString()) {
542 PROPERTY_STORE(QString, value.toQStringNoThrow());
543 } else if (property->isVarProperty()) {
544 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: object);
545 Q_ASSERT(vmemo);
546 vmemo->setVMEProperty(index: property->coreIndex(), v: value);
547 } else if (property->propType() == qMetaTypeId<QQmlScriptString>() && (value.isUndefined() || value.isPrimitive())) {
548 QQmlScriptString ss(value.toQStringNoThrow(), nullptr /* context */, object);
549 if (value.isNumber()) {
550 ss.d->numberValue = value.toNumber();
551 ss.d->isNumberLiteral = true;
552 } else if (value.isString()) {
553 ss.d->script = QV4::CompiledData::Binding::escapedString(string: ss.d->script);
554 ss.d->isStringLiteral = true;
555 }
556 PROPERTY_STORE(QQmlScriptString, ss);
557 } else {
558 QVariant v;
559 if (property->isQList())
560 v = scope.engine->toVariant(value, typeHint: qMetaTypeId<QList<QObject *> >());
561 else
562 v = scope.engine->toVariant(value, typeHint: property->propType());
563
564 QQmlContextData *callingQmlContext = scope.engine->callingQmlContext();
565 if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) {
566 const char *valueType = (v.userType() == QMetaType::UnknownType)
567 ? "an unknown type"
568 : QMetaType::typeName(type: v.userType());
569
570 const char *targetTypeName = QMetaType::typeName(type: property->propType());
571 if (!targetTypeName)
572 targetTypeName = "an unregistered type";
573
574 QString error = QLatin1String("Cannot assign ") +
575 QLatin1String(valueType) +
576 QLatin1String(" to ") +
577 QLatin1String(targetTypeName);
578 scope.engine->throwError(message: error);
579 return;
580 }
581 }
582}
583
584ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *object)
585{
586 Q_ASSERT(!QQmlData::wasDeleted(object));
587
588 QQmlData *ddata = QQmlData::get(object, create: true);
589 if (!ddata)
590 return QV4::Encode::undefined();
591
592 Scope scope(engine);
593
594 if (ddata->jsWrapper.isUndefined() &&
595 (ddata->jsEngineId == engine->m_engineId || // We own the QObject
596 ddata->jsEngineId == 0 || // No one owns the QObject
597 !ddata->hasTaintedV4Object)) { // Someone else has used the QObject, but it isn't tainted
598
599 QV4::ScopedValue rv(scope, create(engine, object));
600 ddata->jsWrapper.set(engine: scope.engine, value: rv);
601 ddata->jsEngineId = engine->m_engineId;
602 return rv->asReturnedValue();
603
604 } else {
605 // If this object is tainted, we have to check to see if it is in our
606 // tainted object list
607 ScopedObject alternateWrapper(scope, (Object *)nullptr);
608 if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
609 alternateWrapper = engine->m_multiplyWrappedQObjects->value(key: object);
610
611 // If our tainted handle doesn't exist or has been collected, and there isn't
612 // a handle in the ddata, we can assume ownership of the ddata->jsWrapper
613 if (ddata->jsWrapper.isUndefined() && !alternateWrapper) {
614 QV4::ScopedValue result(scope, create(engine, object));
615 ddata->jsWrapper.set(engine: scope.engine, value: result);
616 ddata->jsEngineId = engine->m_engineId;
617 return result->asReturnedValue();
618 }
619
620 if (!alternateWrapper) {
621 alternateWrapper = create(engine, object);
622 if (!engine->m_multiplyWrappedQObjects)
623 engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap;
624 engine->m_multiplyWrappedQObjects->insert(key: object, value: alternateWrapper->d());
625 ddata->hasTaintedV4Object = true;
626 }
627
628 return alternateWrapper.asReturnedValue();
629 }
630}
631
632void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
633{
634 if (QQmlData::wasDeleted(object))
635 return;
636
637 QQmlData *ddata = QQmlData::get(object);
638 if (!ddata)
639 return;
640
641 const QV4::ExecutionEngine *engine = markStack->engine();
642 if (ddata->jsEngineId == engine->m_engineId)
643 ddata->jsWrapper.markOnce(markStack);
644 else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
645 engine->m_multiplyWrappedQObjects->mark(key: object, markStack);
646}
647
648void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value)
649{
650 setProperty(engine, object: d()->object(), propertyIndex, value);
651}
652
653void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value)
654{
655 Q_ASSERT(propertyIndex < 0xffff);
656 Q_ASSERT(propertyIndex >= 0);
657
658 if (QQmlData::wasDeleted(object))
659 return;
660 QQmlData *ddata = QQmlData::get(object, /*create*/false);
661 if (!ddata)
662 return;
663
664 QQmlPropertyCache *cache = ddata->propertyCache;
665 Q_ASSERT(cache);
666 QQmlPropertyData *property = cache->property(index: propertyIndex);
667 Q_ASSERT(property); // We resolved this property earlier, so it better exist!
668 return setProperty(engine, object, property, value);
669}
670
671bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
672{
673 Q_ASSERT(a->as<QV4::QObjectWrapper>());
674 QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a);
675 QV4::Object *o = b->as<Object>();
676 if (o) {
677 if (QV4::QQmlTypeWrapper *qmlTypeWrapper = o->as<QV4::QQmlTypeWrapper>())
678 return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object();
679 }
680
681 return false;
682}
683
684ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
685{
686 if (QJSEngine *jsEngine = engine->jsEngine()) {
687 if (QQmlPropertyCache *cache = QQmlData::ensurePropertyCache(engine: jsEngine, object)) {
688 ReturnedValue result = QV4::Encode::null();
689 void *args[] = { &result, &engine };
690 if (cache->callJSFactoryMethod(object, args))
691 return result;
692 }
693 }
694 return (engine->memoryManager->allocate<QV4::QObjectWrapper>(args: object))->asReturnedValue();
695}
696
697QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
698{
699 if (!id.isString())
700 return Object::virtualGet(m, id, receiver, hasProperty);
701
702 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
703 Scope scope(that);
704 ScopedString n(scope, id.asStringOrSymbol());
705 QQmlContextData *qmlContext = that->engine()->callingQmlContext();
706 return that->getQmlProperty(qmlContext, name: n, revisionMode: IgnoreRevision, hasProperty, /*includeImports*/ true);
707}
708
709bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
710{
711 if (!id.isString())
712 return Object::virtualPut(m, id, value, receiver);
713
714 Scope scope(m);
715 QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
716 ScopedString name(scope, id.asStringOrSymbol());
717
718 if (scope.engine->hasException || QQmlData::wasDeleted(object: that->d()->object()))
719 return false;
720
721 QQmlContextData *qmlContext = scope.engine->callingQmlContext();
722 if (!setQmlProperty(engine: scope.engine, qmlContext, object: that->d()->object(), name, revisionMode: QV4::QObjectWrapper::IgnoreRevision, value)) {
723 QQmlData *ddata = QQmlData::get(object: that->d()->object());
724 // Types created by QML are not extensible at run-time, but for other QObjects we can store them
725 // as regular JavaScript properties, like on JavaScript objects.
726 if (ddata && ddata->context) {
727 QString error = QLatin1String("Cannot assign to non-existent property \"") +
728 name->toQString() + QLatin1Char('\"');
729 scope.engine->throwError(message: error);
730 return false;
731 } else {
732 return QV4::Object::virtualPut(m, id, value, receiver);
733 }
734 }
735
736 return true;
737}
738
739PropertyAttributes QObjectWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
740{
741 if (id.isString()) {
742 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
743 const QObject *thatObject = that->d()->object();
744 if (!QQmlData::wasDeleted(object: thatObject)) {
745 Scope scope(m);
746 ScopedString n(scope, id.asStringOrSymbol());
747 QQmlContextData *qmlContext = scope.engine->callingQmlContext();
748 QQmlPropertyData local;
749 if (that->findProperty(engine: scope.engine, qmlContext, name: n, revisionMode: IgnoreRevision, local: &local)
750 || n->equals(other: scope.engine->id_destroy()) || n->equals(other: scope.engine->id_toString())) {
751 if (p) {
752 // ### probably not the fastest implementation
753 bool hasProperty;
754 p->value = that->getQmlProperty(qmlContext, name: n, revisionMode: IgnoreRevision, hasProperty: &hasProperty, /*includeImports*/ true);
755 }
756 return QV4::Attr_Data;
757 }
758 }
759 }
760
761 return QV4::Object::virtualGetOwnProperty(m, id, p);
762}
763
764struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
765{
766 int propertyIndex = 0;
767 ~QObjectWrapperOwnPropertyKeyIterator() override = default;
768 PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
769
770private:
771 QSet<QByteArray> m_alreadySeen;
772};
773
774PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs)
775{
776 // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
777 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal(signal: "destroyed(QObject*)");
778 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal(signal: "destroyed()");
779 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot(slot: "deleteLater()");
780
781 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(o);
782
783 QObject *thatObject = that->d()->object();
784 if (thatObject && !QQmlData::wasDeleted(object: thatObject)) {
785 const QMetaObject *mo = thatObject->metaObject();
786 // These indices don't apply to gadgets, so don't block them.
787 const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
788 const int propertyCount = mo->propertyCount();
789 if (propertyIndex < propertyCount) {
790 ExecutionEngine *thatEngine = that->engine();
791 Scope scope(thatEngine);
792 const QMetaProperty property = mo->property(index: propertyIndex);
793 ScopedString propName(scope, thatEngine->newString(s: QString::fromUtf8(str: property.name())));
794 ++propertyIndex;
795 if (attrs)
796 *attrs= QV4::Attr_Data;
797 if (pd) {
798 QQmlPropertyData local;
799 local.load(property);
800 pd->value = that->getProperty(engine: thatEngine, object: thatObject, property: &local);
801 }
802 return propName->toPropertyKey();
803 }
804 const int methodCount = mo->methodCount();
805 while (propertyIndex < propertyCount + methodCount) {
806 Q_ASSERT(propertyIndex >= propertyCount);
807 int index = propertyIndex - propertyCount;
808 const QMetaMethod method = mo->method(index);
809 ++propertyIndex;
810 if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
811 continue;
812 // filter out duplicates due to overloads:
813 if (m_alreadySeen.contains(value: method.name()))
814 continue;
815 else
816 m_alreadySeen.insert(value: method.name());
817 ExecutionEngine *thatEngine = that->engine();
818 Scope scope(thatEngine);
819 ScopedString methodName(scope, thatEngine->newString(s: QString::fromUtf8(str: method.name())));
820 if (attrs)
821 *attrs = QV4::Attr_Data;
822 if (pd) {
823 QQmlPropertyData local;
824 local.load(method);
825 pd->value = that->getProperty(engine: thatEngine, object: thatObject, property: &local);
826 }
827 return methodName->toPropertyKey();
828 }
829 }
830
831 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
832}
833
834OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
835{
836 *target = *m;
837 return new QObjectWrapperOwnPropertyKeyIterator;
838}
839
840ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
841{
842 // Keep this code in sync with ::getQmlProperty
843 PropertyKey id = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->
844 runtimeStrings[lookup->nameIndex]);
845 if (!id.isString())
846 return Object::virtualResolveLookupGetter(object, engine, lookup);
847 Scope scope(engine);
848
849 const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
850 ScopedString name(scope, id.asStringOrSymbol());
851 QQmlContextData *qmlContext = engine->callingQmlContext();
852
853 QObject * const qobj = This->d()->object();
854
855 if (QQmlData::wasDeleted(object: qobj))
856 return QV4::Encode::undefined();
857
858 if (auto methodValue = getDestroyOrToStringMethod(v4: engine, name, qobj))
859 return *methodValue;
860
861 QQmlData *ddata = QQmlData::get(object: qobj, create: false);
862 if (!ddata || !ddata->propertyCache) {
863 QQmlPropertyData local;
864 QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local);
865 return property ? getProperty(engine, object: qobj, property) : QV4::Encode::undefined();
866 }
867 QQmlPropertyData *property = ddata->propertyCache->property(key: name.getPointer(), object: qobj, context: qmlContext);
868
869 if (!property) {
870 // Check for attached properties
871 if (name->startsWithUpper()) {
872 if (auto importProperty = getPropertyFromImports(v4: engine, name, qmlContext, qobj))
873 return *importProperty;
874 }
875 return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
876 }
877
878 QV4::setupQObjectLookup(lookup, ddata, propertyData: property, self: This);
879 lookup->getter = QV4::Lookup::getterQObject;
880 return lookup->getter(lookup, engine, *object);
881}
882
883bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
884 const Value &value)
885{
886 return Object::virtualResolveLookupSetter(object, engine, lookup, value);
887}
888
889namespace QV4 {
890
891struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
892{
893 QV4::PersistentValue function;
894 QV4::PersistentValue thisObject;
895 int signalIndex;
896
897 QObjectSlotDispatcher()
898 : QtPrivate::QSlotObjectBase(&impl)
899 , signalIndex(-1)
900 {}
901
902 static void impl(int which, QSlotObjectBase *this_, QObject *r, void **metaArgs, bool *ret)
903 {
904 switch (which) {
905 case Destroy: {
906 delete static_cast<QObjectSlotDispatcher*>(this_);
907 }
908 break;
909 case Call: {
910 QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
911 QV4::ExecutionEngine *v4 = This->function.engine();
912 // Might be that we're still connected to a signal that's emitted long
913 // after the engine died. We don't track connections in a global list, so
914 // we need this safeguard.
915 if (!v4)
916 break;
917
918 QQmlMetaObject::ArgTypeStorage storage;
919 int *argsTypes = QQmlMetaObject(r).methodParameterTypes(index: This->signalIndex, argStorage: &storage, unknownTypeError: nullptr);
920
921 int argCount = argsTypes ? argsTypes[0]:0;
922
923 QV4::Scope scope(v4);
924 QV4::ScopedFunctionObject f(scope, This->function.value());
925
926 QV4::JSCallData jsCallData(scope, argCount);
927 *jsCallData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value();
928 for (int ii = 0; ii < argCount; ++ii) {
929 int type = argsTypes[ii + 1];
930 if (type == qMetaTypeId<QVariant>()) {
931 jsCallData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
932 } else {
933 jsCallData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
934 }
935 }
936
937 f->call(data: jsCallData);
938 if (scope.hasException()) {
939 QQmlError error = v4->catchExceptionAsQmlError();
940 if (error.description().isEmpty()) {
941 QV4::ScopedString name(scope, f->name());
942 error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(a: name->toQString()));
943 }
944 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
945 QQmlEnginePrivate::get(e: qmlEngine)->warning(error);
946 } else {
947 QMessageLogger(error.url().toString().toLatin1().constData(),
948 error.line(), nullptr).warning().noquote()
949 << error.toString();
950 }
951 }
952 }
953 break;
954 case Compare: {
955 QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
956 if (connection->function.isUndefined()) {
957 *ret = false;
958 return;
959 }
960
961 // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
962 // for the new-style QObject::connect. Here we use the engine pointer as sentinel
963 // to distinguish those type of QSlotObjectBase connections from our QML connections.
964 QV4::ExecutionEngine *v4 = reinterpret_cast<QV4::ExecutionEngine*>(metaArgs[0]);
965 if (v4 != connection->function.engine()) {
966 *ret = false;
967 return;
968 }
969
970 QV4::Scope scope(v4);
971 QV4::ScopedValue function(scope, *reinterpret_cast<QV4::Value*>(metaArgs[1]));
972 QV4::ScopedValue thisObject(scope, *reinterpret_cast<QV4::Value*>(metaArgs[2]));
973 QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
974 int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
975
976 if (slotIndexToDisconnect != -1) {
977 // This is a QObject function wrapper
978 if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
979 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(x: *connection->thisObject.valueRef(), y: thisObject))) {
980
981 QV4::ScopedFunctionObject f(scope, connection->function.value());
982 QPair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(function: f);
983 if (connectedFunctionData.first == receiverToDisconnect &&
984 connectedFunctionData.second == slotIndexToDisconnect) {
985 *ret = true;
986 return;
987 }
988 }
989 } else {
990 // This is a normal JS function
991 if (RuntimeHelpers::strictEqual(x: *connection->function.valueRef(), y: function) &&
992 connection->thisObject.isUndefined() == thisObject->isUndefined() &&
993 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(x: *connection->thisObject.valueRef(), y: thisObject))) {
994 *ret = true;
995 return;
996 }
997 }
998
999 *ret = false;
1000 }
1001 break;
1002 case NumOperations:
1003 break;
1004 }
1005 };
1006};
1007
1008} // namespace QV4
1009
1010ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1011{
1012 QV4::Scope scope(b);
1013
1014 if (argc == 0)
1015 THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
1016
1017 QPair<QObject *, int> signalInfo = extractQtSignal(value: *thisObject);
1018 QObject *signalObject = signalInfo.first;
1019 int signalIndex = signalInfo.second; // in method range, not signal range!
1020
1021 if (signalIndex < 0)
1022 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1023
1024 if (!signalObject)
1025 THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1026
1027 if (signalObject->metaObject()->method(index: signalIndex).methodType() != QMetaMethod::Signal)
1028 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1029
1030 QV4::ScopedFunctionObject f(scope);
1031 QV4::ScopedValue object (scope, QV4::Encode::undefined());
1032
1033 if (argc == 1) {
1034 f = argv[0];
1035 } else if (argc >= 2) {
1036 object = argv[0];
1037 f = argv[1];
1038 }
1039
1040 if (!f)
1041 THROW_GENERIC_ERROR("Function.prototype.connect: target is not a function");
1042
1043 if (!object->isUndefined() && !object->isObject())
1044 THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object");
1045
1046 QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher;
1047 slot->signalIndex = signalIndex;
1048
1049 slot->thisObject.set(engine: scope.engine, value: object);
1050 slot->function.set(engine: scope.engine, value: f);
1051
1052 if (QQmlData *ddata = QQmlData::get(object: signalObject)) {
1053 if (QQmlPropertyCache *propertyCache = ddata->propertyCache) {
1054 QQmlPropertyPrivate::flushSignal(sender: signalObject, signal_index: propertyCache->methodIndexToSignalIndex(index: signalIndex));
1055 }
1056 }
1057 QObjectPrivate::connect(sender: signalObject, signal_index: signalIndex, slotObj: slot, type: Qt::AutoConnection);
1058
1059 RETURN_UNDEFINED();
1060}
1061
1062ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1063{
1064 QV4::Scope scope(b);
1065
1066 if (argc == 0)
1067 THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
1068
1069 QPair<QObject *, int> signalInfo = extractQtSignal(value: *thisObject);
1070 QObject *signalObject = signalInfo.first;
1071 int signalIndex = signalInfo.second;
1072
1073 if (signalIndex == -1)
1074 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1075
1076 if (!signalObject)
1077 THROW_GENERIC_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1078
1079 if (signalIndex < 0 || signalObject->metaObject()->method(index: signalIndex).methodType() != QMetaMethod::Signal)
1080 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1081
1082 QV4::ScopedFunctionObject functionValue(scope);
1083 QV4::ScopedValue functionThisValue(scope, QV4::Encode::undefined());
1084
1085 if (argc == 1) {
1086 functionValue = argv[0];
1087 } else if (argc >= 2) {
1088 functionThisValue = argv[0];
1089 functionValue = argv[1];
1090 }
1091
1092 if (!functionValue)
1093 THROW_GENERIC_ERROR("Function.prototype.disconnect: target is not a function");
1094
1095 if (!functionThisValue->isUndefined() && !functionThisValue->isObject())
1096 THROW_GENERIC_ERROR("Function.prototype.disconnect: target this is not an object");
1097
1098 QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(function: functionValue);
1099
1100 void *a[] = {
1101 scope.engine,
1102 functionValue.ptr,
1103 functionThisValue.ptr,
1104 functionData.first,
1105 &functionData.second
1106 };
1107
1108 QObjectPrivate::disconnect(sender: signalObject, signal_index: signalIndex, slot: reinterpret_cast<void**>(&a));
1109
1110 RETURN_UNDEFINED();
1111}
1112
1113static void markChildQObjectsRecursively(QObject *parent, QV4::MarkStack *markStack)
1114{
1115 const QObjectList &children = parent->children();
1116 for (int i = 0; i < children.count(); ++i) {
1117 QObject *child = children.at(i);
1118 if (!child)
1119 continue;
1120 QObjectWrapper::markWrapper(object: child, markStack);
1121 markChildQObjectsRecursively(parent: child, markStack);
1122 }
1123}
1124
1125void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack)
1126{
1127 QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
1128
1129 if (QObject *o = This->object()) {
1130 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(obj: o);
1131 if (vme)
1132 vme->mark(markStack);
1133
1134 // Children usually don't need to be marked, the gc keeps them alive.
1135 // But in the rare case of a "floating" QObject without a parent that
1136 // _gets_ marked (we've been called here!) then we also need to
1137 // propagate the marking down to the children recursively.
1138 if (!o->parent())
1139 markChildQObjectsRecursively(parent: o, markStack);
1140 }
1141
1142 Object::markObjects(base: that, stack: markStack);
1143}
1144
1145void QObjectWrapper::destroyObject(bool lastCall)
1146{
1147 Heap::QObjectWrapper *h = d();
1148 Q_ASSERT(h->internalClass);
1149
1150 if (h->object()) {
1151 QQmlData *ddata = QQmlData::get(object: h->object(), create: false);
1152 if (ddata) {
1153 if (!h->object()->parent() && !ddata->indestructible) {
1154 if (ddata && ddata->ownContext) {
1155 Q_ASSERT(ddata->ownContext == ddata->context);
1156 ddata->ownContext->emitDestruction();
1157 ddata->ownContext = nullptr;
1158 ddata->context = nullptr;
1159 }
1160 // This object is notionally destroyed now
1161 ddata->isQueuedForDeletion = true;
1162 if (lastCall)
1163 delete h->object();
1164 else
1165 h->object()->deleteLater();
1166 } else {
1167 // If the object is C++-owned, we still have to release the weak reference we have
1168 // to it.
1169 ddata->jsWrapper.clear();
1170 if (lastCall && ddata->propertyCache) {
1171 ddata->propertyCache->release();
1172 ddata->propertyCache = nullptr;
1173 }
1174 }
1175 }
1176 }
1177
1178 h->destroy();
1179}
1180
1181
1182DEFINE_OBJECT_VTABLE(QObjectWrapper);
1183
1184namespace {
1185
1186template<typename A, typename B, typename C, typename D, typename E, typename F, typename G>
1187class MaxSizeOf7 {
1188 template<typename Z, typename X>
1189 struct SMax {
1190 char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
1191 };
1192public:
1193 static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, G> > > > > >);
1194};
1195
1196struct CallArgument {
1197 inline CallArgument();
1198 inline ~CallArgument();
1199 inline void *dataPtr();
1200
1201 inline void initAsType(int type);
1202 inline bool fromValue(int type, ExecutionEngine *, const QV4::Value &);
1203 inline ReturnedValue toValue(ExecutionEngine *);
1204
1205private:
1206 CallArgument(const CallArgument &);
1207
1208 inline void cleanup();
1209
1210 template <class T, class M>
1211 void fromContainerValue(const QV4::Object *object, int type, M CallArgument::*member, bool &queryEngine);
1212
1213 union {
1214 float floatValue;
1215 double doubleValue;
1216 quint32 intValue;
1217 bool boolValue;
1218 QObject *qobjectPtr;
1219 std::vector<int> *stdVectorIntPtr;
1220 std::vector<qreal> *stdVectorRealPtr;
1221 std::vector<bool> *stdVectorBoolPtr;
1222 std::vector<QString> *stdVectorQStringPtr;
1223 std::vector<QUrl> *stdVectorQUrlPtr;
1224#if QT_CONFIG(qml_itemmodel)
1225 std::vector<QModelIndex> *stdVectorQModelIndexPtr;
1226#endif
1227
1228 char allocData[MaxSizeOf7<QVariant,
1229 QString,
1230 QList<QObject *>,
1231 QJSValue,
1232 QJsonArray,
1233 QJsonObject,
1234 QJsonValue>::Size];
1235 qint64 q_for_alignment;
1236 };
1237
1238 // Pointers to allocData
1239 union {
1240 QString *qstringPtr;
1241 QByteArray *qbyteArrayPtr;
1242 QVariant *qvariantPtr;
1243 QList<QObject *> *qlistPtr;
1244 QJSValue *qjsValuePtr;
1245 QJsonArray *jsonArrayPtr;
1246 QJsonObject *jsonObjectPtr;
1247 QJsonValue *jsonValuePtr;
1248 };
1249
1250 int type;
1251};
1252}
1253
1254static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, int returnType, int argCount,
1255 int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
1256 QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
1257{
1258 if (argCount > 0) {
1259 // Convert all arguments.
1260 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1261 args[0].initAsType(type: returnType);
1262 for (int ii = 0; ii < argCount; ++ii) {
1263 if (!args[ii + 1].fromValue(type: argTypes[ii], engine,
1264 callArgs->args[ii].asValue<QV4::Value>())) {
1265 qWarning() << QString::fromLatin1(str: "Could not convert argument %1 at").arg(a: ii);
1266 const StackTrace stack = engine->stackTrace();
1267 for (const StackFrame &frame : stack) {
1268 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source
1269 + (frame.line > 0
1270 ? (QLatin1Char(':') + QString::number(frame.line))
1271 : QString());
1272
1273 }
1274 qWarning() << QLatin1String("Passing incompatible arguments to C++ functions from "
1275 "JavaScript is dangerous and deprecated.");
1276 qWarning() << QLatin1String("This will throw a JavaScript TypeError in future "
1277 "releases of Qt!");
1278
1279 }
1280 }
1281 QVarLengthArray<void *, 9> argData(args.count());
1282 for (int ii = 0; ii < args.count(); ++ii)
1283 argData[ii] = args[ii].dataPtr();
1284
1285 object.metacall(type: callType, index, argv: argData.data());
1286
1287 return args[0].toValue(engine);
1288
1289 } else if (returnType != QMetaType::Void) {
1290
1291 CallArgument arg;
1292 arg.initAsType(type: returnType);
1293
1294 void *args[] = { arg.dataPtr() };
1295
1296 object.metacall(type: callType, index, argv: args);
1297
1298 return arg.toValue(engine);
1299
1300 } else {
1301
1302 void *args[] = { nullptr };
1303 object.metacall(type: callType, index, argv: args);
1304 return Encode::undefined();
1305
1306 }
1307}
1308
1309/*
1310 Returns the match score for converting \a actual to be of type \a conversionType. A
1311 zero score means "perfect match" whereas a higher score is worse.
1312
1313 The conversion table is copied out of the \l QScript::callQtMethod() function.
1314*/
1315static int MatchScore(const QV4::Value &actual, int conversionType)
1316{
1317 if (actual.isNumber()) {
1318 switch (conversionType) {
1319 case QMetaType::Double:
1320 return 0;
1321 case QMetaType::Float:
1322 return 1;
1323 case QMetaType::LongLong:
1324 case QMetaType::ULongLong:
1325 return 2;
1326 case QMetaType::Long:
1327 case QMetaType::ULong:
1328 return 3;
1329 case QMetaType::Int:
1330 case QMetaType::UInt:
1331 return 4;
1332 case QMetaType::Short:
1333 case QMetaType::UShort:
1334 return 5;
1335 break;
1336 case QMetaType::Char:
1337 case QMetaType::UChar:
1338 return 6;
1339 case QMetaType::QJsonValue:
1340 return 5;
1341 default:
1342 return 10;
1343 }
1344 } else if (actual.isString()) {
1345 switch (conversionType) {
1346 case QMetaType::QString:
1347 return 0;
1348 case QMetaType::QJsonValue:
1349 return 5;
1350 default:
1351 return 10;
1352 }
1353 } else if (actual.isBoolean()) {
1354 switch (conversionType) {
1355 case QMetaType::Bool:
1356 return 0;
1357 case QMetaType::QJsonValue:
1358 return 5;
1359 default:
1360 return 10;
1361 }
1362 } else if (actual.as<DateObject>()) {
1363 switch (conversionType) {
1364 case QMetaType::QDateTime:
1365 return 0;
1366 case QMetaType::QDate:
1367 return 1;
1368 case QMetaType::QTime:
1369 return 2;
1370 default:
1371 return 10;
1372 }
1373 } else if (actual.as<QV4::RegExpObject>()) {
1374 switch (conversionType) {
1375 case QMetaType::QRegExp:
1376#if QT_CONFIG(regularexpression)
1377 case QMetaType::QRegularExpression:
1378#endif
1379 return 0;
1380 default:
1381 return 10;
1382 }
1383 } else if (actual.as<ArrayBuffer>()) {
1384 switch (conversionType) {
1385 case QMetaType::QByteArray:
1386 return 0;
1387 default:
1388 return 10;
1389 }
1390 } else if (actual.as<ArrayObject>()) {
1391 switch (conversionType) {
1392 case QMetaType::QJsonArray:
1393 return 3;
1394 case QMetaType::QStringList:
1395 case QMetaType::QVariantList:
1396 return 5;
1397 case QMetaType::QVector4D:
1398 case QMetaType::QMatrix4x4:
1399 return 6;
1400 case QMetaType::QVector3D:
1401 return 7;
1402 default:
1403 return 10;
1404 }
1405 } else if (actual.isNull()) {
1406 switch (conversionType) {
1407 case QMetaType::Nullptr:
1408 case QMetaType::VoidStar:
1409 case QMetaType::QObjectStar:
1410 case QMetaType::QJsonValue:
1411 return 0;
1412 default: {
1413 const char *typeName = QMetaType::typeName(type: conversionType);
1414 if (typeName && typeName[strlen(s: typeName) - 1] == '*')
1415 return 0;
1416 else
1417 return 10;
1418 }
1419 }
1420 } else if (const QV4::Object *obj = actual.as<QV4::Object>()) {
1421 if (obj->as<QV4::VariantObject>()) {
1422 if (conversionType == qMetaTypeId<QVariant>())
1423 return 0;
1424 if (obj->engine()->toVariant(value: actual, typeHint: -1).userType() == conversionType)
1425 return 0;
1426 else
1427 return 10;
1428 }
1429
1430 if (obj->as<QObjectWrapper>()) {
1431 switch (conversionType) {
1432 case QMetaType::QObjectStar:
1433 return 0;
1434 default:
1435 return 10;
1436 }
1437 }
1438
1439 if (obj->as<QV4::QQmlValueTypeWrapper>()) {
1440 const QVariant v = obj->engine()->toVariant(value: actual, typeHint: -1);
1441 if (v.userType() == conversionType)
1442 return 0;
1443 else if (v.canConvert(targetTypeId: conversionType))
1444 return 5;
1445 return 10;
1446 } else if (conversionType == QMetaType::QJsonObject) {
1447 return 5;
1448 } else if (conversionType == qMetaTypeId<QJSValue>()) {
1449 return 0;
1450 } else {
1451 return 10;
1452 }
1453
1454 } else {
1455 return 10;
1456 }
1457}
1458
1459static inline int QMetaObject_methods(const QMetaObject *metaObject)
1460{
1461 struct Private
1462 {
1463 int revision;
1464 int className;
1465 int classInfoCount, classInfoData;
1466 int methodCount, methodData;
1467 };
1468
1469 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1470}
1471
1472/*
1473Returns the next related method, if one, or 0.
1474*/
1475static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object,
1476 const QQmlPropertyData *current,
1477 QQmlPropertyData &dummy,
1478 const QQmlPropertyCache *propertyCache)
1479{
1480 if (!current->isOverload())
1481 return nullptr;
1482
1483 Q_ASSERT(!current->overrideIndexIsProperty());
1484
1485 if (propertyCache) {
1486 return propertyCache->method(index: current->overrideIndex());
1487 } else {
1488 const QMetaObject *mo = object.metaObject();
1489 int methodOffset = mo->methodCount() - QMetaObject_methods(metaObject: mo);
1490
1491 while (methodOffset > current->overrideIndex()) {
1492 mo = mo->superClass();
1493 methodOffset -= QMetaObject_methods(metaObject: mo);
1494 }
1495
1496 // If we've been called before with the same override index, then
1497 // we can't go any further...
1498 if (&dummy == current && dummy.coreIndex() == current->overrideIndex())
1499 return nullptr;
1500
1501 QMetaMethod method = mo->method(index: current->overrideIndex());
1502 dummy.load(method);
1503
1504 // Look for overloaded methods
1505 QByteArray methodName = method.name();
1506 for (int ii = current->overrideIndex() - 1; ii >= methodOffset; --ii) {
1507 if (methodName == mo->method(index: ii).name()) {
1508 dummy.setOverload(true);
1509 dummy.setOverrideIndexIsProperty(0);
1510 dummy.setOverrideIndex(ii);
1511 return &dummy;
1512 }
1513 }
1514
1515 return &dummy;
1516 }
1517}
1518
1519static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
1520 QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
1521 QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
1522{
1523 QByteArray unknownTypeError;
1524
1525 int returnType = object.methodReturnType(data, unknownTypeError: &unknownTypeError);
1526
1527 if (returnType == QMetaType::UnknownType) {
1528 return engine->throwError(message: QLatin1String("Unknown method return type: ")
1529 + QLatin1String(unknownTypeError));
1530 }
1531
1532 if (data.hasArguments()) {
1533
1534 int *args = nullptr;
1535 QQmlMetaObject::ArgTypeStorage storage;
1536
1537 if (data.isConstructor())
1538 args = static_cast<const QQmlStaticMetaObject&>(object).constructorParameterTypes(
1539 index: data.coreIndex(), dummy: &storage, unknownTypeError: &unknownTypeError);
1540 else
1541 args = object.methodParameterTypes(index: data.coreIndex(), argStorage: &storage, unknownTypeError: &unknownTypeError);
1542
1543 if (!args) {
1544 return engine->throwError(message: QLatin1String("Unknown method parameter type: ")
1545 + QLatin1String(unknownTypeError));
1546 }
1547
1548 if (args[0] > callArgs->argc()) {
1549 QString error = QLatin1String("Insufficient arguments");
1550 return engine->throwError(message: error);
1551 }
1552
1553 return CallMethod(object, index: data.coreIndex(), returnType, argCount: args[0], argTypes: args + 1, engine, callArgs, callType);
1554
1555 } else {
1556
1557 return CallMethod(object, index: data.coreIndex(), returnType, argCount: 0, argTypes: nullptr, engine, callArgs, callType);
1558
1559 }
1560}
1561
1562/*
1563Resolve the overloaded method to call. The algorithm works conceptually like this:
1564 1. Resolve the set of overloads it is *possible* to call.
1565 Impossible overloads include those that have too many parameters or have parameters
1566 of unknown type.
1567 2. Filter the set of overloads to only contain those with the closest number of
1568 parameters.
1569 For example, if we are called with 3 parameters and there are 2 overloads that
1570 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1571 3. Find the best remaining overload based on its match score.
1572 If two or more overloads have the same match score, call the last one. The match
1573 score is constructed by adding the matchScore() result for each of the parameters.
1574*/
1575static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
1576 QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache,
1577 QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
1578{
1579 int argumentCount = callArgs->argc();
1580
1581 QQmlPropertyData best;
1582 int bestParameterScore = INT_MAX;
1583 int bestMatchScore = INT_MAX;
1584
1585 QQmlPropertyData dummy;
1586 const QQmlPropertyData *attempt = &data;
1587
1588 QV4::Scope scope(engine);
1589 QV4::ScopedValue v(scope);
1590
1591 do {
1592 QQmlMetaObject::ArgTypeStorage storage;
1593 int methodArgumentCount = 0;
1594 int *methodArgTypes = nullptr;
1595 if (attempt->hasArguments()) {
1596 int *args = object.methodParameterTypes(index: attempt->coreIndex(), argStorage: &storage, unknownTypeError: nullptr);
1597 if (!args) // Must be an unknown argument
1598 continue;
1599
1600 methodArgumentCount = args[0];
1601 methodArgTypes = args + 1;
1602 }
1603
1604 if (methodArgumentCount > argumentCount)
1605 continue; // We don't have sufficient arguments to call this method
1606
1607 int methodParameterScore = argumentCount - methodArgumentCount;
1608 if (methodParameterScore > bestParameterScore)
1609 continue; // We already have a better option
1610
1611 int methodMatchScore = 0;
1612 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1613 methodMatchScore += MatchScore(actual: (v = QV4::Value::fromStaticValue(staticValue: callArgs->args[ii])),
1614 conversionType: methodArgTypes[ii]);
1615 }
1616
1617 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1618 best = *attempt;
1619 bestParameterScore = methodParameterScore;
1620 bestMatchScore = methodMatchScore;
1621 }
1622
1623 if (bestParameterScore == 0 && bestMatchScore == 0)
1624 break; // We can't get better than that
1625
1626 } while ((attempt = RelatedMethod(object, current: attempt, dummy, propertyCache)) != nullptr);
1627
1628 if (best.isValid()) {
1629 return CallPrecise(object, data: best, engine, callArgs, callType);
1630 } else {
1631 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1632 const QQmlPropertyData *candidate = &data;
1633 while (candidate) {
1634 error += QLatin1String("\n ") +
1635 QString::fromUtf8(str: object.metaObject()->method(index: candidate->coreIndex())
1636 .methodSignature());
1637 candidate = RelatedMethod(object, current: candidate, dummy, propertyCache);
1638 }
1639
1640 return engine->throwError(message: error);
1641 }
1642}
1643
1644CallArgument::CallArgument()
1645: type(QMetaType::UnknownType)
1646{
1647}
1648
1649CallArgument::~CallArgument()
1650{
1651 cleanup();
1652}
1653
1654void CallArgument::cleanup()
1655{
1656 if (type == QMetaType::QString) {
1657 qstringPtr->~QString();
1658 } else if (type == QMetaType::QByteArray) {
1659 qbyteArrayPtr->~QByteArray();
1660 } else if (type == -1 || type == QMetaType::QVariant) {
1661 qvariantPtr->~QVariant();
1662 } else if (type == qMetaTypeId<QJSValue>()) {
1663 qjsValuePtr->~QJSValue();
1664 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1665 qlistPtr->~QList<QObject *>();
1666 } else if (type == QMetaType::QJsonArray) {
1667 jsonArrayPtr->~QJsonArray();
1668 } else if (type == QMetaType::QJsonObject) {
1669 jsonObjectPtr->~QJsonObject();
1670 } else if (type == QMetaType::QJsonValue) {
1671 jsonValuePtr->~QJsonValue();
1672 }
1673}
1674
1675void *CallArgument::dataPtr()
1676{
1677 if (type == -1)
1678 return qvariantPtr->data();
1679 else if (type == qMetaTypeId<std::vector<int>>())
1680 return stdVectorIntPtr;
1681 else if (type == qMetaTypeId<std::vector<qreal>>())
1682 return stdVectorRealPtr;
1683 else if (type == qMetaTypeId<std::vector<bool>>())
1684 return stdVectorBoolPtr;
1685 else if (type == qMetaTypeId<std::vector<QString>>())
1686 return stdVectorQStringPtr;
1687 else if (type == qMetaTypeId<std::vector<QUrl>>())
1688 return stdVectorQUrlPtr;
1689#if QT_CONFIG(qml_itemmodel)
1690 else if (type == qMetaTypeId<std::vector<QModelIndex>>())
1691 return stdVectorQModelIndexPtr;
1692#endif
1693 else if (type != 0)
1694 return (void *)&allocData;
1695 return nullptr;
1696}
1697
1698void CallArgument::initAsType(int callType)
1699{
1700 if (type != 0) { cleanup(); type = 0; }
1701 if (callType == QMetaType::UnknownType || callType == QMetaType::Void) return;
1702
1703 if (callType == qMetaTypeId<QJSValue>()) {
1704 qjsValuePtr = new (&allocData) QJSValue();
1705 type = callType;
1706 } else if (callType == QMetaType::Int ||
1707 callType == QMetaType::UInt ||
1708 callType == QMetaType::Bool ||
1709 callType == QMetaType::Double ||
1710 callType == QMetaType::Float) {
1711 type = callType;
1712 } else if (callType == QMetaType::QObjectStar) {
1713 qobjectPtr = nullptr;
1714 type = callType;
1715 } else if (callType == QMetaType::QString) {
1716 qstringPtr = new (&allocData) QString();
1717 type = callType;
1718 } else if (callType == QMetaType::QVariant) {
1719 type = callType;
1720 qvariantPtr = new (&allocData) QVariant();
1721 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1722 type = callType;
1723 qlistPtr = new (&allocData) QList<QObject *>();
1724 } else if (callType == QMetaType::QJsonArray) {
1725 type = callType;
1726 jsonArrayPtr = new (&allocData) QJsonArray();
1727 } else if (callType == QMetaType::QJsonObject) {
1728 type = callType;
1729 jsonObjectPtr = new (&allocData) QJsonObject();
1730 } else if (callType == QMetaType::QJsonValue) {
1731 type = callType;
1732 jsonValuePtr = new (&allocData) QJsonValue();
1733 } else {
1734 type = -1;
1735 qvariantPtr = new (&allocData) QVariant(callType, (void *)nullptr);
1736 }
1737}
1738
1739#if QT_CONFIG(qml_sequence_object)
1740template <class T, class M>
1741void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine)
1742{
1743 if (object && object->isListType()) {
1744 T* ptr = static_cast<T*>(QV4::SequencePrototype::getRawContainerPtr(object, typeHint: callType));
1745 if (ptr) {
1746 (this->*member) = ptr;
1747 type = callType;
1748 queryEngine = false;
1749 }
1750 }
1751}
1752#endif
1753
1754bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value)
1755{
1756 if (type != 0) {
1757 cleanup();
1758 type = 0;
1759 }
1760
1761 QV4::Scope scope(engine);
1762
1763 bool queryEngine = false;
1764 if (callType == qMetaTypeId<QJSValue>()) {
1765 qjsValuePtr = new (&allocData) QJSValue(scope.engine, value.asReturnedValue());
1766 type = qMetaTypeId<QJSValue>();
1767 } else if (callType == QMetaType::Int) {
1768 intValue = quint32(value.toInt32());
1769 type = callType;
1770 } else if (callType == QMetaType::UInt) {
1771 intValue = quint32(value.toUInt32());
1772 type = callType;
1773 } else if (callType == QMetaType::Bool) {
1774 boolValue = value.toBoolean();
1775 type = callType;
1776 } else if (callType == QMetaType::Double) {
1777 doubleValue = double(value.toNumber());
1778 type = callType;
1779 } else if (callType == QMetaType::Float) {
1780 floatValue = float(value.toNumber());
1781 type = callType;
1782 } else if (callType == QMetaType::QString) {
1783 if (value.isNull() || value.isUndefined())
1784 qstringPtr = new (&allocData) QString();
1785 else
1786 qstringPtr = new (&allocData) QString(value.toQStringNoThrow());
1787 type = callType;
1788 } else if (callType == QMetaType::QObjectStar) {
1789 qobjectPtr = nullptr;
1790 type = callType;
1791 if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>())
1792 qobjectPtr = qobjectWrapper->object();
1793 else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>())
1794 queryEngine = qmlTypeWrapper->isSingleton();
1795 else if (!value.isNull() && !value.isUndefined()) // null and undefined are nullptr
1796 return false;
1797 } else if (callType == qMetaTypeId<QVariant>()) {
1798 qvariantPtr = new (&allocData) QVariant(scope.engine->toVariant(value, typeHint: -1));
1799 type = callType;
1800 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1801 qlistPtr = new (&allocData) QList<QObject *>();
1802 type = callType;
1803 QV4::ScopedArrayObject array(scope, value);
1804 if (array) {
1805 Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
1806
1807 uint length = array->getLength();
1808 for (uint ii = 0; ii < length; ++ii) {
1809 QObject *o = nullptr;
1810 qobjectWrapper = array->get(idx: ii);
1811 if (!!qobjectWrapper)
1812 o = qobjectWrapper->object();
1813 qlistPtr->append(t: o);
1814 }
1815 } else {
1816 if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) {
1817 qlistPtr->append(t: qobjectWrapper->object());
1818 } else {
1819 qlistPtr->append(t: nullptr);
1820 if (!value.isNull() && !value.isUndefined())
1821 return false;
1822 }
1823 }
1824 } else if (callType == QMetaType::QJsonArray) {
1825 QV4::ScopedArrayObject a(scope, value);
1826 jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(a));
1827 type = callType;
1828 } else if (callType == QMetaType::QJsonObject) {
1829 QV4::ScopedObject o(scope, value);
1830 jsonObjectPtr = new (&allocData) QJsonObject(QV4::JsonObject::toJsonObject(o));
1831 type = callType;
1832 } else if (callType == QMetaType::QJsonValue) {
1833 jsonValuePtr = new (&allocData) QJsonValue(QV4::JsonObject::toJsonValue(value));
1834 type = callType;
1835 } else if (callType == QMetaType::Void) {
1836 *qvariantPtr = QVariant();
1837#if QT_CONFIG(qml_sequence_object)
1838 } else if (callType == qMetaTypeId<std::vector<int>>()
1839 || callType == qMetaTypeId<std::vector<qreal>>()
1840 || callType == qMetaTypeId<std::vector<bool>>()
1841 || callType == qMetaTypeId<std::vector<QString>>()
1842 || callType == qMetaTypeId<std::vector<QUrl>>()
1843#if QT_CONFIG(qml_itemmodel)
1844 || callType == qMetaTypeId<std::vector<QModelIndex>>()
1845#endif
1846 ) {
1847 queryEngine = true;
1848 const QV4::Object* object = value.as<QV4::Object>();
1849 if (callType == qMetaTypeId<std::vector<int>>()) {
1850 stdVectorIntPtr = nullptr;
1851 fromContainerValue<std::vector<int>>(object, callType, member: &CallArgument::stdVectorIntPtr, queryEngine);
1852 } else if (callType == qMetaTypeId<std::vector<qreal>>()) {
1853 stdVectorRealPtr = nullptr;
1854 fromContainerValue<std::vector<qreal>>(object, callType, member: &CallArgument::stdVectorRealPtr, queryEngine);
1855 } else if (callType == qMetaTypeId<std::vector<bool>>()) {
1856 stdVectorBoolPtr = nullptr;
1857 fromContainerValue<std::vector<bool>>(object, callType, member: &CallArgument::stdVectorBoolPtr, queryEngine);
1858 } else if (callType == qMetaTypeId<std::vector<QString>>()) {
1859 stdVectorQStringPtr = nullptr;
1860 fromContainerValue<std::vector<QString>>(object, callType, member: &CallArgument::stdVectorQStringPtr, queryEngine);
1861 } else if (callType == qMetaTypeId<std::vector<QUrl>>()) {
1862 stdVectorQUrlPtr = nullptr;
1863 fromContainerValue<std::vector<QUrl>>(object, callType, member: &CallArgument::stdVectorQUrlPtr, queryEngine);
1864#if QT_CONFIG(qml_itemmodel)
1865 } else if (callType == qMetaTypeId<std::vector<QModelIndex>>()) {
1866 stdVectorQModelIndexPtr = nullptr;
1867 fromContainerValue<std::vector<QModelIndex>>(object, callType, member: &CallArgument::stdVectorQModelIndexPtr, queryEngine);
1868#endif
1869 }
1870#endif
1871 } else if (QMetaType::typeFlags(type: callType)
1872 & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) {
1873 // You can assign null or undefined to any pointer. The result is a nullptr.
1874 if (value.isNull() || value.isUndefined()) {
1875 qvariantPtr = new (&allocData) QVariant(callType, nullptr);
1876 type = callType;
1877 } else {
1878 queryEngine = true;
1879 }
1880 } else {
1881 queryEngine = true;
1882 }
1883
1884 if (queryEngine) {
1885 qvariantPtr = new (&allocData) QVariant();
1886 type = -1;
1887
1888 QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(e: engine->qmlEngine()) : nullptr;
1889 QVariant v = scope.engine->toVariant(value, typeHint: callType);
1890
1891 if (v.userType() == callType) {
1892 *qvariantPtr = v;
1893 } else if (v.canConvert(targetTypeId: callType)) {
1894 *qvariantPtr = v;
1895 qvariantPtr->convert(targetTypeId: callType);
1896 } else {
1897 QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
1898 if (!mo.isNull()) {
1899 QObject *obj = ep->toQObject(v);
1900
1901 if (obj != nullptr && !QQmlMetaObject::canConvert(from: obj, to: mo)) {
1902 *qvariantPtr = QVariant(callType, nullptr);
1903 return false;
1904 }
1905
1906 *qvariantPtr = QVariant(callType, &obj);
1907 return true;
1908 }
1909
1910 *qvariantPtr = QVariant(callType, (void *)nullptr);
1911 return false;
1912 }
1913 }
1914 return true;
1915}
1916
1917QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
1918{
1919 QV4::Scope scope(engine);
1920
1921 if (type == qMetaTypeId<QJSValue>()) {
1922 return QJSValuePrivate::convertedToValue(e: scope.engine, jsval: *qjsValuePtr);
1923 } else if (type == QMetaType::Int) {
1924 return QV4::Encode(int(intValue));
1925 } else if (type == QMetaType::UInt) {
1926 return QV4::Encode((uint)intValue);
1927 } else if (type == QMetaType::Bool) {
1928 return QV4::Encode(boolValue);
1929 } else if (type == QMetaType::Double) {
1930 return QV4::Encode(doubleValue);
1931 } else if (type == QMetaType::Float) {
1932 return QV4::Encode(floatValue);
1933 } else if (type == QMetaType::QString) {
1934 return QV4::Encode(engine->newString(s: *qstringPtr));
1935 } else if (type == QMetaType::QByteArray) {
1936 return QV4::Encode(engine->newArrayBuffer(array: *qbyteArrayPtr));
1937 } else if (type == QMetaType::QObjectStar) {
1938 QObject *object = qobjectPtr;
1939 if (object)
1940 QQmlData::get(object, create: true)->setImplicitDestructible();
1941 return QV4::QObjectWrapper::wrap(engine: scope.engine, object);
1942 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1943 // XXX Can this be made more by using Array as a prototype and implementing
1944 // directly against QList<QObject*>?
1945 QList<QObject *> &list = *qlistPtr;
1946 QV4::ScopedArrayObject array(scope, scope.engine->newArrayObject());
1947 array->arrayReserve(n: list.count());
1948 QV4::ScopedValue v(scope);
1949 for (int ii = 0; ii < list.count(); ++ii)
1950 array->arrayPut(index: ii, value: (v = QV4::QObjectWrapper::wrap(engine: scope.engine, object: list.at(i: ii))));
1951 array->setArrayLengthUnchecked(list.count());
1952 return array.asReturnedValue();
1953 } else if (type == QMetaType::QJsonArray) {
1954 return QV4::JsonObject::fromJsonArray(engine: scope.engine, array: *jsonArrayPtr);
1955 } else if (type == QMetaType::QJsonObject) {
1956 return QV4::JsonObject::fromJsonObject(engine: scope.engine, object: *jsonObjectPtr);
1957 } else if (type == QMetaType::QJsonValue) {
1958 return QV4::JsonObject::fromJsonValue(engine: scope.engine, value: *jsonValuePtr);
1959 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1960 QVariant value = *qvariantPtr;
1961 QV4::ScopedValue rv(scope, scope.engine->fromVariant(value));
1962 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, rv);
1963 if (!!qobjectWrapper) {
1964 if (QObject *object = qobjectWrapper->object())
1965 QQmlData::get(object, create: true)->setImplicitDestructible();
1966 }
1967 return rv->asReturnedValue();
1968 } else {
1969 return QV4::Encode::undefined();
1970 }
1971}
1972
1973ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
1974{
1975 Scope valueScope(scope);
1976 Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(args: scope));
1977 method->d()->setObject(object);
1978
1979 if (QQmlData *ddata = QQmlData::get(object))
1980 method->d()->setPropertyCache(ddata->propertyCache);
1981
1982 method->d()->index = index;
1983 return method.asReturnedValue();
1984}
1985
1986ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
1987{
1988 Scope valueScope(scope);
1989 Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(args: scope));
1990 method->d()->setPropertyCache(valueType->propertyCache());
1991 method->d()->index = index;
1992 method->d()->valueTypeWrapper.set(e: valueScope.engine, newVal: valueType);
1993 return method.asReturnedValue();
1994}
1995
1996void Heap::QObjectMethod::init(QV4::ExecutionContext *scope)
1997{
1998 Heap::FunctionObject::init(scope);
1999}
2000
2001const QMetaObject *Heap::QObjectMethod::metaObject()
2002{
2003 if (propertyCache())
2004 return propertyCache()->createMetaObject();
2005 return object()->metaObject();
2006}
2007
2008QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionEngine *engine) const
2009{
2010 QString result;
2011 if (const QMetaObject *metaObject = d()->metaObject()) {
2012
2013 result += QString::fromUtf8(str: metaObject->className()) +
2014 QLatin1String("(0x") + QString::number((quintptr)d()->object(),base: 16);
2015
2016 if (d()->object()) {
2017 QString objectName = d()->object()->objectName();
2018 if (!objectName.isEmpty())
2019 result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
2020 }
2021
2022 result += QLatin1Char(')');
2023 } else {
2024 result = QLatin1String("null");
2025 }
2026
2027 return engine->newString(s: result)->asReturnedValue();
2028}
2029
2030QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, const Value *args, int argc) const
2031{
2032 if (!d()->object())
2033 return Encode::undefined();
2034 if (QQmlData::keepAliveDuringGarbageCollection(object: d()->object()))
2035 return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2036
2037 int delay = 0;
2038 if (argc > 0)
2039 delay = args[0].toUInt32();
2040
2041 if (delay > 0)
2042 QTimer::singleShot(msec: delay, receiver: d()->object(), SLOT(deleteLater()));
2043 else
2044 d()->object()->deleteLater();
2045
2046 return Encode::undefined();
2047}
2048
2049ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2050{
2051 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2052 return This->callInternal(thisObject, argv, argc);
2053}
2054
2055ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const
2056{
2057 ExecutionEngine *v4 = engine();
2058 if (d()->index == DestroyMethod)
2059 return method_destroy(engine: v4, args: argv, argc);
2060 else if (d()->index == ToStringMethod)
2061 return method_toString(engine: v4);
2062
2063 QQmlObjectOrGadget object(d()->object());
2064 if (!d()->object()) {
2065 if (!d()->valueTypeWrapper)
2066 return Encode::undefined();
2067
2068 object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr());
2069 }
2070
2071 QQmlPropertyData method;
2072
2073 if (d()->propertyCache()) {
2074 QQmlPropertyData *data = d()->propertyCache()->method(index: d()->index);
2075 if (!data)
2076 return QV4::Encode::undefined();
2077 method = *data;
2078 } else {
2079 const QMetaObject *mo = d()->object()->metaObject();
2080 const QMetaMethod moMethod = mo->method(index: d()->index);
2081 method.load(moMethod);
2082
2083 if (method.coreIndex() == -1)
2084 return QV4::Encode::undefined();
2085
2086 // Look for overloaded methods
2087 QByteArray methodName = moMethod.name();
2088 const int methodOffset = mo->methodOffset();
2089 for (int ii = d()->index - 1; ii >= methodOffset; --ii) {
2090 if (methodName == mo->method(index: ii).name()) {
2091 method.setOverload(true);
2092 method.setOverrideIndexIsProperty(0);
2093 method.setOverrideIndex(ii);
2094 break;
2095 }
2096 }
2097 }
2098
2099 Scope scope(v4);
2100 JSCallData cData(scope, argc, argv, thisObject);
2101 CallData *callData = cData.callData();
2102
2103 if (method.isV4Function()) {
2104 QV4::ScopedValue rv(scope, QV4::Value::undefinedValue());
2105 QQmlV4Function func(callData, rv, v4);
2106 QQmlV4Function *funcptr = &func;
2107
2108 void *args[] = { nullptr, &funcptr };
2109 object.metacall(type: QMetaObject::InvokeMetaMethod, index: method.coreIndex(), argv: args);
2110
2111 return rv->asReturnedValue();
2112 }
2113
2114 if (!method.isOverload()) {
2115 return CallPrecise(object, data: method, engine: v4, callArgs: callData);
2116 } else {
2117 return CallOverloaded(object, data: method, engine: v4, callArgs: callData, propertyCache: d()->propertyCache());
2118 }
2119}
2120
2121DEFINE_OBJECT_VTABLE(QObjectMethod);
2122
2123
2124void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject)
2125{
2126 FunctionObject::init();
2127 this->metaObject = metaObject;
2128 constructors = nullptr;
2129 constructorCount = 0;
2130}
2131
2132void Heap::QMetaObjectWrapper::destroy()
2133{
2134 delete[] constructors;
2135}
2136
2137void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
2138
2139 const int count = metaObject->constructorCount();
2140 if (constructorCount != count) {
2141 delete[] constructors;
2142 constructorCount = count;
2143 if (count == 0) {
2144 constructors = nullptr;
2145 return;
2146 }
2147 constructors = new QQmlPropertyData[count];
2148
2149 for (int i = 0; i < count; ++i) {
2150 QMetaMethod method = metaObject->constructor(index: i);
2151 QQmlPropertyData &d = constructors[i];
2152 d.load(method);
2153 d.setCoreIndex(i);
2154 }
2155 }
2156}
2157
2158
2159ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
2160
2161 QV4::Scope scope(engine);
2162 Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QV4::QMetaObjectWrapper>(args: metaObject)->asReturnedValue());
2163 mo->init(engine);
2164 return mo->asReturnedValue();
2165}
2166
2167void QMetaObjectWrapper::init(ExecutionEngine *) {
2168 const QMetaObject & mo = *d()->metaObject;
2169
2170 for (int i = 0; i < mo.enumeratorCount(); i++) {
2171 QMetaEnum Enum = mo.enumerator(index: i);
2172 for (int k = 0; k < Enum.keyCount(); k++) {
2173 const char* key = Enum.key(index: k);
2174 const int value = Enum.value(index: k);
2175 defineReadonlyProperty(name: QLatin1String(key), value: Value::fromInt32(i: value));
2176 }
2177 }
2178}
2179
2180ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
2181{
2182 const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f);
2183 return This->constructInternal(argv, argc);
2184}
2185
2186ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const
2187{
2188
2189 d()->ensureConstructorsCache();
2190
2191 ExecutionEngine *v4 = engine();
2192 const QMetaObject* mo = d()->metaObject;
2193 if (d()->constructorCount == 0) {
2194 return v4->throwTypeError(message: QLatin1String(mo->className())
2195 + QLatin1String(" has no invokable constructor"));
2196 }
2197
2198 Scope scope(v4);
2199 Scoped<QObjectWrapper> object(scope);
2200 JSCallData cData(scope, argc, argv);
2201 CallData *callData = cData.callData();
2202
2203 if (d()->constructorCount == 1) {
2204 object = callConstructor(data: d()->constructors[0], engine: v4, callArgs: callData);
2205 }
2206 else {
2207 object = callOverloadedConstructor(engine: v4, callArgs: callData);
2208 }
2209 Scoped<QMetaObjectWrapper> metaObject(scope, this);
2210 object->defineDefaultProperty(name: v4->id_constructor(), value: metaObject);
2211 object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
2212 return object.asReturnedValue();
2213
2214}
2215
2216ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
2217
2218 const QMetaObject* mo = d()->metaObject;
2219 const QQmlStaticMetaObject object(mo);
2220 return CallPrecise(object, data, engine, callArgs, callType: QMetaObject::CreateInstance);
2221}
2222
2223
2224ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
2225 const int numberOfConstructors = d()->constructorCount;
2226 const int argumentCount = callArgs->argc();
2227 const QQmlStaticMetaObject object(d()->metaObject);
2228
2229 QQmlPropertyData best;
2230 int bestParameterScore = INT_MAX;
2231 int bestMatchScore = INT_MAX;
2232
2233 QV4::Scope scope(engine);
2234 QV4::ScopedValue v(scope);
2235
2236 for (int i = 0; i < numberOfConstructors; i++) {
2237 const QQmlPropertyData & attempt = d()->constructors[i];
2238 QQmlMetaObject::ArgTypeStorage storage;
2239 int methodArgumentCount = 0;
2240 int *methodArgTypes = nullptr;
2241 if (attempt.hasArguments()) {
2242 int *args = object.constructorParameterTypes(index: attempt.coreIndex(), dummy: &storage, unknownTypeError: nullptr);
2243 if (!args) // Must be an unknown argument
2244 continue;
2245
2246 methodArgumentCount = args[0];
2247 methodArgTypes = args + 1;
2248 }
2249
2250 if (methodArgumentCount > argumentCount)
2251 continue; // We don't have sufficient arguments to call this method
2252
2253 int methodParameterScore = argumentCount - methodArgumentCount;
2254 if (methodParameterScore > bestParameterScore)
2255 continue; // We already have a better option
2256
2257 int methodMatchScore = 0;
2258 for (int ii = 0; ii < methodArgumentCount; ++ii) {
2259 methodMatchScore += MatchScore(actual: (v = QV4::Value::fromStaticValue(staticValue: callArgs->args[ii])),
2260 conversionType: methodArgTypes[ii]);
2261 }
2262
2263 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
2264 best = attempt;
2265 bestParameterScore = methodParameterScore;
2266 bestMatchScore = methodMatchScore;
2267 }
2268
2269 if (bestParameterScore == 0 && bestMatchScore == 0)
2270 break; // We can't get better than that
2271 };
2272
2273 if (best.isValid()) {
2274 return CallPrecise(object, data: best, engine, callArgs, callType: QMetaObject::CreateInstance);
2275 } else {
2276 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
2277 for (int i = 0; i < numberOfConstructors; i++) {
2278 const QQmlPropertyData & candidate = d()->constructors[i];
2279 error += QLatin1String("\n ") +
2280 QString::fromUtf8(str: d()->metaObject->constructor(index: candidate.coreIndex())
2281 .methodSignature());
2282 }
2283
2284 return engine->throwError(message: error);
2285 }
2286}
2287
2288bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
2289{
2290 Q_ASSERT(a->as<QMetaObjectWrapper>());
2291 QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
2292 QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>();
2293 if (!bMetaObject)
2294 return true;
2295 return aMetaObject->metaObject() == bMetaObject->metaObject();
2296}
2297
2298DEFINE_OBJECT_VTABLE(QMetaObjectWrapper);
2299
2300
2301
2302
2303void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
2304{
2305 Object::init();
2306 this->signalIndex = signalIndex;
2307 setObject(object);
2308}
2309
2310DEFINE_OBJECT_VTABLE(QmlSignalHandler);
2311
2312void QmlSignalHandler::initProto(ExecutionEngine *engine)
2313{
2314 if (engine->signalHandlerPrototype()->d_unchecked())
2315 return;
2316
2317 Scope scope(engine);
2318 ScopedObject o(scope, engine->newObject());
2319 QV4::ScopedString connect(scope, engine->newIdentifier(QStringLiteral("connect")));
2320 QV4::ScopedString disconnect(scope, engine->newIdentifier(QStringLiteral("disconnect")));
2321 o->put(name: connect, v: QV4::ScopedValue(scope, engine->functionPrototype()->get(name: connect)));
2322 o->put(name: disconnect, v: QV4::ScopedValue(scope, engine->functionPrototype()->get(name: disconnect)));
2323
2324 engine->jsObjects[QV4::ExecutionEngine::SignalHandlerProto] = o->d();
2325}
2326
2327void MultiplyWrappedQObjectMap::insert(QObject *key, Heap::Object *value)
2328{
2329 QHash<QObject*, QV4::WeakValue>::operator[](key).set(engine: value->internalClass->engine, obj: value);
2330 connect(sender: key, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(removeDestroyedObject(QObject*)));
2331}
2332
2333
2334
2335MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it)
2336{
2337 disconnect(sender: it.key(), SIGNAL(destroyed(QObject*)), receiver: this, SLOT(removeDestroyedObject(QObject*)));
2338 return QHash<QObject*, QV4::WeakValue>::erase(it);
2339}
2340
2341void MultiplyWrappedQObjectMap::remove(QObject *key)
2342{
2343 Iterator it = find(key);
2344 if (it == end())
2345 return;
2346 erase(it);
2347}
2348
2349void MultiplyWrappedQObjectMap::mark(QObject *key, MarkStack *markStack)
2350{
2351 Iterator it = find(key);
2352 if (it == end())
2353 return;
2354 it->markOnce(markStack);
2355}
2356
2357void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
2358{
2359 QHash<QObject*, QV4::WeakValue>::remove(key: object);
2360}
2361
2362QT_END_NAMESPACE
2363
2364#include "moc_qv4qobjectwrapper_p.cpp"
2365

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