1 | // Copyright (C) 2017 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | #ifndef QV4JSCALL_H |
4 | #define QV4JSCALL_H |
5 | |
6 | // |
7 | // W A R N I N G |
8 | // ------------- |
9 | // |
10 | // This file is not part of the Qt API. It exists purely as an |
11 | // implementation detail. This header file may change from version to |
12 | // version without notice, or even be removed. |
13 | // |
14 | // We mean it. |
15 | // |
16 | |
17 | #include <private/qqmllistwrapper_p.h> |
18 | #include <private/qqmlvaluetypewrapper_p.h> |
19 | |
20 | #include <private/qv4alloca_p.h> |
21 | #include <private/qv4dateobject_p.h> |
22 | #include <private/qv4function_p.h> |
23 | #include <private/qv4functionobject_p.h> |
24 | #include <private/qv4qobjectwrapper_p.h> |
25 | #include <private/qv4regexpobject_p.h> |
26 | #include <private/qv4scopedvalue_p.h> |
27 | #include <private/qv4sequenceobject_p.h> |
28 | #include <private/qv4urlobject_p.h> |
29 | #include <private/qv4variantobject_p.h> |
30 | |
31 | #if QT_CONFIG(regularexpression) |
32 | #include <QtCore/qregularexpression.h> |
33 | #endif |
34 | |
35 | QT_BEGIN_NAMESPACE |
36 | |
37 | namespace QV4 { |
38 | |
39 | template<typename Args> |
40 | CallData *callDatafromJS(const Scope &scope, const Args *args, const FunctionObject *f = nullptr) |
41 | { |
42 | int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + args->argc; |
43 | CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(nValues: size)); |
44 | ptr->function = Encode::undefined(); |
45 | ptr->context = Encode::undefined(); |
46 | ptr->accumulator = Encode::undefined(); |
47 | ptr->thisObject = args->thisObject ? args->thisObject->asReturnedValue() : Encode::undefined(); |
48 | ptr->newTarget = Encode::undefined(); |
49 | ptr->setArgc(args->argc); |
50 | if (args->argc) |
51 | memcpy(ptr->args, args->args, args->argc*sizeof(Value)); |
52 | if (f) |
53 | ptr->function = f->asReturnedValue(); |
54 | return ptr; |
55 | } |
56 | |
57 | struct JSCallArguments |
58 | { |
59 | JSCallArguments(const Scope &scope, int argc = 0) |
60 | : thisObject(scope.alloc()), args(scope.alloc(nValues: argc)), argc(argc) |
61 | { |
62 | } |
63 | |
64 | CallData *callData(const Scope &scope, const FunctionObject *f = nullptr) const |
65 | { |
66 | return callDatafromJS(scope, args: this, f); |
67 | } |
68 | |
69 | Value *thisObject; |
70 | Value *args; |
71 | const int argc; |
72 | }; |
73 | |
74 | struct JSCallData |
75 | { |
76 | JSCallData(const Value *thisObject, const Value *argv, int argc) |
77 | : thisObject(thisObject), args(argv), argc(argc) |
78 | { |
79 | } |
80 | |
81 | Q_IMPLICIT JSCallData(const JSCallArguments &args) |
82 | : thisObject(args.thisObject), args(args.args), argc(args.argc) |
83 | { |
84 | } |
85 | |
86 | CallData *callData(const Scope &scope, const FunctionObject *f = nullptr) const |
87 | { |
88 | return callDatafromJS(scope, args: this, f); |
89 | } |
90 | |
91 | const Value *thisObject; |
92 | const Value *args; |
93 | const int argc; |
94 | }; |
95 | |
96 | inline |
97 | ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const |
98 | { |
99 | return callAsConstructor(argv: data.args, argc: data.argc, newTarget: this); |
100 | } |
101 | |
102 | inline |
103 | ReturnedValue FunctionObject::call(const JSCallData &data) const |
104 | { |
105 | return call(thisObject: data.thisObject, argv: data.args, argc: data.argc); |
106 | } |
107 | |
108 | void populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, int argc, |
109 | void **args, const QMetaType *types); |
110 | |
111 | template<typename Callable> |
112 | ReturnedValue convertAndCall( |
113 | ExecutionEngine *engine, const Function::AOTCompiledFunction *aotFunction, |
114 | const Value *thisObject, const Value *argv, int argc, Callable call) |
115 | { |
116 | const qsizetype numFunctionArguments = aotFunction->types.length() - 1; |
117 | Q_ALLOCA_VAR(void *, values, (numFunctionArguments + 1) * sizeof(void *)); |
118 | Q_ALLOCA_VAR(QMetaType, types, (numFunctionArguments + 1) * sizeof(QMetaType)); |
119 | |
120 | for (qsizetype i = 0; i < numFunctionArguments; ++i) { |
121 | const QMetaType argumentType = aotFunction->types[i + 1]; |
122 | types[i + 1] = argumentType; |
123 | if (const qsizetype argumentSize = argumentType.sizeOf()) { |
124 | Q_ALLOCA_VAR(void, argument, argumentSize); |
125 | if (argumentType.flags() & QMetaType::NeedsConstruction) { |
126 | argumentType.construct(where: argument); |
127 | if (i < argc) |
128 | ExecutionEngine::metaTypeFromJS(value: argv[i], type: argumentType, data: argument); |
129 | } else if (i >= argc |
130 | || !ExecutionEngine::metaTypeFromJS(value: argv[i], type: argumentType, data: argument)) { |
131 | // If we can't convert the argument, we need to default-construct it even if it |
132 | // doesn't formally need construction. |
133 | // E.g. an int doesn't need construction, but we still want it to be 0. |
134 | argumentType.construct(where: argument); |
135 | } |
136 | |
137 | values[i + 1] = argument; |
138 | } else { |
139 | values[i + 1] = nullptr; |
140 | } |
141 | } |
142 | |
143 | Q_ALLOCA_DECLARE(void, returnValue); |
144 | types[0] = aotFunction->types[0]; |
145 | if (const qsizetype returnSize = types[0].sizeOf()) { |
146 | Q_ALLOCA_ASSIGN(void, returnValue, returnSize); |
147 | values[0] = returnValue; |
148 | if (types[0].flags() & QMetaType::NeedsConstruction) |
149 | types[0].construct(where: returnValue); |
150 | } else { |
151 | values[0] = nullptr; |
152 | } |
153 | |
154 | if (const QV4::QObjectWrapper *cppThisObject = thisObject |
155 | ? thisObject->as<QV4::QObjectWrapper>() |
156 | : nullptr) { |
157 | call(cppThisObject->object(), values, types, argc); |
158 | } else { |
159 | call(nullptr, values, types, argc); |
160 | } |
161 | |
162 | ReturnedValue result; |
163 | if (values[0]) { |
164 | result = engine->metaTypeToJS(type: types[0], data: values[0]); |
165 | if (types[0].flags() & QMetaType::NeedsDestruction) |
166 | types[0].destruct(data: values[0]); |
167 | } else { |
168 | result = Encode::undefined(); |
169 | } |
170 | |
171 | for (qsizetype i = 1, end = numFunctionArguments + 1; i < end; ++i) { |
172 | if (types[i].flags() & QMetaType::NeedsDestruction) |
173 | types[i].destruct(data: values[i]); |
174 | } |
175 | |
176 | return result; |
177 | } |
178 | |
179 | template<typename Callable> |
180 | bool convertAndCall(ExecutionEngine *engine, QObject *thisObject, |
181 | void **a, const QMetaType *types, int argc, Callable call) |
182 | { |
183 | Scope scope(engine); |
184 | QV4::JSCallArguments jsCallData(scope, argc); |
185 | |
186 | for (int ii = 0; ii < argc; ++ii) |
187 | jsCallData.args[ii] = engine->metaTypeToJS(type: types[ii + 1], data: a[ii + 1]); |
188 | |
189 | ScopedObject jsThisObject(scope); |
190 | if (thisObject) { |
191 | // The result of wrap() can only be null, undefined, or an object. |
192 | jsThisObject = QV4::QObjectWrapper::wrap(engine, object: thisObject); |
193 | if (!jsThisObject) |
194 | jsThisObject = engine->globalObject; |
195 | } else { |
196 | jsThisObject = engine->globalObject; |
197 | } |
198 | |
199 | ScopedValue jsResult(scope, call(jsThisObject, jsCallData.args, argc)); |
200 | void *result = a[0]; |
201 | if (!result) |
202 | return !jsResult->isUndefined(); |
203 | |
204 | const QMetaType resultType = types[0]; |
205 | if (scope.hasException()) { |
206 | // Clear the return value |
207 | resultType.destruct(data: result); |
208 | resultType.construct(where: result); |
209 | } else if (resultType == QMetaType::fromType<QVariant>()) { |
210 | // When the return type is QVariant, JS objects are to be returned as |
211 | // QJSValue wrapped in QVariant. metaTypeFromJS unwraps them, unfortunately. |
212 | *static_cast<QVariant *>(result) = ExecutionEngine::toVariant(value: jsResult, typeHint: QMetaType {}); |
213 | } else if (!ExecutionEngine::metaTypeFromJS(value: jsResult, type: resultType, data: result)) { |
214 | // If we cannot convert, also clear the return value. |
215 | // The caller may have given us an uninitialized QObject*, expecting it to be overwritten. |
216 | resultType.destruct(data: result); |
217 | resultType.construct(where: result); |
218 | } |
219 | return !jsResult->isUndefined(); |
220 | } |
221 | |
222 | inline ReturnedValue coerce( |
223 | ExecutionEngine *engine, const Value &value, const QQmlType &qmlType, bool isList); |
224 | |
225 | inline QObject *coerceQObject(const Value &value, const QQmlType &qmlType) |
226 | { |
227 | QObject *o; |
228 | if (const QV4::QObjectWrapper *wrapper = value.as<QV4::QObjectWrapper>()) |
229 | o = wrapper->object(); |
230 | else if (const QV4::QQmlTypeWrapper *wrapper = value.as<QQmlTypeWrapper>()) |
231 | o = wrapper->object(); |
232 | else |
233 | return nullptr; |
234 | |
235 | return (o && qmlobject_can_qml_cast(object: o, type: qmlType)) ? o : nullptr; |
236 | } |
237 | |
238 | enum CoercionProblem |
239 | { |
240 | InsufficientAnnotation, |
241 | InvalidListType |
242 | }; |
243 | |
244 | Q_QML_EXPORT void warnAboutCoercionToVoid( |
245 | ExecutionEngine *engine, const Value &value, CoercionProblem problem); |
246 | |
247 | inline ReturnedValue coerceListType( |
248 | ExecutionEngine *engine, const Value &value, const QQmlType &qmlType) |
249 | { |
250 | QMetaType type = qmlType.qListTypeId(); |
251 | const auto metaSequence = [&]() { |
252 | // TODO: We should really add the metasequence to the same QQmlType that holds |
253 | // all the other type information. Then we can get rid of the extra |
254 | // QQmlMetaType::qmlListType() here. |
255 | return qmlType.isSequentialContainer() |
256 | ? qmlType.listMetaSequence() |
257 | : QQmlMetaType::qmlListType(metaType: type).listMetaSequence(); |
258 | }; |
259 | |
260 | if (const QV4::Sequence *sequence = value.as<QV4::Sequence>()) { |
261 | if (sequence->d()->listType() == type) |
262 | return value.asReturnedValue(); |
263 | } |
264 | |
265 | if (const QmlListWrapper *list = value.as<QmlListWrapper>()) { |
266 | if (list->d()->propertyType() == type) |
267 | return value.asReturnedValue(); |
268 | } |
269 | |
270 | QMetaType listValueType = qmlType.typeId(); |
271 | if (!listValueType.isValid()) { |
272 | warnAboutCoercionToVoid(engine, value, problem: InvalidListType); |
273 | return value.asReturnedValue(); |
274 | } |
275 | |
276 | QV4::Scope scope(engine); |
277 | |
278 | const ArrayObject *array = value.as<ArrayObject>(); |
279 | if (!array) { |
280 | return (listValueType.flags() & QMetaType::PointerToQObject) |
281 | ? QmlListWrapper::create(engine, propType: listValueType) |
282 | : SequencePrototype::fromData(engine, type, metaSequence: metaSequence(), data: nullptr); |
283 | } |
284 | |
285 | if (listValueType.flags() & QMetaType::PointerToQObject) { |
286 | QV4::Scoped<QmlListWrapper> newList(scope, QmlListWrapper::create(engine, propType: type)); |
287 | QQmlListProperty<QObject> *listProperty = newList->d()->property(); |
288 | |
289 | const qsizetype length = array->getLength(); |
290 | qsizetype i = 0; |
291 | for (; i < length; ++i) { |
292 | ScopedValue v(scope, array->get(idx: i)); |
293 | listProperty->append(listProperty, coerceQObject(value: v, qmlType)); |
294 | } |
295 | |
296 | return newList->asReturnedValue(); |
297 | } |
298 | |
299 | QV4::Scoped<Sequence> sequence( |
300 | scope, SequencePrototype::fromData(engine, type, metaSequence: metaSequence(), data: nullptr)); |
301 | const qsizetype length = array->getLength(); |
302 | for (qsizetype i = 0; i < length; ++i) |
303 | sequence->containerPutIndexed(index: i, value: array->get(idx: i)); |
304 | return sequence->asReturnedValue(); |
305 | } |
306 | |
307 | inline ReturnedValue coerce( |
308 | ExecutionEngine *engine, const Value &value, const QQmlType &qmlType, bool isList) |
309 | { |
310 | // These are all the named non-list, non-QObject builtins. Only those need special handling. |
311 | // Some of them may be wrapped in VariantObject because that is how they are stored in VME |
312 | // properties. |
313 | if (isList) |
314 | return coerceListType(engine, value, qmlType); |
315 | |
316 | const QMetaType metaType = qmlType.typeId(); |
317 | if (!metaType.isValid()) { |
318 | if (!value.isUndefined()) |
319 | warnAboutCoercionToVoid(engine, value, problem: InsufficientAnnotation); |
320 | return value.asReturnedValue(); |
321 | } |
322 | |
323 | switch (metaType.id()) { |
324 | case QMetaType::Void: |
325 | return Encode::undefined(); |
326 | case QMetaType::QVariant: |
327 | return value.asReturnedValue(); |
328 | case QMetaType::Int: |
329 | return Encode(value.toInt32()); |
330 | case QMetaType::Double: |
331 | return value.convertedToNumber(); |
332 | case QMetaType::QString: |
333 | return value.toString(e: engine)->asReturnedValue(); |
334 | case QMetaType::Bool: |
335 | return Encode(value.toBoolean()); |
336 | case QMetaType::QDateTime: |
337 | if (value.as<DateObject>()) |
338 | return value.asReturnedValue(); |
339 | if (const VariantObject *varObject = value.as<VariantObject>()) { |
340 | const QVariant &var = varObject->d()->data(); |
341 | switch (var.metaType().id()) { |
342 | case QMetaType::QDateTime: |
343 | return engine->newDateObject(dateTime: var.value<QDateTime>())->asReturnedValue(); |
344 | case QMetaType::QTime: |
345 | return engine->newDateObject(time: var.value<QTime>(), parent: nullptr, index: -1, flags: 0)->asReturnedValue(); |
346 | case QMetaType::QDate: |
347 | return engine->newDateObject(date: var.value<QDate>(), parent: nullptr, index: -1, flags: 0)->asReturnedValue(); |
348 | default: |
349 | break; |
350 | } |
351 | } |
352 | return engine->newDateObject(dateTime: QDateTime())->asReturnedValue(); |
353 | case QMetaType::QUrl: |
354 | if (value.as<UrlObject>()) |
355 | return value.asReturnedValue(); |
356 | if (const VariantObject *varObject = value.as<VariantObject>()) { |
357 | const QVariant &var = varObject->d()->data(); |
358 | return var.metaType() == QMetaType::fromType<QUrl>() |
359 | ? engine->newUrlObject(url: var.value<QUrl>())->asReturnedValue() |
360 | : engine->newUrlObject()->asReturnedValue(); |
361 | } |
362 | // Since URL properties are stored as string, we need to support the string conversion here. |
363 | if (const String *string = value.stringValue()) |
364 | return engine->newUrlObject(url: QUrl(string->toQString()))->asReturnedValue(); |
365 | return engine->newUrlObject()->asReturnedValue(); |
366 | #if QT_CONFIG(regularexpression) |
367 | case QMetaType::QRegularExpression: |
368 | if (value.as<RegExpObject>()) |
369 | return value.asReturnedValue(); |
370 | if (const VariantObject *varObject = value.as<VariantObject>()) { |
371 | const QVariant &var = varObject->d()->data(); |
372 | if (var.metaType() == QMetaType::fromType<QRegularExpression>()) |
373 | return engine->newRegExpObject(re: var.value<QRegularExpression>())->asReturnedValue(); |
374 | } |
375 | return engine->newRegExpObject(pattern: QString(), flags: 0)->asReturnedValue(); |
376 | #endif |
377 | default: |
378 | break; |
379 | } |
380 | |
381 | if (metaType.flags() & QMetaType::PointerToQObject) { |
382 | return coerceQObject(value, qmlType) |
383 | ? value.asReturnedValue() |
384 | : Encode::null(); |
385 | } |
386 | |
387 | if (const QQmlValueTypeWrapper *wrapper = value.as<QQmlValueTypeWrapper>()) { |
388 | if (wrapper->type() == metaType) |
389 | return value.asReturnedValue(); |
390 | } |
391 | |
392 | if (void *target = QQmlValueTypeProvider::heapCreateValueType(targetType: qmlType, source: value, engine)) { |
393 | Heap::QQmlValueTypeWrapper *wrapper = engine->memoryManager->allocate<QQmlValueTypeWrapper>( |
394 | args: nullptr, args: metaType, args: qmlType.metaObjectForValueType(), |
395 | args: nullptr, args: -1, args: Heap::ReferenceObject::NoFlag); |
396 | Q_ASSERT(!wrapper->gadgetPtr()); |
397 | wrapper->setGadgetPtr(target); |
398 | return wrapper->asReturnedValue(); |
399 | } |
400 | |
401 | return Encode::undefined(); |
402 | } |
403 | |
404 | template<typename Callable> |
405 | ReturnedValue coerceAndCall( |
406 | ExecutionEngine *engine, |
407 | const Function::JSTypedFunction *typedFunction, const CompiledData::Function *compiledFunction, |
408 | const Value *argv, int argc, Callable call) |
409 | { |
410 | Scope scope(engine); |
411 | |
412 | QV4::JSCallArguments jsCallData(scope, typedFunction->types.size() - 1); |
413 | const CompiledData::Parameter *formals = compiledFunction->formalsTable(); |
414 | for (qsizetype i = 0; i < jsCallData.argc; ++i) { |
415 | jsCallData.args[i] = coerce( |
416 | engine, value: i < argc ? argv[i] : Encode::undefined(), |
417 | qmlType: typedFunction->types[i + 1], isList: formals[i].type.isList()); |
418 | } |
419 | |
420 | ScopedValue result(scope, call(jsCallData.args, jsCallData.argc)); |
421 | return coerce(engine, value: result, qmlType: typedFunction->types[0], isList: compiledFunction->returnType.isList()); |
422 | } |
423 | |
424 | // Note: \a to is unininitialized here! This is in contrast to most other related functions. |
425 | inline void coerce( |
426 | ExecutionEngine *engine, QMetaType fromType, const void *from, QMetaType toType, void *to) |
427 | { |
428 | if ((fromType.flags() & QMetaType::PointerToQObject) |
429 | && (toType.flags() & QMetaType::PointerToQObject)) { |
430 | QObject *fromObj = *static_cast<QObject * const*>(from); |
431 | *static_cast<QObject **>(to) |
432 | = (fromObj && fromObj->metaObject()->inherits(metaObject: toType.metaObject())) |
433 | ? fromObj |
434 | : nullptr; |
435 | return; |
436 | } |
437 | |
438 | if (toType == QMetaType::fromType<QVariant>()) { |
439 | new (to) QVariant(fromType, from); |
440 | return; |
441 | } |
442 | |
443 | if (toType == QMetaType::fromType<QJSPrimitiveValue>()) { |
444 | new (to) QJSPrimitiveValue(fromType, from); |
445 | return; |
446 | } |
447 | |
448 | if (fromType == QMetaType::fromType<QVariant>()) { |
449 | const QVariant *fromVariant = static_cast<const QVariant *>(from); |
450 | if (fromVariant->metaType() == toType) |
451 | toType.construct(where: to, copy: fromVariant->data()); |
452 | else |
453 | coerce(engine, fromType: fromVariant->metaType(), from: fromVariant->data(), toType, to); |
454 | return; |
455 | } |
456 | |
457 | if (fromType == QMetaType::fromType<QJSPrimitiveValue>()) { |
458 | const QJSPrimitiveValue *fromPrimitive = static_cast<const QJSPrimitiveValue *>(from); |
459 | if (fromPrimitive->metaType() == toType) |
460 | toType.construct(where: to, copy: fromPrimitive->data()); |
461 | else |
462 | coerce(engine, fromType: fromPrimitive->metaType(), from: fromPrimitive->data(), toType, to); |
463 | return; |
464 | } |
465 | |
466 | // TODO: This is expensive. We might establish a direct C++-to-C++ type coercion, like we have |
467 | // for JS-to-JS. However, we shouldn't need this very often. Most of the time the compiler |
468 | // will generate code that passes the right arguments. |
469 | if (toType.flags() & QMetaType::NeedsConstruction) |
470 | toType.construct(where: to); |
471 | QV4::Scope scope(engine); |
472 | QV4::ScopedValue value(scope, engine->fromData(type: fromType, ptr: from)); |
473 | if (!ExecutionEngine::metaTypeFromJS(value, type: toType, data: to)) |
474 | QMetaType::convert(fromType, from, toType, to); |
475 | } |
476 | |
477 | template<typename TypedFunction, typename Callable> |
478 | void coerceAndCall( |
479 | ExecutionEngine *engine, const TypedFunction *typedFunction, |
480 | void **argv, const QMetaType *types, int argc, Callable call) |
481 | { |
482 | const qsizetype numFunctionArguments = typedFunction->parameterCount(); |
483 | |
484 | Q_ALLOCA_DECLARE(void *, transformedArguments); |
485 | Q_ALLOCA_DECLARE(void, transformedResult); |
486 | |
487 | const QMetaType returnType = typedFunction->returnMetaType(); |
488 | const QMetaType frameReturn = types[0]; |
489 | bool returnsQVariantWrapper = false; |
490 | if (argv[0] && returnType != frameReturn) { |
491 | Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); |
492 | memcpy(dest: transformedArguments, src: argv, n: (argc + 1) * sizeof(void *)); |
493 | |
494 | if (frameReturn == QMetaType::fromType<QVariant>()) { |
495 | QVariant *returnValue = static_cast<QVariant *>(argv[0]); |
496 | *returnValue = QVariant(returnType); |
497 | transformedResult = transformedArguments[0] = returnValue->data(); |
498 | returnsQVariantWrapper = true; |
499 | } else if (returnType.sizeOf() > 0) { |
500 | Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf()); |
501 | transformedArguments[0] = transformedResult; |
502 | if (returnType.flags() & QMetaType::NeedsConstruction) |
503 | returnType.construct(where: transformedResult); |
504 | } else { |
505 | transformedResult = transformedArguments[0] = &argc; // Some non-null marker value |
506 | } |
507 | } |
508 | |
509 | for (qsizetype i = 0; i < numFunctionArguments; ++i) { |
510 | const bool isValid = argc > i; |
511 | const QMetaType frameType = isValid ? types[i + 1] : QMetaType(); |
512 | |
513 | const QMetaType argumentType = typedFunction->parameterMetaType(i); |
514 | if (isValid && argumentType == frameType) |
515 | continue; |
516 | |
517 | if (transformedArguments == nullptr) { |
518 | Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); |
519 | memcpy(dest: transformedArguments, src: argv, n: (argc + 1) * sizeof(void *)); |
520 | } |
521 | |
522 | if (argumentType.sizeOf() == 0) { |
523 | transformedArguments[i + 1] = nullptr; |
524 | continue; |
525 | } |
526 | |
527 | void *frameVal = isValid ? argv[i + 1] : nullptr; |
528 | if (isValid && frameType == QMetaType::fromType<QVariant>()) { |
529 | QVariant *variant = static_cast<QVariant *>(frameVal); |
530 | |
531 | const QMetaType variantType = variant->metaType(); |
532 | if (variantType == argumentType) { |
533 | // Slightly nasty, but we're allowed to do this. |
534 | // We don't want to destruct() the QVariant's data() below. |
535 | transformedArguments[i + 1] = argv[i + 1] = variant->data(); |
536 | } else { |
537 | Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); |
538 | coerce(engine, fromType: variantType, from: variant->constData(), toType: argumentType, to: arg); |
539 | transformedArguments[i + 1] = arg; |
540 | } |
541 | continue; |
542 | } |
543 | |
544 | Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); |
545 | |
546 | if (isValid) |
547 | coerce(engine, fromType: frameType, from: frameVal, toType: argumentType, to: arg); |
548 | else |
549 | argumentType.construct(where: arg); |
550 | |
551 | transformedArguments[i + 1] = arg; |
552 | } |
553 | |
554 | if (!transformedArguments) { |
555 | call(argv, numFunctionArguments); |
556 | return; |
557 | } |
558 | |
559 | call(transformedArguments, numFunctionArguments); |
560 | |
561 | if (transformedResult && !returnsQVariantWrapper) { |
562 | if (frameReturn.sizeOf() > 0) { |
563 | if (frameReturn.flags() & QMetaType::NeedsDestruction) |
564 | frameReturn.destruct(data: argv[0]); |
565 | coerce(engine, fromType: returnType, from: transformedResult, toType: frameReturn, to: argv[0]); |
566 | } |
567 | if (returnType.flags() & QMetaType::NeedsDestruction) |
568 | returnType.destruct(data: transformedResult); |
569 | } |
570 | |
571 | for (qsizetype i = 0; i < numFunctionArguments; ++i) { |
572 | void *arg = transformedArguments[i + 1]; |
573 | if (arg == nullptr) |
574 | continue; |
575 | if (i >= argc || arg != argv[i + 1]) { |
576 | const QMetaType argumentType = typedFunction->parameterMetaType(i); |
577 | if (argumentType.flags() & QMetaType::NeedsDestruction) |
578 | argumentType.destruct(data: arg); |
579 | } |
580 | } |
581 | } |
582 | |
583 | } // namespace QV4 |
584 | |
585 | QT_END_NAMESPACE |
586 | |
587 | #endif // QV4JSCALL_H |
588 | |