1 | // Copyright (C) 2016 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 QV4FUNCTIONOBJECT_H |
4 | #define QV4FUNCTIONOBJECT_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 "qv4object_p.h" |
18 | #include "qv4function_p.h" |
19 | #include "qv4context_p.h" |
20 | #include <private/qv4mm_p.h> |
21 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | struct QQmlSourceLocation; |
25 | |
26 | namespace QV4 { |
27 | |
28 | struct IndexedBuiltinFunction; |
29 | struct JSCallData; |
30 | |
31 | namespace Heap { |
32 | |
33 | |
34 | #define FunctionObjectMembers(class, Member) \ |
35 | Member(class, Pointer, ExecutionContext *, scope) \ |
36 | Member(class, NoMark, Function *, function) \ |
37 | Member(class, NoMark, VTable::Call, jsCall) \ |
38 | Member(class, NoMark, VTable::CallAsConstructor, jsConstruct) \ |
39 | Member(class, NoMark, VTable::CallWithMetaTypes, jsCallWithMetaTypes) \ |
40 | Member(class, NoMark, bool, canBeTailCalled) |
41 | |
42 | DECLARE_HEAP_OBJECT(FunctionObject, Object) { |
43 | DECLARE_MARKOBJECTS(FunctionObject) |
44 | enum { |
45 | Index_ProtoConstructor = 0, |
46 | Index_Prototype = 0, |
47 | Index_HasInstance = 1, |
48 | }; |
49 | |
50 | bool isConstructor() const { |
51 | return jsConstruct != nullptr; |
52 | } |
53 | |
54 | Q_QML_PRIVATE_EXPORT void init( |
55 | QV4::ExecutionContext *scope, QV4::String *name, |
56 | VTable::Call call, VTable::CallWithMetaTypes callWithMetaTypes = nullptr); |
57 | Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); |
58 | Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); |
59 | Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, const QString &name); |
60 | Q_QML_PRIVATE_EXPORT void init(); |
61 | Q_QML_PRIVATE_EXPORT void destroy(); |
62 | |
63 | void setFunction(Function *f); |
64 | |
65 | unsigned int formalParameterCount() { return function ? function->nFormals : 0; } |
66 | unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; } |
67 | }; |
68 | |
69 | struct FunctionCtor : FunctionObject { |
70 | void init(QV4::ExecutionContext *scope); |
71 | }; |
72 | |
73 | struct FunctionPrototype : FunctionObject { |
74 | void init(); |
75 | }; |
76 | |
77 | // A function object with an additional index into a list. |
78 | // Used by Models to refer to property roles. |
79 | struct IndexedBuiltinFunction : FunctionObject { |
80 | inline void init(QV4::ExecutionContext *scope, qsizetype index, VTable::Call call); |
81 | qsizetype index; |
82 | }; |
83 | |
84 | struct ArrowFunction : FunctionObject { |
85 | enum { |
86 | Index_Name = Index_HasInstance + 1, |
87 | Index_Length |
88 | }; |
89 | void init(QV4::ExecutionContext *scope, Function *function, QV4::String *name = nullptr); |
90 | }; |
91 | |
92 | #define ScriptFunctionMembers(class, Member) \ |
93 | Member(class, Pointer, InternalClass *, cachedClassForConstructor) |
94 | |
95 | DECLARE_HEAP_OBJECT(ScriptFunction, ArrowFunction) { |
96 | DECLARE_MARKOBJECTS(ScriptFunction) |
97 | void init(QV4::ExecutionContext *scope, Function *function); |
98 | }; |
99 | |
100 | #define MemberFunctionMembers(class, Member) \ |
101 | Member(class, Pointer, Object *, homeObject) |
102 | |
103 | DECLARE_HEAP_OBJECT(MemberFunction, ArrowFunction) { |
104 | DECLARE_MARKOBJECTS(MemberFunction) |
105 | |
106 | void init(QV4::ExecutionContext *scope, Function *function, QV4::String *name = nullptr) { |
107 | ArrowFunction::init(scope, function, name); |
108 | } |
109 | }; |
110 | |
111 | #define ConstructorFunctionMembers(class, Member) \ |
112 | Member(class, Pointer, Object *, homeObject) |
113 | |
114 | DECLARE_HEAP_OBJECT(ConstructorFunction, ScriptFunction) { |
115 | DECLARE_MARKOBJECTS(ConstructorFunction) |
116 | bool isDerivedConstructor; |
117 | }; |
118 | |
119 | struct DefaultClassConstructorFunction : FunctionObject |
120 | { |
121 | bool isDerivedConstructor; |
122 | }; |
123 | |
124 | #define BoundFunctionMembers(class, Member) \ |
125 | Member(class, Pointer, FunctionObject *, target) \ |
126 | Member(class, HeapValue, HeapValue, boundThis) \ |
127 | Member(class, Pointer, MemberData *, boundArgs) |
128 | |
129 | DECLARE_HEAP_OBJECT(BoundFunction, FunctionObject) { |
130 | DECLARE_MARKOBJECTS(BoundFunction) |
131 | |
132 | void init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); |
133 | }; |
134 | |
135 | } |
136 | |
137 | struct Q_QML_EXPORT FunctionObject: Object { |
138 | enum { |
139 | IsFunctionObject = true |
140 | }; |
141 | V4_OBJECT2(FunctionObject, Object) |
142 | Q_MANAGED_TYPE(FunctionObject) |
143 | V4_INTERNALCLASS(FunctionObject) |
144 | V4_PROTOTYPE(functionPrototype) |
145 | V4_NEEDS_DESTROY |
146 | enum { NInlineProperties = 1 }; |
147 | |
148 | bool canBeTailCalled() const { return d()->canBeTailCalled; } |
149 | Heap::ExecutionContext *scope() const { return d()->scope; } |
150 | Function *function() const { return d()->function; } |
151 | |
152 | ReturnedValue name() const; |
153 | unsigned int formalParameterCount() const { return d()->formalParameterCount(); } |
154 | unsigned int varCount() const { return d()->varCount(); } |
155 | |
156 | void setName(String *name) { |
157 | defineReadonlyConfigurableProperty(name: engine()->id_name(), value: *name); |
158 | } |
159 | void createDefaultPrototypeProperty(uint protoConstructorSlot); |
160 | |
161 | inline ReturnedValue callAsConstructor(const JSCallData &data) const; |
162 | ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget = nullptr) const { |
163 | if (!d()->jsConstruct) |
164 | return engine()->throwTypeError(QStringLiteral("Function is not a constructor." )); |
165 | return d()->jsConstruct(this, argv, argc, newTarget ? newTarget : this); |
166 | } |
167 | inline ReturnedValue call(const JSCallData &data) const; |
168 | ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const { |
169 | if (!d()->jsCall) |
170 | return engine()->throwTypeError(QStringLiteral("Function can only be called with |new|." )); |
171 | return d()->jsCall(this, thisObject, argv, argc); |
172 | } |
173 | static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
174 | void call(QObject *thisObject, void **a, const QMetaType *types, int argc); |
175 | static void virtualCallWithMetaTypes(const FunctionObject *f, QObject *thisObject, |
176 | void **a, const QMetaType *types, int argc); |
177 | |
178 | static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); |
179 | static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor); |
180 | static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, String *name); |
181 | static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount); |
182 | |
183 | bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } |
184 | bool isBinding() const; |
185 | bool isBoundFunction() const; |
186 | bool isConstructor() const { |
187 | return d()->isConstructor(); |
188 | } |
189 | |
190 | ReturnedValue getHomeObject() const; |
191 | |
192 | ReturnedValue protoProperty() const { |
193 | return getValueByIndex(propertyIndex: Heap::FunctionObject::Index_Prototype); |
194 | } |
195 | bool hasHasInstanceProperty() const { |
196 | return !internalClass()->propertyData.at(i: Heap::FunctionObject::Index_HasInstance).isEmpty(); |
197 | } |
198 | |
199 | QQmlSourceLocation sourceLocation() const; |
200 | }; |
201 | |
202 | template<> |
203 | inline const FunctionObject *Value::as() const { |
204 | return isManaged() && m()->internalClass->vtable->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr; |
205 | } |
206 | |
207 | |
208 | struct FunctionCtor: FunctionObject |
209 | { |
210 | V4_OBJECT2(FunctionCtor, FunctionObject) |
211 | |
212 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); |
213 | static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
214 | protected: |
215 | enum Type { |
216 | Type_Function, |
217 | Type_Generator |
218 | }; |
219 | static QQmlRefPointer<ExecutableCompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function); |
220 | }; |
221 | |
222 | struct FunctionPrototype: FunctionObject |
223 | { |
224 | V4_OBJECT2(FunctionPrototype, FunctionObject) |
225 | |
226 | void init(ExecutionEngine *engine, Object *ctor); |
227 | |
228 | static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
229 | static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
230 | static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
231 | static ReturnedValue method_bind(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
232 | static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
233 | }; |
234 | |
235 | struct Q_QML_PRIVATE_EXPORT IndexedBuiltinFunction : FunctionObject |
236 | { |
237 | V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) |
238 | }; |
239 | |
240 | void Heap::IndexedBuiltinFunction::init( |
241 | QV4::ExecutionContext *scope, qsizetype index, VTable::Call call) |
242 | { |
243 | Heap::FunctionObject::init(scope); |
244 | this->jsCall = call; |
245 | this->index = index; |
246 | } |
247 | |
248 | struct ArrowFunction : FunctionObject { |
249 | V4_OBJECT2(ArrowFunction, FunctionObject) |
250 | V4_INTERNALCLASS(ArrowFunction) |
251 | enum { NInlineProperties = 3 }; |
252 | |
253 | static void virtualCallWithMetaTypes(const FunctionObject *f, QObject *thisObject, |
254 | void **a, const QMetaType *types, int argc); |
255 | static ReturnedValue virtualCall(const QV4::FunctionObject *f, const QV4::Value *thisObject, |
256 | const QV4::Value *argv, int argc); |
257 | }; |
258 | |
259 | struct ScriptFunction : ArrowFunction { |
260 | V4_OBJECT2(ScriptFunction, ArrowFunction) |
261 | V4_INTERNALCLASS(ScriptFunction) |
262 | |
263 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); |
264 | |
265 | Heap::InternalClass *classForConstructor() const; |
266 | }; |
267 | |
268 | struct MemberFunction : ArrowFunction { |
269 | V4_OBJECT2(MemberFunction, ArrowFunction) |
270 | V4_INTERNALCLASS(MemberFunction) |
271 | }; |
272 | |
273 | struct ConstructorFunction : ScriptFunction { |
274 | V4_OBJECT2(ConstructorFunction, ScriptFunction) |
275 | V4_INTERNALCLASS(ConstructorFunction) |
276 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); |
277 | static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
278 | }; |
279 | |
280 | struct DefaultClassConstructorFunction : FunctionObject { |
281 | V4_OBJECT2(DefaultClassConstructorFunction, FunctionObject) |
282 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); |
283 | static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
284 | }; |
285 | |
286 | struct BoundFunction: FunctionObject { |
287 | V4_OBJECT2(BoundFunction, FunctionObject) |
288 | |
289 | static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) |
290 | { |
291 | return scope->engine()->memoryManager->allocate<BoundFunction>(args&: scope, args&: target, args: boundThis, args&: boundArgs); |
292 | } |
293 | |
294 | Heap::FunctionObject *target() const { return d()->target; } |
295 | Value boundThis() const { return d()->boundThis; } |
296 | Heap::MemberData *boundArgs() const { return d()->boundArgs; } |
297 | |
298 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); |
299 | static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
300 | }; |
301 | |
302 | inline bool FunctionObject::isBoundFunction() const |
303 | { |
304 | return d()->vtable() == BoundFunction::staticVTable(); |
305 | } |
306 | |
307 | inline ReturnedValue checkedResult(QV4::ExecutionEngine *v4, ReturnedValue result) |
308 | { |
309 | return v4->hasException ? QV4::Encode::undefined() : result; |
310 | } |
311 | |
312 | } |
313 | |
314 | QT_END_NAMESPACE |
315 | |
316 | #endif // QMLJS_OBJECTS_H |
317 | |