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 | ScopedValue v(scope); |
292 | for (; i < length; ++i) { |
293 | v = array->get(idx: i); |
294 | listProperty->append(listProperty, coerceQObject(value: v, qmlType)); |
295 | } |
296 | |
297 | return newList->asReturnedValue(); |
298 | } |
299 | |
300 | QV4::Scoped<Sequence> sequence( |
301 | scope, SequencePrototype::fromData(engine, type, metaSequence: metaSequence(), data: nullptr)); |
302 | const qsizetype length = array->getLength(); |
303 | ScopedValue v(scope); |
304 | for (qsizetype i = 0; i < length; ++i) { |
305 | v = array->get(idx: i); |
306 | sequence->containerPutIndexed(index: i, value: v); |
307 | } |
308 | return sequence->asReturnedValue(); |
309 | } |
310 | |
311 | inline ReturnedValue coerce( |
312 | ExecutionEngine *engine, const Value &value, const QQmlType &qmlType, bool isList) |
313 | { |
314 | // These are all the named non-list, non-QObject builtins. Only those need special handling. |
315 | // Some of them may be wrapped in VariantObject because that is how they are stored in VME |
316 | // properties. |
317 | if (isList) |
318 | return coerceListType(engine, value, qmlType); |
319 | |
320 | const QMetaType metaType = qmlType.typeId(); |
321 | if (!metaType.isValid()) { |
322 | if (!value.isUndefined()) |
323 | warnAboutCoercionToVoid(engine, value, problem: InsufficientAnnotation); |
324 | return value.asReturnedValue(); |
325 | } |
326 | |
327 | switch (metaType.id()) { |
328 | case QMetaType::Void: |
329 | return Encode::undefined(); |
330 | case QMetaType::QVariant: |
331 | return value.asReturnedValue(); |
332 | case QMetaType::Int: |
333 | return Encode(value.toInt32()); |
334 | case QMetaType::Double: |
335 | return value.convertedToNumber(); |
336 | case QMetaType::QString: |
337 | return value.toString(e: engine)->asReturnedValue(); |
338 | case QMetaType::Bool: |
339 | return Encode(value.toBoolean()); |
340 | case QMetaType::QDateTime: |
341 | if (value.as<DateObject>()) |
342 | return value.asReturnedValue(); |
343 | if (const VariantObject *varObject = value.as<VariantObject>()) { |
344 | const QVariant &var = varObject->d()->data(); |
345 | switch (var.metaType().id()) { |
346 | case QMetaType::QDateTime: |
347 | return engine->newDateObject(dateTime: var.value<QDateTime>())->asReturnedValue(); |
348 | case QMetaType::QTime: |
349 | return engine->newDateObject(time: var.value<QTime>(), parent: nullptr, index: -1, flags: 0)->asReturnedValue(); |
350 | case QMetaType::QDate: |
351 | return engine->newDateObject(date: var.value<QDate>(), parent: nullptr, index: -1, flags: 0)->asReturnedValue(); |
352 | default: |
353 | break; |
354 | } |
355 | } |
356 | return engine->newDateObject(dateTime: QDateTime())->asReturnedValue(); |
357 | case QMetaType::QUrl: |
358 | if (value.as<UrlObject>()) |
359 | return value.asReturnedValue(); |
360 | if (const VariantObject *varObject = value.as<VariantObject>()) { |
361 | const QVariant &var = varObject->d()->data(); |
362 | return var.metaType() == QMetaType::fromType<QUrl>() |
363 | ? engine->newUrlObject(url: var.value<QUrl>())->asReturnedValue() |
364 | : engine->newUrlObject()->asReturnedValue(); |
365 | } |
366 | // Since URL properties are stored as string, we need to support the string conversion here. |
367 | if (const String *string = value.stringValue()) |
368 | return engine->newUrlObject(url: QUrl(string->toQString()))->asReturnedValue(); |
369 | return engine->newUrlObject()->asReturnedValue(); |
370 | #if QT_CONFIG(regularexpression) |
371 | case QMetaType::QRegularExpression: |
372 | if (value.as<RegExpObject>()) |
373 | return value.asReturnedValue(); |
374 | if (const VariantObject *varObject = value.as<VariantObject>()) { |
375 | const QVariant &var = varObject->d()->data(); |
376 | if (var.metaType() == QMetaType::fromType<QRegularExpression>()) |
377 | return engine->newRegExpObject(re: var.value<QRegularExpression>())->asReturnedValue(); |
378 | } |
379 | return engine->newRegExpObject(pattern: QString(), flags: 0)->asReturnedValue(); |
380 | #endif |
381 | default: |
382 | break; |
383 | } |
384 | |
385 | if (metaType.flags() & QMetaType::PointerToQObject) { |
386 | return coerceQObject(value, qmlType) |
387 | ? value.asReturnedValue() |
388 | : Encode::null(); |
389 | } |
390 | |
391 | if (const QQmlValueTypeWrapper *wrapper = value.as<QQmlValueTypeWrapper>()) { |
392 | if (wrapper->type() == metaType) |
393 | return value.asReturnedValue(); |
394 | } |
395 | |
396 | if (void *target = QQmlValueTypeProvider::heapCreateValueType(targetType: qmlType, source: value, engine)) { |
397 | Heap::QQmlValueTypeWrapper *wrapper = engine->memoryManager->allocate<QQmlValueTypeWrapper>( |
398 | args: nullptr, args: metaType, args: qmlType.metaObjectForValueType(), |
399 | args: nullptr, args: -1, args: Heap::ReferenceObject::NoFlag); |
400 | Q_ASSERT(!wrapper->gadgetPtr()); |
401 | wrapper->setGadgetPtr(target); |
402 | return wrapper->asReturnedValue(); |
403 | } |
404 | |
405 | return Encode::undefined(); |
406 | } |
407 | |
408 | template<typename Callable> |
409 | ReturnedValue coerceAndCall( |
410 | ExecutionEngine *engine, |
411 | const Function::JSTypedFunction *typedFunction, const CompiledData::Function *compiledFunction, |
412 | const Value *argv, int argc, Callable call) |
413 | { |
414 | Scope scope(engine); |
415 | |
416 | QV4::JSCallArguments jsCallData(scope, typedFunction->types.size() - 1); |
417 | const CompiledData::Parameter *formals = compiledFunction->formalsTable(); |
418 | for (qsizetype i = 0; i < jsCallData.argc; ++i) { |
419 | jsCallData.args[i] = coerce( |
420 | engine, value: i < argc ? argv[i] : QV4::Value::fromReturnedValue(val: Encode::undefined()), |
421 | qmlType: typedFunction->types[i + 1], isList: formals[i].type.isList()); |
422 | } |
423 | |
424 | ScopedValue result(scope, call(jsCallData.args, jsCallData.argc)); |
425 | return coerce(engine, value: result, qmlType: typedFunction->types[0], isList: compiledFunction->returnType.isList()); |
426 | } |
427 | |
428 | // Note: \a to is unininitialized here! This is in contrast to most other related functions. |
429 | inline void coerce( |
430 | ExecutionEngine *engine, QMetaType fromType, const void *from, QMetaType toType, void *to) |
431 | { |
432 | if ((fromType.flags() & QMetaType::PointerToQObject) |
433 | && (toType.flags() & QMetaType::PointerToQObject)) { |
434 | QObject *fromObj = *static_cast<QObject * const*>(from); |
435 | *static_cast<QObject **>(to) |
436 | = (fromObj && fromObj->metaObject()->inherits(metaObject: toType.metaObject())) |
437 | ? fromObj |
438 | : nullptr; |
439 | return; |
440 | } |
441 | |
442 | if (toType == QMetaType::fromType<QVariant>()) { |
443 | new (to) QVariant(fromType, from); |
444 | return; |
445 | } |
446 | |
447 | if (toType == QMetaType::fromType<QJSPrimitiveValue>()) { |
448 | new (to) QJSPrimitiveValue(fromType, from); |
449 | return; |
450 | } |
451 | |
452 | if (fromType == QMetaType::fromType<QVariant>()) { |
453 | const QVariant *fromVariant = static_cast<const QVariant *>(from); |
454 | if (fromVariant->metaType() == toType) |
455 | toType.construct(where: to, copy: fromVariant->data()); |
456 | else |
457 | coerce(engine, fromType: fromVariant->metaType(), from: fromVariant->data(), toType, to); |
458 | return; |
459 | } |
460 | |
461 | if (fromType == QMetaType::fromType<QJSPrimitiveValue>()) { |
462 | const QJSPrimitiveValue *fromPrimitive = static_cast<const QJSPrimitiveValue *>(from); |
463 | if (fromPrimitive->metaType() == toType) |
464 | toType.construct(where: to, copy: fromPrimitive->data()); |
465 | else |
466 | coerce(engine, fromType: fromPrimitive->metaType(), from: fromPrimitive->data(), toType, to); |
467 | return; |
468 | } |
469 | |
470 | // TODO: This is expensive. We might establish a direct C++-to-C++ type coercion, like we have |
471 | // for JS-to-JS. However, we shouldn't need this very often. Most of the time the compiler |
472 | // will generate code that passes the right arguments. |
473 | if (toType.flags() & QMetaType::NeedsConstruction) |
474 | toType.construct(where: to); |
475 | QV4::Scope scope(engine); |
476 | QV4::ScopedValue value(scope, engine->fromData(type: fromType, ptr: from)); |
477 | if (!ExecutionEngine::metaTypeFromJS(value, type: toType, data: to)) |
478 | QMetaType::convert(fromType, from, toType, to); |
479 | } |
480 | |
481 | template<typename TypedFunction, typename Callable> |
482 | void coerceAndCall( |
483 | ExecutionEngine *engine, const TypedFunction *typedFunction, |
484 | void **argv, const QMetaType *types, int argc, Callable call) |
485 | { |
486 | const qsizetype numFunctionArguments = typedFunction->parameterCount(); |
487 | |
488 | Q_ALLOCA_DECLARE(void *, transformedArguments); |
489 | Q_ALLOCA_DECLARE(void, transformedResult); |
490 | |
491 | const QMetaType returnType = typedFunction->returnMetaType(); |
492 | const QMetaType frameReturn = types[0]; |
493 | bool returnsQVariantWrapper = false; |
494 | if (argv[0] && returnType != frameReturn) { |
495 | Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); |
496 | memcpy(dest: transformedArguments, src: argv, n: (argc + 1) * sizeof(void *)); |
497 | |
498 | if (frameReturn == QMetaType::fromType<QVariant>()) { |
499 | QVariant *returnValue = static_cast<QVariant *>(argv[0]); |
500 | *returnValue = QVariant(returnType); |
501 | transformedResult = transformedArguments[0] = returnValue->data(); |
502 | returnsQVariantWrapper = true; |
503 | } else if (returnType.sizeOf() > 0) { |
504 | Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf()); |
505 | transformedArguments[0] = transformedResult; |
506 | if (returnType.flags() & QMetaType::NeedsConstruction) |
507 | returnType.construct(where: transformedResult); |
508 | } else { |
509 | transformedResult = transformedArguments[0] = &argc; // Some non-null marker value |
510 | } |
511 | } |
512 | |
513 | for (qsizetype i = 0; i < numFunctionArguments; ++i) { |
514 | const bool isValid = argc > i; |
515 | const QMetaType frameType = isValid ? types[i + 1] : QMetaType(); |
516 | |
517 | const QMetaType argumentType = typedFunction->parameterMetaType(i); |
518 | if (isValid && argumentType == frameType) |
519 | continue; |
520 | |
521 | if (transformedArguments == nullptr) { |
522 | Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); |
523 | memcpy(dest: transformedArguments, src: argv, n: (argc + 1) * sizeof(void *)); |
524 | } |
525 | |
526 | if (argumentType.sizeOf() == 0) { |
527 | transformedArguments[i + 1] = nullptr; |
528 | continue; |
529 | } |
530 | |
531 | void *frameVal = isValid ? argv[i + 1] : nullptr; |
532 | if (isValid && frameType == QMetaType::fromType<QVariant>()) { |
533 | QVariant *variant = static_cast<QVariant *>(frameVal); |
534 | |
535 | const QMetaType variantType = variant->metaType(); |
536 | if (variantType == argumentType) { |
537 | // Slightly nasty, but we're allowed to do this. |
538 | // We don't want to destruct() the QVariant's data() below. |
539 | transformedArguments[i + 1] = argv[i + 1] = variant->data(); |
540 | } else { |
541 | Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); |
542 | coerce(engine, fromType: variantType, from: variant->constData(), toType: argumentType, to: arg); |
543 | transformedArguments[i + 1] = arg; |
544 | } |
545 | continue; |
546 | } |
547 | |
548 | Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); |
549 | |
550 | if (isValid) |
551 | coerce(engine, fromType: frameType, from: frameVal, toType: argumentType, to: arg); |
552 | else |
553 | argumentType.construct(where: arg); |
554 | |
555 | transformedArguments[i + 1] = arg; |
556 | } |
557 | |
558 | if (!transformedArguments) { |
559 | call(argv, numFunctionArguments); |
560 | return; |
561 | } |
562 | |
563 | call(transformedArguments, numFunctionArguments); |
564 | |
565 | if (transformedResult && !returnsQVariantWrapper) { |
566 | if (frameReturn.sizeOf() > 0) { |
567 | if (frameReturn.flags() & QMetaType::NeedsDestruction) |
568 | frameReturn.destruct(data: argv[0]); |
569 | coerce(engine, fromType: returnType, from: transformedResult, toType: frameReturn, to: argv[0]); |
570 | } |
571 | if (returnType.flags() & QMetaType::NeedsDestruction) |
572 | returnType.destruct(data: transformedResult); |
573 | } |
574 | |
575 | for (qsizetype i = 0; i < numFunctionArguments; ++i) { |
576 | void *arg = transformedArguments[i + 1]; |
577 | if (arg == nullptr) |
578 | continue; |
579 | if (i >= argc || arg != argv[i + 1]) { |
580 | const QMetaType argumentType = typedFunction->parameterMetaType(i); |
581 | if (argumentType.flags() & QMetaType::NeedsDestruction) |
582 | argumentType.destruct(data: arg); |
583 | } |
584 | } |
585 | } |
586 | |
587 | } // namespace QV4 |
588 | |
589 | QT_END_NAMESPACE |
590 | |
591 | #endif // QV4JSCALL_H |
592 | |