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 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | namespace QV4 { |
26 | |
27 | template<typename Args> |
28 | CallData *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 | |
45 | struct 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 | |
62 | struct 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 | |
84 | inline |
85 | ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const |
86 | { |
87 | return callAsConstructor(argv: data.args, argc: data.argc, newTarget: this); |
88 | } |
89 | |
90 | inline |
91 | ReturnedValue FunctionObject::call(const JSCallData &data) const |
92 | { |
93 | return call(thisObject: data.thisObject, argv: data.args, argc: data.argc); |
94 | } |
95 | |
96 | void populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, int argc, |
97 | void **args, const QMetaType *types); |
98 | |
99 | template<typename Callable> |
100 | ReturnedValue 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 | |
150 | template<typename Callable> |
151 | bool 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 | |
192 | template<typename Callable> |
193 | ReturnedValue 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 | |
233 | QT_END_NAMESPACE |
234 | |
235 | #endif // QV4JSCALL_H |
236 | |