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(method->object(), method->methodIndex());
108 }
109
110 return qMakePair((QObject *)nullptr, -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(handler->object(), handler->signalIndex());
125 }
126
127 return qMakePair((QObject *)nullptr, -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(object, &rv);
139 return QV4::QObjectWrapper::wrap(v4, rv);
140 } else if (property.isQList()) {
141 return QmlListWrapper::create(v4, object, property.coreIndex(), property.propType());
142 } else if (property.propType() == QMetaType::QReal) {
143 qreal v = 0;
144 property.readProperty(object, &v);
145 return QV4::Encode(v);
146 } else if (property.propType() == QMetaType::Int || property.isEnum()) {
147 int v = 0;
148 property.readProperty(object, &v);
149 return QV4::Encode(v);
150 } else if (property.propType() == QMetaType::Bool) {
151 bool v = false;
152 property.readProperty(object, &v);
153 return QV4::Encode(v);
154 } else if (property.propType() == QMetaType::QString) {
155 QString v;
156 property.readProperty(object, &v);
157 return v4->newString(v)->asReturnedValue();
158 } else if (property.propType() == QMetaType::UInt) {
159 uint v = 0;
160 property.readProperty(object, &v);
161 return QV4::Encode(v);
162 } else if (property.propType() == QMetaType::Float) {
163 float v = 0;
164 property.readProperty(object, &v);
165 return QV4::Encode(v);
166 } else if (property.propType() == QMetaType::Double) {
167 double v = 0;
168 property.readProperty(object, &v);
169 return QV4::Encode(v);
170 } else if (property.propType() == qMetaTypeId<QJSValue>()) {
171 QJSValue v;
172 property.readProperty(object, &v);
173 return QJSValuePrivate::convertedToValue(v4, v);
174 } else if (property.isQVariant()) {
175 QVariant v;
176 property.readProperty(object, &v);
177
178 if (QQmlValueTypeFactory::isValueType(v.userType())) {
179 if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(v.userType()))
180 return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, v.userType()); // VariantReference value-type.
181 }
182
183 return scope.engine->fromVariant(v);
184 } else if (QQmlValueTypeFactory::isValueType(property.propType())) {
185 if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType()))
186 return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, 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(v4, property.propType(), object, property.coreIndex(), !property.isWritable(), &succeeded));
192 if (succeeded)
193 return retn->asReturnedValue();
194#endif
195 }
196
197 if (property.propType() == QMetaType::UnknownType) {
198 QMetaProperty p = object->metaObject()->property(property.coreIndex());
199 qWarning("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(object, v.data());
205 return scope.engine->fromVariant(v);
206 }
207}
208
209void QObjectWrapper::initializeBindings(ExecutionEngine *engine)
210{
211 engine->functionPrototype()->defineDefaultProperty(QStringLiteral("connect"), method_connect);
212 engine->functionPrototype()->defineDefaultProperty(QStringLiteral("disconnect"), 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(o, false);
226 QQmlPropertyData *result = nullptr;
227 if (ddata && ddata->propertyCache)
228 result = ddata->propertyCache->property(name, o, 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(object, QQmlPropertyIndex(property->coreIndex()));
237
238 if (property->isFunction() && !property->isVarProperty()) {
239 if (property->isVMEFunction()) {
240 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
241 Q_ASSERT(vmemo);
242 return vmemo->vmeMethod(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(global, object, property->coreIndex());
249 } else if (property->isSignalHandler()) {
250 QmlSignalHandler::initProto(engine);
251 return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
252 } else {
253 ExecutionContext *global = engine->rootContext();
254 return QV4::QObjectMethod::create(global, object, property->coreIndex());
255 }
256 }
257
258 QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(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(object);
265 Q_ASSERT(vmemo);
266 return vmemo->vmeProperty(property->coreIndex());
267 } else {
268 return loadProperty(engine, object, *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(v4->id_destroy()))
276 index = QV4::QObjectMethod::DestroyMethod;
277 else if (name->equals(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(global, 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(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, d()->object(), hasProperty))
328 return *methodValue;
329
330 QQmlPropertyData local;
331 QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local);
332
333 if (!result) {
334 // Check for attached properties
335 if (includeImports && name->startsWithUpper()) {
336 if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty))
337 return *importProperty;
338 }
339 return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty);
340 }
341
342 QQmlData *ddata = QQmlData::get(d()->object(), false);
343
344 if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
345 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(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(v4, d()->object(), 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(engine, name, object, hasProperty))
367 return *methodValue;
368
369 QQmlData *ddata = QQmlData::get(object, false);
370 QQmlPropertyData local;
371 QQmlPropertyData *result = findProperty(engine, object, qmlContext, name, revisionMode, &local);
372
373 if (result) {
374 if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
375 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(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)
386 *property = result;
387
388 return getProperty(engine, object, 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(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(result))
432 return false;
433 }
434
435 setProperty(engine, object, 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(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(property->propType()))
457 error += QLatin1String("[unknown property type]");
458 else
459 error += QLatin1String(QMetaType::typeName(property->propType()));
460 scope.engine->throwError(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, f->function(), object, callingQmlContext, ctx);
472 newBinding->setSourceLocation(bindingFunction->currentLocation());
473 if (f->isBoundFunction())
474 newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
475 newBinding->setTarget(object, *property, nullptr);
476 }
477 }
478
479 if (newBinding) {
480 QQmlPropertyPrivate::setBinding(newBinding);
481 } else {
482 if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
483 if (auto binding = QQmlPropertyPrivate::binding(object, 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(object, 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(object);
500 Q_ASSERT(vmemo);
501 vmemo->setVMEProperty(property->coreIndex(), 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(property->propType()))
526 error += QLatin1String("[unknown property type]");
527 else
528 error += QLatin1String(QMetaType::typeName(property->propType()));
529 scope.engine->throwError(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(object);
545 Q_ASSERT(vmemo);
546 vmemo->setVMEProperty(property->coreIndex(), 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(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, qMetaTypeId<QList<QObject *> >());
561 else
562 v = scope.engine->toVariant(value, 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(v.userType());
569
570 const char *targetTypeName = QMetaType::typeName(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(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, 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(scope.engine, 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(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(scope.engine, 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(object, 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 if (ddata->jsEngineId == markStack->engine->m_engineId)
642 ddata->jsWrapper.markOnce(markStack);
643 else if (markStack->engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
644 markStack->engine->m_multiplyWrappedQObjects->mark(object, markStack);
645}
646
647void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value)
648{
649 setProperty(engine, d()->object(), propertyIndex, value);
650}
651
652void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value)
653{
654 Q_ASSERT(propertyIndex < 0xffff);
655 Q_ASSERT(propertyIndex >= 0);
656
657 if (QQmlData::wasDeleted(object))
658 return;
659 QQmlData *ddata = QQmlData::get(object, /*create*/false);
660 if (!ddata)
661 return;
662
663 QQmlPropertyCache *cache = ddata->propertyCache;
664 Q_ASSERT(cache);
665 QQmlPropertyData *property = cache->property(propertyIndex);
666 Q_ASSERT(property); // We resolved this property earlier, so it better exist!
667 return setProperty(engine, object, property, value);
668}
669
670bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
671{
672 Q_ASSERT(a->as<QV4::QObjectWrapper>());
673 QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a);
674 QV4::Object *o = b->as<Object>();
675 if (o) {
676 if (QV4::QQmlTypeWrapper *qmlTypeWrapper = o->as<QV4::QQmlTypeWrapper>())
677 return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object();
678 }
679
680 return false;
681}
682
683ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
684{
685 if (QJSEngine *jsEngine = engine->jsEngine()) {
686 if (QQmlPropertyCache *cache = QQmlData::ensurePropertyCache(jsEngine, object)) {
687 ReturnedValue result = QV4::Encode::null();
688 void *args[] = { &result, &engine };
689 if (cache->callJSFactoryMethod(object, args))
690 return result;
691 }
692 }
693 return (engine->memoryManager->allocate<QV4::QObjectWrapper>(object))->asReturnedValue();
694}
695
696QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
697{
698 if (!id.isString())
699 return Object::virtualGet(m, id, receiver, hasProperty);
700
701 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
702 Scope scope(that);
703 ScopedString n(scope, id.asStringOrSymbol());
704 QQmlContextData *qmlContext = that->engine()->callingQmlContext();
705 return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true);
706}
707
708bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
709{
710 if (!id.isString())
711 return Object::virtualPut(m, id, value, receiver);
712
713 Scope scope(m);
714 QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
715 ScopedString name(scope, id.asStringOrSymbol());
716
717 if (scope.engine->hasException || QQmlData::wasDeleted(that->d()->object()))
718 return false;
719
720 QQmlContextData *qmlContext = scope.engine->callingQmlContext();
721 if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) {
722 QQmlData *ddata = QQmlData::get(that->d()->object());
723 // Types created by QML are not extensible at run-time, but for other QObjects we can store them
724 // as regular JavaScript properties, like on JavaScript objects.
725 if (ddata && ddata->context) {
726 QString error = QLatin1String("Cannot assign to non-existent property \"") +
727 name->toQString() + QLatin1Char('\"');
728 scope.engine->throwError(error);
729 return false;
730 } else {
731 return QV4::Object::virtualPut(m, id, value, receiver);
732 }
733 }
734
735 return true;
736}
737
738PropertyAttributes QObjectWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
739{
740 if (id.isString()) {
741 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
742 const QObject *thatObject = that->d()->object();
743 if (!QQmlData::wasDeleted(thatObject)) {
744 Scope scope(m);
745 ScopedString n(scope, id.asStringOrSymbol());
746 QQmlContextData *qmlContext = scope.engine->callingQmlContext();
747 QQmlPropertyData local;
748 if (that->findProperty(scope.engine, qmlContext, n, IgnoreRevision, &local)
749 || n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) {
750 if (p) {
751 // ### probably not the fastest implementation
752 bool hasProperty;
753 p->value = that->getQmlProperty(qmlContext, n, IgnoreRevision, &hasProperty, /*includeImports*/ true);
754 }
755 return QV4::Attr_Data;
756 }
757 }
758 }
759
760 return QV4::Object::virtualGetOwnProperty(m, id, p);
761}
762
763struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
764{
765 int propertyIndex = 0;
766 ~QObjectWrapperOwnPropertyKeyIterator() override = default;
767 PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
768
769private:
770 QSet<QByteArray> m_alreadySeen;
771};
772
773PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs)
774{
775 // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
776 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
777 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
778 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
779
780 const QObjectWrapper *that = static_cast<const QObjectWrapper*>(o);
781
782 QObject *thatObject = that->d()->object();
783 if (thatObject && !QQmlData::wasDeleted(thatObject)) {
784 const QMetaObject *mo = thatObject->metaObject();
785 // These indices don't apply to gadgets, so don't block them.
786 const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
787 const int propertyCount = mo->propertyCount();
788 if (propertyIndex < propertyCount) {
789 ExecutionEngine *thatEngine = that->engine();
790 Scope scope(thatEngine);
791 const QMetaProperty property = mo->property(propertyIndex);
792 ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name())));
793 ++propertyIndex;
794 if (attrs)
795 *attrs= QV4::Attr_Data;
796 if (pd) {
797 QQmlPropertyData local;
798 local.load(property);
799 pd->value = that->getProperty(thatEngine, thatObject, &local);
800 }
801 return propName->toPropertyKey();
802 }
803 const int methodCount = mo->methodCount();
804 while (propertyIndex < propertyCount + methodCount) {
805 Q_ASSERT(propertyIndex >= propertyCount);
806 int index = propertyIndex - propertyCount;
807 const QMetaMethod method = mo->method(index);
808 ++propertyIndex;
809 if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
810 continue;
811 // filter out duplicates due to overloads:
812 if (m_alreadySeen.contains(method.name()))
813 continue;
814 else
815 m_alreadySeen.insert(method.name());
816 ExecutionEngine *thatEngine = that->engine();
817 Scope scope(thatEngine);
818 ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name())));
819 if (attrs)
820 *attrs = QV4::Attr_Data;
821 if (pd) {
822 QQmlPropertyData local;
823 local.load(method);
824 pd->value = that->getProperty(thatEngine, thatObject, &local);
825 }
826 return methodName->toPropertyKey();
827 }
828 }
829
830 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
831}
832
833OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
834{
835 *target = *m;
836 return new QObjectWrapperOwnPropertyKeyIterator;
837}
838
839ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
840{
841 // Keep this code in sync with ::getQmlProperty
842 PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
843 runtimeStrings[lookup->nameIndex]);
844 if (!id.isString())
845 return Object::virtualResolveLookupGetter(object, engine, lookup);
846 Scope scope(engine);
847
848 const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
849 ScopedString name(scope, id.asStringOrSymbol());
850 QQmlContextData *qmlContext = engine->callingQmlContext();
851
852 QObject * const qobj = This->d()->object();
853
854 if (QQmlData::wasDeleted(qobj))
855 return QV4::Encode::undefined();
856
857 if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj))
858 return *methodValue;
859
860 QQmlData *ddata = QQmlData::get(qobj, false);
861 if (!ddata || !ddata->propertyCache) {
862 QQmlPropertyData local;
863 QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local);
864 return property ? getProperty(engine, qobj, property) : QV4::Encode::undefined();
865 }
866 QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
867
868 if (!property) {
869 // Check for attached properties
870 if (name->startsWithUpper()) {
871 if (auto importProperty = getPropertyFromImports(engine, name, qmlContext, qobj))
872 return *importProperty;
873 }
874 return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
875 }
876
877 lookup->qobjectLookup.ic = This->internalClass();
878 lookup->qobjectLookup.propertyCache = ddata->propertyCache;
879 lookup->qobjectLookup.propertyCache->addref();
880 lookup->qobjectLookup.propertyData = property;
881 lookup->getter = QV4::QObjectWrapper::lookupGetter;
882 return lookup->getter(lookup, engine, *object);
883}
884
885ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
886{
887 const auto revertLookup = [lookup, engine, &object]() {
888 lookup->qobjectLookup.propertyCache->release();
889 lookup->qobjectLookup.propertyCache = nullptr;
890 lookup->getter = Lookup::getterGeneric;
891 return Lookup::getterGeneric(lookup, engine, object);
892 };
893
894 return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
895}
896
897bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
898 const Value &value)
899{
900 return Object::virtualResolveLookupSetter(object, engine, lookup, value);
901}
902
903namespace QV4 {
904
905struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
906{
907 QV4::PersistentValue function;
908 QV4::PersistentValue thisObject;
909 int signalIndex;
910
911 QObjectSlotDispatcher()
912 : QtPrivate::QSlotObjectBase(&impl)
913 , signalIndex(-1)
914 {}
915
916 static void impl(int which, QSlotObjectBase *this_, QObject *r, void **metaArgs, bool *ret)
917 {
918 switch (which) {
919 case Destroy: {
920 delete static_cast<QObjectSlotDispatcher*>(this_);
921 }
922 break;
923 case Call: {
924 QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
925 QV4::ExecutionEngine *v4 = This->function.engine();
926 // Might be that we're still connected to a signal that's emitted long
927 // after the engine died. We don't track connections in a global list, so
928 // we need this safeguard.
929 if (!v4)
930 break;
931
932 QQmlMetaObject::ArgTypeStorage storage;
933 int *argsTypes = QQmlMetaObject(r).methodParameterTypes(This->signalIndex, &storage, nullptr);
934
935 int argCount = argsTypes ? argsTypes[0]:0;
936
937 QV4::Scope scope(v4);
938 QV4::ScopedFunctionObject f(scope, This->function.value());
939
940 QV4::JSCallData jsCallData(scope, argCount);
941 *jsCallData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value();
942 for (int ii = 0; ii < argCount; ++ii) {
943 int type = argsTypes[ii + 1];
944 if (type == qMetaTypeId<QVariant>()) {
945 jsCallData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
946 } else {
947 jsCallData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
948 }
949 }
950
951 f->call(jsCallData);
952 if (scope.hasException()) {
953 QQmlError error = v4->catchExceptionAsQmlError();
954 if (error.description().isEmpty()) {
955 QV4::ScopedString name(scope, f->name());
956 error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString()));
957 }
958 if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
959 QQmlEnginePrivate::get(qmlEngine)->warning(error);
960 } else {
961 QMessageLogger(error.url().toString().toLatin1().constData(),
962 error.line(), nullptr).warning().noquote()
963 << error.toString();
964 }
965 }
966 }
967 break;
968 case Compare: {
969 QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
970 if (connection->function.isUndefined()) {
971 *ret = false;
972 return;
973 }
974
975 // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
976 // for the new-style QObject::connect. Here we use the engine pointer as sentinel
977 // to distinguish those type of QSlotObjectBase connections from our QML connections.
978 QV4::ExecutionEngine *v4 = reinterpret_cast<QV4::ExecutionEngine*>(metaArgs[0]);
979 if (v4 != connection->function.engine()) {
980 *ret = false;
981 return;
982 }
983
984 QV4::Scope scope(v4);
985 QV4::ScopedValue function(scope, *reinterpret_cast<QV4::Value*>(metaArgs[1]));
986 QV4::ScopedValue thisObject(scope, *reinterpret_cast<QV4::Value*>(metaArgs[2]));
987 QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
988 int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
989
990 if (slotIndexToDisconnect != -1) {
991 // This is a QObject function wrapper
992 if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
993 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
994
995 QV4::ScopedFunctionObject f(scope, connection->function.value());
996 QPair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f);
997 if (connectedFunctionData.first == receiverToDisconnect &&
998 connectedFunctionData.second == slotIndexToDisconnect) {
999 *ret = true;
1000 return;
1001 }
1002 }
1003 } else {
1004 // This is a normal JS function
1005 if (RuntimeHelpers::strictEqual(*connection->function.valueRef(), function) &&
1006 connection->thisObject.isUndefined() == thisObject->isUndefined() &&
1007 (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
1008 *ret = true;
1009 return;
1010 }
1011 }
1012
1013 *ret = false;
1014 }
1015 break;
1016 case NumOperations:
1017 break;
1018 }
1019 };
1020};
1021
1022} // namespace QV4
1023
1024ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1025{
1026 QV4::Scope scope(b);
1027
1028 if (argc == 0)
1029 THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
1030
1031 QPair<QObject *, int> signalInfo = extractQtSignal(*thisObject);
1032 QObject *signalObject = signalInfo.first;
1033 int signalIndex = signalInfo.second; // in method range, not signal range!
1034
1035 if (signalIndex < 0)
1036 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1037
1038 if (!signalObject)
1039 THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1040
1041 if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1042 THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal");
1043
1044 QV4::ScopedFunctionObject f(scope);
1045 QV4::ScopedValue object (scope, QV4::Encode::undefined());
1046
1047 if (argc == 1) {
1048 f = argv[0];
1049 } else if (argc >= 2) {
1050 object = argv[0];
1051 f = argv[1];
1052 }
1053
1054 if (!f)
1055 THROW_GENERIC_ERROR("Function.prototype.connect: target is not a function");
1056
1057 if (!object->isUndefined() && !object->isObject())
1058 THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object");
1059
1060 QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher;
1061 slot->signalIndex = signalIndex;
1062
1063 slot->thisObject.set(scope.engine, object);
1064 slot->function.set(scope.engine, f);
1065
1066 if (QQmlData *ddata = QQmlData::get(signalObject)) {
1067 if (QQmlPropertyCache *propertyCache = ddata->propertyCache) {
1068 QQmlPropertyPrivate::flushSignal(signalObject, propertyCache->methodIndexToSignalIndex(signalIndex));
1069 }
1070 }
1071 QObjectPrivate::connect(signalObject, signalIndex, slot, Qt::AutoConnection);
1072
1073 RETURN_UNDEFINED();
1074}
1075
1076ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1077{
1078 QV4::Scope scope(b);
1079
1080 if (argc == 0)
1081 THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
1082
1083 QPair<QObject *, int> signalInfo = extractQtSignal(*thisObject);
1084 QObject *signalObject = signalInfo.first;
1085 int signalIndex = signalInfo.second;
1086
1087 if (signalIndex == -1)
1088 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1089
1090 if (!signalObject)
1091 THROW_GENERIC_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1092
1093 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1094 THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal");
1095
1096 QV4::ScopedFunctionObject functionValue(scope);
1097 QV4::ScopedValue functionThisValue(scope, QV4::Encode::undefined());
1098
1099 if (argc == 1) {
1100 functionValue = argv[0];
1101 } else if (argc >= 2) {
1102 functionThisValue = argv[0];
1103 functionValue = argv[1];
1104 }
1105
1106 if (!functionValue)
1107 THROW_GENERIC_ERROR("Function.prototype.disconnect: target is not a function");
1108
1109 if (!functionThisValue->isUndefined() && !functionThisValue->isObject())
1110 THROW_GENERIC_ERROR("Function.prototype.disconnect: target this is not an object");
1111
1112 QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(functionValue);
1113
1114 void *a[] = {
1115 scope.engine,
1116 functionValue.ptr,
1117 functionThisValue.ptr,
1118 functionData.first,
1119 &functionData.second
1120 };
1121
1122 QObjectPrivate::disconnect(signalObject, signalIndex, reinterpret_cast<void**>(&a));
1123
1124 RETURN_UNDEFINED();
1125}
1126
1127static void markChildQObjectsRecursively(QObject *parent, QV4::MarkStack *markStack)
1128{
1129 const QObjectList &children = parent->children();
1130 for (int i = 0; i < children.count(); ++i) {
1131 QObject *child = children.at(i);
1132 if (!child)
1133 continue;
1134 QObjectWrapper::markWrapper(child, markStack);
1135 markChildQObjectsRecursively(child, markStack);
1136 }
1137}
1138
1139void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack)
1140{
1141 QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
1142
1143 if (QObject *o = This->object()) {
1144 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
1145 if (vme)
1146 vme->mark(markStack);
1147
1148 // Children usually don't need to be marked, the gc keeps them alive.
1149 // But in the rare case of a "floating" QObject without a parent that
1150 // _gets_ marked (we've been called here!) then we also need to
1151 // propagate the marking down to the children recursively.
1152 if (!o->parent())
1153 markChildQObjectsRecursively(o, markStack);
1154 }
1155
1156 Object::markObjects(that, markStack);
1157}
1158
1159void QObjectWrapper::destroyObject(bool lastCall)
1160{
1161 Heap::QObjectWrapper *h = d();
1162 if (!h->internalClass)
1163 return; // destroyObject already got called
1164
1165 if (h->object()) {
1166 QQmlData *ddata = QQmlData::get(h->object(), false);
1167 if (ddata) {
1168 if (!h->object()->parent() && !ddata->indestructible) {
1169 if (ddata && ddata->ownContext) {
1170 Q_ASSERT(ddata->ownContext == ddata->context);
1171 ddata->ownContext->emitDestruction();
1172 ddata->ownContext = nullptr;
1173 ddata->context = nullptr;
1174 }
1175 // This object is notionally destroyed now
1176 ddata->isQueuedForDeletion = true;
1177 if (lastCall)
1178 delete h->object();
1179 else
1180 h->object()->deleteLater();
1181 } else {
1182 // If the object is C++-owned, we still have to release the weak reference we have
1183 // to it.
1184 ddata->jsWrapper.clear();
1185 if (lastCall && ddata->propertyCache) {
1186 ddata->propertyCache->release();
1187 ddata->propertyCache = nullptr;
1188 }
1189 }
1190 }
1191 }
1192
1193 h->~Data();
1194}
1195
1196
1197DEFINE_OBJECT_VTABLE(QObjectWrapper);
1198
1199namespace {
1200
1201template<typename A, typename B, typename C, typename D, typename E, typename F, typename G>
1202class MaxSizeOf7 {
1203 template<typename Z, typename X>
1204 struct SMax {
1205 char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
1206 };
1207public:
1208 static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, G> > > > > >);
1209};
1210
1211struct CallArgument {
1212 inline CallArgument();
1213 inline ~CallArgument();
1214 inline void *dataPtr();
1215
1216 inline void initAsType(int type);
1217 inline bool fromValue(int type, ExecutionEngine *, const QV4::Value &);
1218 inline ReturnedValue toValue(ExecutionEngine *);
1219
1220private:
1221 CallArgument(const CallArgument &);
1222
1223 inline void cleanup();
1224
1225 template <class T, class M>
1226 void fromContainerValue(const QV4::Object *object, int type, M CallArgument::*member, bool &queryEngine);
1227
1228 union {
1229 float floatValue;
1230 double doubleValue;
1231 quint32 intValue;
1232 bool boolValue;
1233 QObject *qobjectPtr;
1234 std::vector<int> *stdVectorIntPtr;
1235 std::vector<qreal> *stdVectorRealPtr;
1236 std::vector<bool> *stdVectorBoolPtr;
1237 std::vector<QString> *stdVectorQStringPtr;
1238 std::vector<QUrl> *stdVectorQUrlPtr;
1239#if QT_CONFIG(qml_itemmodel)
1240 std::vector<QModelIndex> *stdVectorQModelIndexPtr;
1241#endif
1242
1243 char allocData[MaxSizeOf7<QVariant,
1244 QString,
1245 QList<QObject *>,
1246 QJSValue,
1247 QJsonArray,
1248 QJsonObject,
1249 QJsonValue>::Size];
1250 qint64 q_for_alignment;
1251 };
1252
1253 // Pointers to allocData
1254 union {
1255 QString *qstringPtr;
1256 QByteArray *qbyteArrayPtr;
1257 QVariant *qvariantPtr;
1258 QList<QObject *> *qlistPtr;
1259 QJSValue *qjsValuePtr;
1260 QJsonArray *jsonArrayPtr;
1261 QJsonObject *jsonObjectPtr;
1262 QJsonValue *jsonValuePtr;
1263 };
1264
1265 int type;
1266};
1267}
1268
1269static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, int returnType, int argCount,
1270 int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
1271 QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
1272{
1273 if (argCount > 0) {
1274 // Convert all arguments.
1275 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1276 args[0].initAsType(returnType);
1277 for (int ii = 0; ii < argCount; ++ii) {
1278 if (!args[ii + 1].fromValue(argTypes[ii], engine,
1279 callArgs->args[ii].asValue<QV4::Value>())) {
1280 qWarning() << QString::fromLatin1("Could not convert argument %1 at").arg(ii);
1281 const StackTrace stack = engine->stackTrace();
1282 for (const StackFrame &frame : stack) {
1283 qWarning() << "\t" << frame.function + QLatin1Char('@') + frame.source
1284 + (frame.line > 0
1285 ? (QLatin1Char(':') + QString::number(frame.line))
1286 : QString());
1287
1288 }
1289 qWarning() << QLatin1String("Passing incompatible arguments to C++ functions from "
1290 "JavaScript is dangerous and deprecated.");
1291 qWarning() << QLatin1String("This will throw a JavaScript TypeError in future "
1292 "releases of Qt!");
1293
1294 }
1295 }
1296 QVarLengthArray<void *, 9> argData(args.count());
1297 for (int ii = 0; ii < args.count(); ++ii)
1298 argData[ii] = args[ii].dataPtr();
1299
1300 object.metacall(callType, index, argData.data());
1301
1302 return args[0].toValue(engine);
1303
1304 } else if (returnType != QMetaType::Void) {
1305
1306 CallArgument arg;
1307 arg.initAsType(returnType);
1308
1309 void *args[] = { arg.dataPtr() };
1310
1311 object.metacall(callType, index, args);
1312
1313 return arg.toValue(engine);
1314
1315 } else {
1316
1317 void *args[] = { nullptr };
1318 object.metacall(callType, index, args);
1319 return Encode::undefined();
1320
1321 }
1322}
1323
1324/*
1325 Returns the match score for converting \a actual to be of type \a conversionType. A
1326 zero score means "perfect match" whereas a higher score is worse.
1327
1328 The conversion table is copied out of the \l QScript::callQtMethod() function.
1329*/
1330static int MatchScore(const QV4::Value &actual, int conversionType)
1331{
1332 if (actual.isNumber()) {
1333 switch (conversionType) {
1334 case QMetaType::Double:
1335 return 0;
1336 case QMetaType::Float:
1337 return 1;
1338 case QMetaType::LongLong:
1339 case QMetaType::ULongLong:
1340 return 2;
1341 case QMetaType::Long:
1342 case QMetaType::ULong:
1343 return 3;
1344 case QMetaType::Int:
1345 case QMetaType::UInt:
1346 return 4;
1347 case QMetaType::Short:
1348 case QMetaType::UShort:
1349 return 5;
1350 break;
1351 case QMetaType::Char:
1352 case QMetaType::UChar:
1353 return 6;
1354 case QMetaType::QJsonValue:
1355 return 5;
1356 default:
1357 return 10;
1358 }
1359 } else if (actual.isString()) {
1360 switch (conversionType) {
1361 case QMetaType::QString:
1362 return 0;
1363 case QMetaType::QJsonValue:
1364 return 5;
1365 default:
1366 return 10;
1367 }
1368 } else if (actual.isBoolean()) {
1369 switch (conversionType) {
1370 case QMetaType::Bool:
1371 return 0;
1372 case QMetaType::QJsonValue:
1373 return 5;
1374 default:
1375 return 10;
1376 }
1377 } else if (actual.as<DateObject>()) {
1378 switch (conversionType) {
1379 case QMetaType::QDateTime:
1380 return 0;
1381 case QMetaType::QDate:
1382 return 1;
1383 case QMetaType::QTime:
1384 return 2;
1385 default:
1386 return 10;
1387 }
1388 } else if (actual.as<QV4::RegExpObject>()) {
1389 switch (conversionType) {
1390 case QMetaType::QRegExp:
1391#if QT_CONFIG(regularexpression)
1392 case QMetaType::QRegularExpression:
1393#endif
1394 return 0;
1395 default:
1396 return 10;
1397 }
1398 } else if (actual.as<ArrayBuffer>()) {
1399 switch (conversionType) {
1400 case QMetaType::QByteArray:
1401 return 0;
1402 default:
1403 return 10;
1404 }
1405 } else if (actual.as<ArrayObject>()) {
1406 switch (conversionType) {
1407 case QMetaType::QJsonArray:
1408 return 3;
1409 case QMetaType::QStringList:
1410 case QMetaType::QVariantList:
1411 return 5;
1412 case QMetaType::QVector4D:
1413 case QMetaType::QMatrix4x4:
1414 return 6;
1415 case QMetaType::QVector3D:
1416 return 7;
1417 default:
1418 return 10;
1419 }
1420 } else if (actual.isNull()) {
1421 switch (conversionType) {
1422 case QMetaType::Nullptr:
1423 case QMetaType::VoidStar:
1424 case QMetaType::QObjectStar:
1425 case QMetaType::QJsonValue:
1426 return 0;
1427 default: {
1428 const char *typeName = QMetaType::typeName(conversionType);
1429 if (typeName && typeName[strlen(typeName) - 1] == '*')
1430 return 0;
1431 else
1432 return 10;
1433 }
1434 }
1435 } else if (const QV4::Object *obj = actual.as<QV4::Object>()) {
1436 if (obj->as<QV4::VariantObject>()) {
1437 if (conversionType == qMetaTypeId<QVariant>())
1438 return 0;
1439 if (obj->engine()->toVariant(actual, -1).userType() == conversionType)
1440 return 0;
1441 else
1442 return 10;
1443 }
1444
1445 if (obj->as<QObjectWrapper>()) {
1446 switch (conversionType) {
1447 case QMetaType::QObjectStar:
1448 return 0;
1449 default:
1450 return 10;
1451 }
1452 }
1453
1454 if (obj->as<QV4::QQmlValueTypeWrapper>()) {
1455 if (obj->engine()->toVariant(actual, -1).userType() == conversionType)
1456 return 0;
1457 return 10;
1458 } else if (conversionType == QMetaType::QJsonObject) {
1459 return 5;
1460 } else if (conversionType == qMetaTypeId<QJSValue>()) {
1461 return 0;
1462 } else {
1463 return 10;
1464 }
1465
1466 } else {
1467 return 10;
1468 }
1469}
1470
1471static inline int QMetaObject_methods(const QMetaObject *metaObject)
1472{
1473 struct Private
1474 {
1475 int revision;
1476 int className;
1477 int classInfoCount, classInfoData;
1478 int methodCount, methodData;
1479 };
1480
1481 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1482}
1483
1484/*
1485Returns the next related method, if one, or 0.
1486*/
1487static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object,
1488 const QQmlPropertyData *current,
1489 QQmlPropertyData &dummy,
1490 const QQmlPropertyCache *propertyCache)
1491{
1492 if (!current->isOverload())
1493 return nullptr;
1494
1495 Q_ASSERT(!current->overrideIndexIsProperty());
1496
1497 if (propertyCache) {
1498 return propertyCache->method(current->overrideIndex());
1499 } else {
1500 const QMetaObject *mo = object.metaObject();
1501 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1502
1503 while (methodOffset > current->overrideIndex()) {
1504 mo = mo->superClass();
1505 methodOffset -= QMetaObject_methods(mo);
1506 }
1507
1508 // If we've been called before with the same override index, then
1509 // we can't go any further...
1510 if (&dummy == current && dummy.coreIndex() == current->overrideIndex())
1511 return nullptr;
1512
1513 QMetaMethod method = mo->method(current->overrideIndex());
1514 dummy.load(method);
1515
1516 // Look for overloaded methods
1517 QByteArray methodName = method.name();
1518 for (int ii = current->overrideIndex() - 1; ii >= methodOffset; --ii) {
1519 if (methodName == mo->method(ii).name()) {
1520 dummy.setOverload(true);
1521 dummy.setOverrideIndexIsProperty(0);
1522 dummy.setOverrideIndex(ii);
1523 return &dummy;
1524 }
1525 }
1526
1527 return &dummy;
1528 }
1529}
1530
1531static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
1532 QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
1533 QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
1534{
1535 QByteArray unknownTypeError;
1536
1537 int returnType = object.methodReturnType(data, &unknownTypeError);
1538
1539 if (returnType == QMetaType::UnknownType) {
1540 return engine->throwError(QLatin1String("Unknown method return type: ")
1541 + QLatin1String(unknownTypeError));
1542 }
1543
1544 if (data.hasArguments()) {
1545
1546 int *args = nullptr;
1547 QQmlMetaObject::ArgTypeStorage storage;
1548
1549 if (data.isConstructor())
1550 args = static_cast<const QQmlStaticMetaObject&>(object).constructorParameterTypes(
1551 data.coreIndex(), &storage, &unknownTypeError);
1552 else
1553 args = object.methodParameterTypes(data.coreIndex(), &storage, &unknownTypeError);
1554
1555 if (!args) {
1556 return engine->throwError(QLatin1String("Unknown method parameter type: ")
1557 + QLatin1String(unknownTypeError));
1558 }
1559
1560 if (args[0] > callArgs->argc()) {
1561 QString error = QLatin1String("Insufficient arguments");
1562 return engine->throwError(error);
1563 }
1564
1565 return CallMethod(object, data.coreIndex(), returnType, args[0], args + 1, engine, callArgs, callType);
1566
1567 } else {
1568
1569 return CallMethod(object, data.coreIndex(), returnType, 0, nullptr, engine, callArgs, callType);
1570
1571 }
1572}
1573
1574/*
1575Resolve the overloaded method to call. The algorithm works conceptually like this:
1576 1. Resolve the set of overloads it is *possible* to call.
1577 Impossible overloads include those that have too many parameters or have parameters
1578 of unknown type.
1579 2. Filter the set of overloads to only contain those with the closest number of
1580 parameters.
1581 For example, if we are called with 3 parameters and there are 2 overloads that
1582 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1583 3. Find the best remaining overload based on its match score.
1584 If two or more overloads have the same match score, call the last one. The match
1585 score is constructed by adding the matchScore() result for each of the parameters.
1586*/
1587static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
1588 QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache,
1589 QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
1590{
1591 int argumentCount = callArgs->argc();
1592
1593 QQmlPropertyData best;
1594 int bestParameterScore = INT_MAX;
1595 int bestMatchScore = INT_MAX;
1596
1597 QQmlPropertyData dummy;
1598 const QQmlPropertyData *attempt = &data;
1599
1600 QV4::Scope scope(engine);
1601 QV4::ScopedValue v(scope);
1602
1603 do {
1604 QQmlMetaObject::ArgTypeStorage storage;
1605 int methodArgumentCount = 0;
1606 int *methodArgTypes = nullptr;
1607 if (attempt->hasArguments()) {
1608 int *args = object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr);
1609 if (!args) // Must be an unknown argument
1610 continue;
1611
1612 methodArgumentCount = args[0];
1613 methodArgTypes = args + 1;
1614 }
1615
1616 if (methodArgumentCount > argumentCount)
1617 continue; // We don't have sufficient arguments to call this method
1618
1619 int methodParameterScore = argumentCount - methodArgumentCount;
1620 if (methodParameterScore > bestParameterScore)
1621 continue; // We already have a better option
1622
1623 int methodMatchScore = 0;
1624 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1625 methodMatchScore += MatchScore((v = Value::fromStaticValue(callArgs->args[ii])),
1626 methodArgTypes[ii]);
1627 }
1628
1629 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1630 best = *attempt;
1631 bestParameterScore = methodParameterScore;
1632 bestMatchScore = methodMatchScore;
1633 }
1634
1635 if (bestParameterScore == 0 && bestMatchScore == 0)
1636 break; // We can't get better than that
1637
1638 } while ((attempt = RelatedMethod(object, attempt, dummy, propertyCache)) != nullptr);
1639
1640 if (best.isValid()) {
1641 return CallPrecise(object, best, engine, callArgs, callType);
1642 } else {
1643 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1644 const QQmlPropertyData *candidate = &data;
1645 while (candidate) {
1646 error += QLatin1String("\n ") +
1647 QString::fromUtf8(object.metaObject()->method(candidate->coreIndex())
1648 .methodSignature());
1649 candidate = RelatedMethod(object, candidate, dummy, propertyCache);
1650 }
1651
1652 return engine->throwError(error);
1653 }
1654}
1655
1656CallArgument::CallArgument()
1657: type(QVariant::Invalid)
1658{
1659}
1660
1661CallArgument::~CallArgument()
1662{
1663 cleanup();
1664}
1665
1666void CallArgument::cleanup()
1667{
1668 if (type == QMetaType::QString) {
1669 qstringPtr->~QString();
1670 } else if (type == QMetaType::QByteArray) {
1671 qbyteArrayPtr->~QByteArray();
1672 } else if (type == -1 || type == QMetaType::QVariant) {
1673 qvariantPtr->~QVariant();
1674 } else if (type == qMetaTypeId<QJSValue>()) {
1675 qjsValuePtr->~QJSValue();
1676 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1677 qlistPtr->~QList<QObject *>();
1678 } else if (type == QMetaType::QJsonArray) {
1679 jsonArrayPtr->~QJsonArray();
1680 } else if (type == QMetaType::QJsonObject) {
1681 jsonObjectPtr->~QJsonObject();
1682 } else if (type == QMetaType::QJsonValue) {
1683 jsonValuePtr->~QJsonValue();
1684 }
1685}
1686
1687void *CallArgument::dataPtr()
1688{
1689 if (type == -1)
1690 return qvariantPtr->data();
1691 else if (type == qMetaTypeId<std::vector<int>>())
1692 return stdVectorIntPtr;
1693 else if (type == qMetaTypeId<std::vector<qreal>>())
1694 return stdVectorRealPtr;
1695 else if (type == qMetaTypeId<std::vector<bool>>())
1696 return stdVectorBoolPtr;
1697 else if (type == qMetaTypeId<std::vector<QString>>())
1698 return stdVectorQStringPtr;
1699 else if (type == qMetaTypeId<std::vector<QUrl>>())
1700 return stdVectorQUrlPtr;
1701#if QT_CONFIG(qml_itemmodel)
1702 else if (type == qMetaTypeId<std::vector<QModelIndex>>())
1703 return stdVectorQModelIndexPtr;
1704#endif
1705 else if (type != 0)
1706 return (void *)&allocData;
1707 return nullptr;
1708}
1709
1710void CallArgument::initAsType(int callType)
1711{
1712 if (type != 0) { cleanup(); type = 0; }
1713 if (callType == QMetaType::UnknownType || callType == QMetaType::Void) return;
1714
1715 if (callType == qMetaTypeId<QJSValue>()) {
1716 qjsValuePtr = new (&allocData) QJSValue();
1717 type = callType;
1718 } else if (callType == QMetaType::Int ||
1719 callType == QMetaType::UInt ||
1720 callType == QMetaType::Bool ||
1721 callType == QMetaType::Double ||
1722 callType == QMetaType::Float) {
1723 type = callType;
1724 } else if (callType == QMetaType::QObjectStar) {
1725 qobjectPtr = nullptr;
1726 type = callType;
1727 } else if (callType == QMetaType::QString) {
1728 qstringPtr = new (&allocData) QString();
1729 type = callType;
1730 } else if (callType == QMetaType::QVariant) {
1731 type = callType;
1732 qvariantPtr = new (&allocData) QVariant();
1733 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1734 type = callType;
1735 qlistPtr = new (&allocData) QList<QObject *>();
1736 } else if (callType == QMetaType::QJsonArray) {
1737 type = callType;
1738 jsonArrayPtr = new (&allocData) QJsonArray();
1739 } else if (callType == QMetaType::QJsonObject) {
1740 type = callType;
1741 jsonObjectPtr = new (&allocData) QJsonObject();
1742 } else if (callType == QMetaType::QJsonValue) {
1743 type = callType;
1744 jsonValuePtr = new (&allocData) QJsonValue();
1745 } else {
1746 type = -1;
1747 qvariantPtr = new (&allocData) QVariant(callType, (void *)nullptr);
1748 }
1749}
1750
1751#if QT_CONFIG(qml_sequence_object)
1752template <class T, class M>
1753void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine)
1754{
1755 if (object && object->isListType()) {
1756 T* ptr = static_cast<T*>(QV4::SequencePrototype::getRawContainerPtr(object, callType));
1757 if (ptr) {
1758 (this->*member) = ptr;
1759 type = callType;
1760 queryEngine = false;
1761 }
1762 }
1763}
1764#endif
1765
1766bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value)
1767{
1768 if (type != 0) {
1769 cleanup();
1770 type = 0;
1771 }
1772
1773 QV4::Scope scope(engine);
1774
1775 bool queryEngine = false;
1776 if (callType == qMetaTypeId<QJSValue>()) {
1777 qjsValuePtr = new (&allocData) QJSValue(scope.engine, value.asReturnedValue());
1778 type = qMetaTypeId<QJSValue>();
1779 } else if (callType == QMetaType::Int) {
1780 intValue = quint32(value.toInt32());
1781 type = callType;
1782 } else if (callType == QMetaType::UInt) {
1783 intValue = quint32(value.toUInt32());
1784 type = callType;
1785 } else if (callType == QMetaType::Bool) {
1786 boolValue = value.toBoolean();
1787 type = callType;
1788 } else if (callType == QMetaType::Double) {
1789 doubleValue = double(value.toNumber());
1790 type = callType;
1791 } else if (callType == QMetaType::Float) {
1792 floatValue = float(value.toNumber());
1793 type = callType;
1794 } else if (callType == QMetaType::QString) {
1795 if (value.isNull() || value.isUndefined())
1796 qstringPtr = new (&allocData) QString();
1797 else
1798 qstringPtr = new (&allocData) QString(value.toQStringNoThrow());
1799 type = callType;
1800 } else if (callType == QMetaType::QObjectStar) {
1801 qobjectPtr = nullptr;
1802 type = callType;
1803 if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>())
1804 qobjectPtr = qobjectWrapper->object();
1805 else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>())
1806 queryEngine = qmlTypeWrapper->isSingleton();
1807 else if (!value.isNull() && !value.isUndefined()) // null and undefined are nullptr
1808 return false;
1809 } else if (callType == qMetaTypeId<QVariant>()) {
1810 qvariantPtr = new (&allocData) QVariant(scope.engine->toVariant(value, -1));
1811 type = callType;
1812 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1813 qlistPtr = new (&allocData) QList<QObject *>();
1814 type = callType;
1815 QV4::ScopedArrayObject array(scope, value);
1816 if (array) {
1817 Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
1818
1819 uint length = array->getLength();
1820 for (uint ii = 0; ii < length; ++ii) {
1821 QObject *o = nullptr;
1822 qobjectWrapper = array->get(ii);
1823 if (!!qobjectWrapper)
1824 o = qobjectWrapper->object();
1825 qlistPtr->append(o);
1826 }
1827 } else {
1828 if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) {
1829 qlistPtr->append(qobjectWrapper->object());
1830 } else {
1831 qlistPtr->append(nullptr);
1832 if (!value.isNull() && !value.isUndefined())
1833 return false;
1834 }
1835 }
1836 } else if (callType == QMetaType::QJsonArray) {
1837 QV4::ScopedArrayObject a(scope, value);
1838 jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(a));
1839 type = callType;
1840 } else if (callType == QMetaType::QJsonObject) {
1841 QV4::ScopedObject o(scope, value);
1842 jsonObjectPtr = new (&allocData) QJsonObject(QV4::JsonObject::toJsonObject(o));
1843 type = callType;
1844 } else if (callType == QMetaType::QJsonValue) {
1845 jsonValuePtr = new (&allocData) QJsonValue(QV4::JsonObject::toJsonValue(value));
1846 type = callType;
1847 } else if (callType == QMetaType::Void) {
1848 *qvariantPtr = QVariant();
1849#if QT_CONFIG(qml_sequence_object)
1850 } else if (callType == qMetaTypeId<std::vector<int>>()
1851 || callType == qMetaTypeId<std::vector<qreal>>()
1852 || callType == qMetaTypeId<std::vector<bool>>()
1853 || callType == qMetaTypeId<std::vector<QString>>()
1854 || callType == qMetaTypeId<std::vector<QUrl>>()
1855#if QT_CONFIG(qml_itemmodel)
1856 || callType == qMetaTypeId<std::vector<QModelIndex>>()
1857#endif
1858 ) {
1859 queryEngine = true;
1860 const QV4::Object* object = value.as<QV4::Object>();
1861 if (callType == qMetaTypeId<std::vector<int>>()) {
1862 stdVectorIntPtr = nullptr;
1863 fromContainerValue<std::vector<int>>(object, callType, &CallArgument::stdVectorIntPtr, queryEngine);
1864 } else if (callType == qMetaTypeId<std::vector<qreal>>()) {
1865 stdVectorRealPtr = nullptr;
1866 fromContainerValue<std::vector<qreal>>(object, callType, &CallArgument::stdVectorRealPtr, queryEngine);
1867 } else if (callType == qMetaTypeId<std::vector<bool>>()) {
1868 stdVectorBoolPtr = nullptr;
1869 fromContainerValue<std::vector<bool>>(object, callType, &CallArgument::stdVectorBoolPtr, queryEngine);
1870 } else if (callType == qMetaTypeId<std::vector<QString>>()) {
1871 stdVectorQStringPtr = nullptr;
1872 fromContainerValue<std::vector<QString>>(object, callType, &CallArgument::stdVectorQStringPtr, queryEngine);
1873 } else if (callType == qMetaTypeId<std::vector<QUrl>>()) {
1874 stdVectorQUrlPtr = nullptr;
1875 fromContainerValue<std::vector<QUrl>>(object, callType, &CallArgument::stdVectorQUrlPtr, queryEngine);
1876#if QT_CONFIG(qml_itemmodel)
1877 } else if (callType == qMetaTypeId<std::vector<QModelIndex>>()) {
1878 stdVectorQModelIndexPtr = nullptr;
1879 fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine);
1880#endif
1881 }
1882#endif
1883 } else if (QMetaType::typeFlags(callType)
1884 & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) {
1885 // You can assign null or undefined to any pointer. The result is a nullptr.
1886 if (value.isNull() || value.isUndefined()) {
1887 qvariantPtr = new (&allocData) QVariant(callType, nullptr);
1888 type = callType;
1889 } else {
1890 queryEngine = true;
1891 }
1892 } else {
1893 queryEngine = true;
1894 }
1895
1896 if (queryEngine) {
1897 qvariantPtr = new (&allocData) QVariant();
1898 type = -1;
1899
1900 QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
1901 QVariant v = scope.engine->toVariant(value, callType);
1902
1903 if (v.userType() == callType) {
1904 *qvariantPtr = v;
1905 } else if (v.canConvert(callType)) {
1906 *qvariantPtr = v;
1907 qvariantPtr->convert(callType);
1908 } else {
1909 QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
1910 if (!mo.isNull()) {
1911 QObject *obj = ep->toQObject(v);
1912
1913 if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) {
1914 *qvariantPtr = QVariant(callType, nullptr);
1915 return false;
1916 }
1917
1918 *qvariantPtr = QVariant(callType, &obj);
1919 return true;
1920 }
1921
1922 *qvariantPtr = QVariant(callType, (void *)nullptr);
1923 return false;
1924 }
1925 }
1926 return true;
1927}
1928
1929QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
1930{
1931 QV4::Scope scope(engine);
1932
1933 if (type == qMetaTypeId<QJSValue>()) {
1934 return QJSValuePrivate::convertedToValue(scope.engine, *qjsValuePtr);
1935 } else if (type == QMetaType::Int) {
1936 return QV4::Encode(int(intValue));
1937 } else if (type == QMetaType::UInt) {
1938 return QV4::Encode((uint)intValue);
1939 } else if (type == QMetaType::Bool) {
1940 return QV4::Encode(boolValue);
1941 } else if (type == QMetaType::Double) {
1942 return QV4::Encode(doubleValue);
1943 } else if (type == QMetaType::Float) {
1944 return QV4::Encode(floatValue);
1945 } else if (type == QMetaType::QString) {
1946 return QV4::Encode(engine->newString(*qstringPtr));
1947 } else if (type == QMetaType::QByteArray) {
1948 return QV4::Encode(engine->newArrayBuffer(*qbyteArrayPtr));
1949 } else if (type == QMetaType::QObjectStar) {
1950 QObject *object = qobjectPtr;
1951 if (object)
1952 QQmlData::get(object, true)->setImplicitDestructible();
1953 return QV4::QObjectWrapper::wrap(scope.engine, object);
1954 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1955 // XXX Can this be made more by using Array as a prototype and implementing
1956 // directly against QList<QObject*>?
1957 QList<QObject *> &list = *qlistPtr;
1958 QV4::ScopedArrayObject array(scope, scope.engine->newArrayObject());
1959 array->arrayReserve(list.count());
1960 QV4::ScopedValue v(scope);
1961 for (int ii = 0; ii < list.count(); ++ii)
1962 array->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(scope.engine, list.at(ii))));
1963 array->setArrayLengthUnchecked(list.count());
1964 return array.asReturnedValue();
1965 } else if (type == QMetaType::QJsonArray) {
1966 return QV4::JsonObject::fromJsonArray(scope.engine, *jsonArrayPtr);
1967 } else if (type == QMetaType::QJsonObject) {
1968 return QV4::JsonObject::fromJsonObject(scope.engine, *jsonObjectPtr);
1969 } else if (type == QMetaType::QJsonValue) {
1970 return QV4::JsonObject::fromJsonValue(scope.engine, *jsonValuePtr);
1971 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1972 QVariant value = *qvariantPtr;
1973 QV4::ScopedValue rv(scope, scope.engine->fromVariant(value));
1974 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, rv);
1975 if (!!qobjectWrapper) {
1976 if (QObject *object = qobjectWrapper->object())
1977 QQmlData::get(object, true)->setImplicitDestructible();
1978 }
1979 return rv->asReturnedValue();
1980 } else {
1981 return QV4::Encode::undefined();
1982 }
1983}
1984
1985ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
1986{
1987 Scope valueScope(scope);
1988 Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
1989 method->d()->setObject(object);
1990
1991 if (QQmlData *ddata = QQmlData::get(object))
1992 method->d()->setPropertyCache(ddata->propertyCache);
1993
1994 method->d()->index = index;
1995 return method.asReturnedValue();
1996}
1997
1998ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
1999{
2000 Scope valueScope(scope);
2001 Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
2002 method->d()->setPropertyCache(valueType->propertyCache());
2003 method->d()->index = index;
2004 method->d()->valueTypeWrapper.set(valueScope.engine, valueType);
2005 return method.asReturnedValue();
2006}
2007
2008void Heap::QObjectMethod::init(QV4::ExecutionContext *scope)
2009{
2010 Heap::FunctionObject::init(scope);
2011}
2012
2013const QMetaObject *Heap::QObjectMethod::metaObject()
2014{
2015 if (propertyCache())
2016 return propertyCache()->createMetaObject();
2017 return object()->metaObject();
2018}
2019
2020QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionEngine *engine) const
2021{
2022 QString result;
2023 if (const QMetaObject *metaObject = d()->metaObject()) {
2024
2025 result += QString::fromUtf8(metaObject->className()) +
2026 QLatin1String("(0x") + QString::number((quintptr)d()->object(),16);
2027
2028 if (d()->object()) {
2029 QString objectName = d()->object()->objectName();
2030 if (!objectName.isEmpty())
2031 result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
2032 }
2033
2034 result += QLatin1Char(')');
2035 } else {
2036 result = QLatin1String("null");
2037 }
2038
2039 return engine->newString(result)->asReturnedValue();
2040}
2041
2042QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, const Value *args, int argc) const
2043{
2044 if (!d()->object())
2045 return Encode::undefined();
2046 if (QQmlData::keepAliveDuringGarbageCollection(d()->object()))
2047 return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
2048
2049 int delay = 0;
2050 if (argc > 0)
2051 delay = args[0].toUInt32();
2052
2053 if (delay > 0)
2054 QTimer::singleShot(delay, d()->object(), SLOT(deleteLater()));
2055 else
2056 d()->object()->deleteLater();
2057
2058 return Encode::undefined();
2059}
2060
2061ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
2062{
2063 const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
2064 return This->callInternal(thisObject, argv, argc);
2065}
2066
2067ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const
2068{