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/qv4alloca_p.h>
18#include <private/qv4functionobject_p.h>
19#include <private/qv4object_p.h>
20#include <private/qv4qobjectwrapper_p.h>
21#include <private/qv4scopedvalue_p.h>
22
23QT_BEGIN_NAMESPACE
24
25namespace QV4 {
26
27template<typename Args>
28CallData *callDatafromJS(const Scope &scope, const Args *args, const FunctionObject *f = nullptr)
29{
30 int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + args->argc;
31 CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(nValues: size));
32 ptr->function = Encode::undefined();
33 ptr->context = Encode::undefined();
34 ptr->accumulator = Encode::undefined();
35 ptr->thisObject = args->thisObject ? args->thisObject->asReturnedValue() : Encode::undefined();
36 ptr->newTarget = Encode::undefined();
37 ptr->setArgc(args->argc);
38 if (args->argc)
39 memcpy(ptr->args, args->args, args->argc*sizeof(Value));
40 if (f)
41 ptr->function = f->asReturnedValue();
42 return ptr;
43}
44
45struct JSCallArguments
46{
47 JSCallArguments(const Scope &scope, int argc = 0)
48 : thisObject(scope.alloc()), args(scope.alloc(nValues: argc)), argc(argc)
49 {
50 }
51
52 CallData *callData(const Scope &scope, const FunctionObject *f = nullptr) const
53 {
54 return callDatafromJS(scope, args: this, f);
55 }
56
57 Value *thisObject;
58 Value *args;
59 const int argc;
60};
61
62struct JSCallData
63{
64 JSCallData(const Value *thisObject, const Value *argv, int argc)
65 : thisObject(thisObject), args(argv), argc(argc)
66 {
67 }
68
69 Q_IMPLICIT JSCallData(const JSCallArguments &args)
70 : thisObject(args.thisObject), args(args.args), argc(args.argc)
71 {
72 }
73
74 CallData *callData(const Scope &scope, const FunctionObject *f = nullptr) const
75 {
76 return callDatafromJS(scope, args: this, f);
77 }
78
79 const Value *thisObject;
80 const Value *args;
81 const int argc;
82};
83
84inline
85ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const
86{
87 return callAsConstructor(argv: data.args, argc: data.argc, newTarget: this);
88}
89
90inline
91ReturnedValue FunctionObject::call(const JSCallData &data) const
92{
93 return call(thisObject: data.thisObject, argv: data.args, argc: data.argc);
94}
95
96void populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, int argc,
97 void **args, const QMetaType *types);
98
99template<typename Callable>
100ReturnedValue convertAndCall(
101 ExecutionEngine *engine, const QQmlPrivate::AOTCompiledFunction *aotFunction,
102 const Value *thisObject, const Value *argv, int argc, Callable call)
103{
104 const qsizetype numFunctionArguments = aotFunction->argumentTypes.size();
105 Q_ALLOCA_VAR(void *, values, (numFunctionArguments + 1) * sizeof(void *));
106 Q_ALLOCA_VAR(QMetaType, types, (numFunctionArguments + 1) * sizeof(QMetaType));
107
108 for (qsizetype i = 0; i < numFunctionArguments; ++i) {
109 const QMetaType argumentType = aotFunction->argumentTypes[i];
110 types[i + 1] = argumentType;
111 if (const qsizetype argumentSize = argumentType.sizeOf()) {
112 Q_ALLOCA_VAR(void, argument, argumentSize);
113 argumentType.construct(where: argument);
114 if (i < argc)
115 ExecutionEngine::metaTypeFromJS(value: argv[i], type: argumentType, data: argument);
116 values[i + 1] = argument;
117 } else {
118 values[i + 1] = nullptr;
119 }
120 }
121
122 Q_ALLOCA_DECLARE(void, returnValue);
123 types[0] = aotFunction->returnType;
124 if (const qsizetype returnSize = types[0].sizeOf()) {
125 Q_ALLOCA_ASSIGN(void, returnValue, returnSize);
126 values[0] = returnValue;
127 } else {
128 values[0] = nullptr;
129 }
130
131 if (const QV4::QObjectWrapper *cppThisObject = thisObject->as<QV4::QObjectWrapper>())
132 call(cppThisObject->object(), values, types, argc);
133 else
134 call(nullptr, values, types, argc);
135
136 ReturnedValue result;
137 if (values[0]) {
138 result = engine->metaTypeToJS(type: types[0], data: values[0]);
139 types[0].destruct(data: values[0]);
140 } else {
141 result = Encode::undefined();
142 }
143
144 for (qsizetype i = 1, end = numFunctionArguments + 1; i < end; ++i)
145 types[i].destruct(data: values[i]);
146
147 return result;
148}
149
150template<typename Callable>
151bool convertAndCall(ExecutionEngine *engine, QObject *thisObject,
152 void **a, const QMetaType *types, int argc, Callable call)
153{
154 Scope scope(engine);
155 QV4::JSCallArguments jsCallData(scope, argc);
156
157 for (int ii = 0; ii < argc; ++ii)
158 jsCallData.args[ii] = engine->metaTypeToJS(type: types[ii + 1], data: a[ii + 1]);
159
160 ScopedObject jsThisObject(scope);
161 if (thisObject) {
162 // The result of wrap() can only be null, undefined, or an object.
163 jsThisObject = QV4::QObjectWrapper::wrap(engine, object: thisObject);
164 if (!jsThisObject)
165 jsThisObject = engine->globalObject;
166 } else {
167 jsThisObject = engine->globalObject;
168 }
169
170 ScopedValue jsResult(scope, call(jsThisObject, jsCallData.args, argc));
171 void *result = a[0];
172 if (!result)
173 return !jsResult->isUndefined();
174
175 const QMetaType resultType = types[0];
176 if (scope.hasException()) {
177 // Clear the return value
178 resultType.construct(where: result);
179 } else {
180 // When the return type is QVariant, JS objects are to be returned as
181 // QJSValue wrapped in QVariant. metaTypeFromJS unwraps them, unfortunately.
182 if (resultType == QMetaType::fromType<QVariant>()) {
183 new (result) QVariant(ExecutionEngine::toVariant(value: jsResult, typeHint: QMetaType {}));
184 } else {
185 resultType.construct(where: result);
186 ExecutionEngine::metaTypeFromJS(value: jsResult, type: resultType, data: result);
187 }
188 }
189 return !jsResult->isUndefined();
190}
191
192template<typename Callable>
193ReturnedValue coerceAndCall(
194 ExecutionEngine *engine, const QQmlPrivate::AOTCompiledFunction *typedFunction,
195 const Value *thisObject, const Value *argv, int argc, Callable call)
196{
197 Scope scope(engine);
198 QV4::JSCallArguments jsCallData(scope, argc);
199
200 const qsizetype numFunctionArguments = typedFunction->argumentTypes.size();
201 for (qsizetype i = 0; i < numFunctionArguments; ++i) {
202 const QMetaType argumentType = typedFunction->argumentTypes[i];
203 if (const qsizetype argumentSize = argumentType.sizeOf()) {
204 Q_ALLOCA_VAR(void, argument, argumentSize);
205 argumentType.construct(where: argument);
206 if (i < argc)
207 ExecutionEngine::metaTypeFromJS(value: argv[i], type: argumentType, data: argument);
208 jsCallData.args[i] = engine->metaTypeToJS(type: argumentType, data: argument);
209 } else {
210 jsCallData.args[i] = argv[i];
211 }
212 }
213
214 ScopedValue result(scope, call(thisObject, jsCallData.args, argc));
215 const QMetaType returnType = typedFunction->returnType;
216 if (const qsizetype returnSize = returnType.sizeOf()) {
217 Q_ALLOCA_VAR(void, returnValue, returnSize);
218 if (scope.hasException()) {
219 returnType.construct(where: returnValue);
220 } else if (returnType == QMetaType::fromType<QVariant>()) {
221 new (returnValue) QVariant(ExecutionEngine::toVariant(value: result, typeHint: QMetaType {}));
222 } else {
223 returnType.construct(where: returnValue);
224 ExecutionEngine::metaTypeFromJS(value: result, type: returnType, data: returnValue);
225 }
226 return engine->metaTypeToJS(type: returnType, data: returnValue);
227 }
228 return result->asReturnedValue();
229}
230
231} // namespace QV4
232
233QT_END_NAMESPACE
234
235#endif // QV4JSCALL_H
236

source code of qtdeclarative/src/qml/jsruntime/qv4jscall_p.h