| 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 | // A FunctionObject is generally something that can be called, either with a JavaScript |
| 32 | // signature (QV4::Value etc) or with a C++ signature (QMetaType etc). For this, it has |
| 33 | // the Call and CallWithMetaTypes VTable entries. |
| 34 | // Some FunctionObjects need to select the actual implementation of the call at run time. |
| 35 | // This comese in two flavors: |
| 36 | // 1. The implementation is a JavaScript function. For these we have |
| 37 | // JavaScriptFunctionObject that holds a QV4::Function member to defer the call to. |
| 38 | // 2. The implementation is a C++ function. For these we have DynamicFunctionObject that |
| 39 | // holds another Call member in the heap object to defer the call to. |
| 40 | // In addition, a FunctionObject may want to be called as constructor. For this we have |
| 41 | // another VTable entry and a flag in the heap object. |
| 42 | |
| 43 | namespace Heap { |
| 44 | |
| 45 | #define FunctionObjectMembers(class, Member) |
| 46 | DECLARE_HEAP_OBJECT(FunctionObject, Object) { |
| 47 | enum { |
| 48 | Index_ProtoConstructor = 0, |
| 49 | Index_Prototype = 0, |
| 50 | Index_HasInstance = 1, |
| 51 | }; |
| 52 | |
| 53 | Q_QML_EXPORT void init(QV4::ExecutionEngine *engine, QV4::String *name = nullptr); |
| 54 | Q_QML_EXPORT void init(QV4::ExecutionEngine *engine, const QString &name); |
| 55 | Q_QML_EXPORT void init(); |
| 56 | }; |
| 57 | |
| 58 | #define JavaScriptFunctionObjectMembers(class, Member) \ |
| 59 | Member(class, Pointer, ExecutionContext *, scope) \ |
| 60 | Member(class, NoMark, Function *, function) |
| 61 | |
| 62 | DECLARE_HEAP_OBJECT(JavaScriptFunctionObject, FunctionObject) { |
| 63 | DECLARE_MARKOBJECTS(JavaScriptFunctionObject) |
| 64 | |
| 65 | void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); |
| 66 | Q_QML_EXPORT void destroy(); |
| 67 | |
| 68 | void setFunction(Function *f); |
| 69 | |
| 70 | unsigned int formalParameterCount() { return function ? function->nFormals : 0; } |
| 71 | unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; } |
| 72 | }; |
| 73 | |
| 74 | #define DynamicFunctionObjectMembers(class, Member) \ |
| 75 | Member(class, NoMark, VTable::Call, jsCall) |
| 76 | |
| 77 | DECLARE_HEAP_OBJECT(DynamicFunctionObject, FunctionObject) { |
| 78 | // NB: We might add a CallWithMetaTypes member to this struct and implement our |
| 79 | // builtins with metatypes, to be called from C++ code. This would make them |
| 80 | // available to qmlcachegen's C++ code generation. |
| 81 | void init(ExecutionEngine *engine, QV4::String *name, VTable::Call call); |
| 82 | }; |
| 83 | |
| 84 | struct FunctionCtor : FunctionObject { |
| 85 | void init(QV4::ExecutionEngine *engine); |
| 86 | }; |
| 87 | |
| 88 | struct FunctionPrototype : FunctionObject { |
| 89 | void init(); |
| 90 | }; |
| 91 | |
| 92 | // A function object with an additional index into a list. |
| 93 | // Used by Models to refer to property roles. |
| 94 | struct IndexedBuiltinFunction : DynamicFunctionObject { |
| 95 | inline void init(QV4::ExecutionEngine *engine, qsizetype index, VTable::Call call); |
| 96 | qsizetype index; |
| 97 | }; |
| 98 | |
| 99 | struct ArrowFunction : JavaScriptFunctionObject { |
| 100 | enum { |
| 101 | Index_Name = Index_HasInstance + 1, |
| 102 | Index_Length |
| 103 | }; |
| 104 | void init(QV4::ExecutionContext *scope, Function *function, QV4::String *name = nullptr); |
| 105 | }; |
| 106 | |
| 107 | #define ScriptFunctionMembers(class, Member) \ |
| 108 | Member(class, Pointer, InternalClass *, cachedClassForConstructor) |
| 109 | |
| 110 | DECLARE_HEAP_OBJECT(ScriptFunction, ArrowFunction) { |
| 111 | DECLARE_MARKOBJECTS(ScriptFunction) |
| 112 | void init(QV4::ExecutionContext *scope, Function *function); |
| 113 | }; |
| 114 | |
| 115 | #define MemberFunctionMembers(class, Member) \ |
| 116 | Member(class, Pointer, Object *, homeObject) |
| 117 | |
| 118 | DECLARE_HEAP_OBJECT(MemberFunction, ArrowFunction) { |
| 119 | DECLARE_MARKOBJECTS(MemberFunction) |
| 120 | |
| 121 | void init(QV4::ExecutionContext *scope, Function *function, QV4::String *name = nullptr) { |
| 122 | ArrowFunction::init(scope, function, name); |
| 123 | } |
| 124 | }; |
| 125 | |
| 126 | #define ConstructorFunctionMembers(class, Member) \ |
| 127 | Member(class, Pointer, Object *, homeObject) |
| 128 | |
| 129 | DECLARE_HEAP_OBJECT(ConstructorFunction, ScriptFunction) { |
| 130 | DECLARE_MARKOBJECTS(ConstructorFunction) |
| 131 | bool isDerivedConstructor; |
| 132 | }; |
| 133 | |
| 134 | #define DefaultClassConstructorFunctionMembers(class, Member) \ |
| 135 | Member(class, Pointer, ExecutionContext *, scope) |
| 136 | |
| 137 | DECLARE_HEAP_OBJECT(DefaultClassConstructorFunction, FunctionObject) { |
| 138 | DECLARE_MARKOBJECTS(DefaultClassConstructorFunction) |
| 139 | |
| 140 | bool isDerivedConstructor; |
| 141 | |
| 142 | void init(QV4::ExecutionContext *scope); |
| 143 | }; |
| 144 | |
| 145 | #define BoundFunctionMembers(class, Member) \ |
| 146 | Member(class, Pointer, FunctionObject *, target) \ |
| 147 | Member(class, HeapValue, HeapValue, boundThis) \ |
| 148 | Member(class, Pointer, MemberData *, boundArgs) |
| 149 | |
| 150 | DECLARE_HEAP_OBJECT(BoundFunction, JavaScriptFunctionObject) { |
| 151 | DECLARE_MARKOBJECTS(BoundFunction) |
| 152 | |
| 153 | void init(QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); |
| 154 | }; |
| 155 | |
| 156 | struct BoundConstructor : BoundFunction {}; |
| 157 | |
| 158 | } |
| 159 | |
| 160 | struct Q_QML_EXPORT FunctionObject: Object { |
| 161 | V4_OBJECT2(FunctionObject, Object) |
| 162 | Q_MANAGED_TYPE(FunctionObject) |
| 163 | V4_INTERNALCLASS(FunctionObject) |
| 164 | V4_PROTOTYPE(functionPrototype) |
| 165 | enum { NInlineProperties = 1 }; |
| 166 | |
| 167 | bool canBeTailCalled() const { return vtable()->isTailCallable; } |
| 168 | |
| 169 | ReturnedValue name() const; |
| 170 | |
| 171 | void setName(String *name) { |
| 172 | defineReadonlyConfigurableProperty(name: engine()->id_name(), value: *name); |
| 173 | } |
| 174 | void createDefaultPrototypeProperty(uint protoConstructorSlot); |
| 175 | |
| 176 | ReturnedValue callAsConstructor( |
| 177 | const Value *argv, int argc, const Value *newTarget = nullptr) const |
| 178 | { |
| 179 | if (const auto callAsConstructor = vtable()->callAsConstructor) |
| 180 | return callAsConstructor(this, argv, argc, newTarget ? newTarget : this); |
| 181 | return failCallAsConstructor(); |
| 182 | } |
| 183 | |
| 184 | ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const |
| 185 | { |
| 186 | if (const auto call = vtable()->call) |
| 187 | return call(this, thisObject, argv, argc); |
| 188 | return failCall(); |
| 189 | } |
| 190 | |
| 191 | void call(QObject *thisObject, void **argv, const QMetaType *types, int argc) const |
| 192 | { |
| 193 | if (const auto callWithMetaTypes = vtable()->callWithMetaTypes) |
| 194 | callWithMetaTypes(this, thisObject, argv, types, argc); |
| 195 | else |
| 196 | failCall(); |
| 197 | } |
| 198 | |
| 199 | inline ReturnedValue callAsConstructor(const JSCallData &data) const; |
| 200 | inline ReturnedValue call(const JSCallData &data) const; |
| 201 | |
| 202 | ReturnedValue failCall() const; |
| 203 | ReturnedValue failCallAsConstructor() const; |
| 204 | static void virtualConvertAndCall( |
| 205 | const FunctionObject *f, QObject *thisObject, |
| 206 | void **argv, const QMetaType *types, int argc); |
| 207 | |
| 208 | static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); |
| 209 | static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor); |
| 210 | static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, String *name); |
| 211 | static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount); |
| 212 | |
| 213 | bool isBinding() const; |
| 214 | bool isBoundFunction() const; |
| 215 | bool isConstructor() const { return vtable()->callAsConstructor; } |
| 216 | |
| 217 | ReturnedValue getHomeObject() const; |
| 218 | |
| 219 | ReturnedValue protoProperty() const { |
| 220 | return getValueByIndex(propertyIndex: Heap::FunctionObject::Index_Prototype); |
| 221 | } |
| 222 | bool hasHasInstanceProperty() const { |
| 223 | return !internalClass()->propertyData.at(i: Heap::FunctionObject::Index_HasInstance).isEmpty(); |
| 224 | } |
| 225 | }; |
| 226 | |
| 227 | template<> |
| 228 | inline const FunctionObject *Value::as() const { |
| 229 | if (!isManaged()) |
| 230 | return nullptr; |
| 231 | |
| 232 | const VTable *vtable = m()->internalClass->vtable; |
| 233 | return (vtable->call || vtable->callAsConstructor) |
| 234 | ? reinterpret_cast<const FunctionObject *>(this) |
| 235 | : nullptr; |
| 236 | } |
| 237 | |
| 238 | struct Q_QML_EXPORT JavaScriptFunctionObject: FunctionObject |
| 239 | { |
| 240 | V4_OBJECT2(JavaScriptFunctionObject, FunctionObject) |
| 241 | V4_NEEDS_DESTROY |
| 242 | |
| 243 | Heap::ExecutionContext *scope() const { return d()->scope; } |
| 244 | |
| 245 | Function *function() const { return d()->function; } |
| 246 | unsigned int formalParameterCount() const { return d()->formalParameterCount(); } |
| 247 | unsigned int varCount() const { return d()->varCount(); } |
| 248 | bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } |
| 249 | QQmlSourceLocation sourceLocation() const; |
| 250 | }; |
| 251 | |
| 252 | struct Q_QML_EXPORT DynamicFunctionObject: FunctionObject |
| 253 | { |
| 254 | V4_OBJECT2(DynamicFunctionObject, FunctionObject) |
| 255 | |
| 256 | static ReturnedValue virtualCall( |
| 257 | const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
| 258 | }; |
| 259 | |
| 260 | struct FunctionCtor: FunctionObject |
| 261 | { |
| 262 | V4_OBJECT2(FunctionCtor, FunctionObject) |
| 263 | |
| 264 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); |
| 265 | static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
| 266 | protected: |
| 267 | enum Type { |
| 268 | Type_Function, |
| 269 | Type_Generator |
| 270 | }; |
| 271 | static QQmlRefPointer<ExecutableCompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function); |
| 272 | }; |
| 273 | |
| 274 | struct FunctionPrototype: FunctionObject |
| 275 | { |
| 276 | V4_OBJECT2(FunctionPrototype, FunctionObject) |
| 277 | |
| 278 | void init(ExecutionEngine *engine, Object *ctor); |
| 279 | |
| 280 | static ReturnedValue virtualCall( |
| 281 | const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
| 282 | |
| 283 | static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
| 284 | static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
| 285 | static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
| 286 | static ReturnedValue method_bind(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
| 287 | static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
| 288 | }; |
| 289 | |
| 290 | struct Q_QML_EXPORT IndexedBuiltinFunction : DynamicFunctionObject |
| 291 | { |
| 292 | V4_OBJECT2(IndexedBuiltinFunction, DynamicFunctionObject) |
| 293 | }; |
| 294 | |
| 295 | void Heap::IndexedBuiltinFunction::init( |
| 296 | QV4::ExecutionEngine *engine, qsizetype index, VTable::Call call) |
| 297 | { |
| 298 | Heap::FunctionObject::init(engine); |
| 299 | this->jsCall = call; |
| 300 | this->index = index; |
| 301 | } |
| 302 | |
| 303 | struct ArrowFunction : JavaScriptFunctionObject { |
| 304 | V4_OBJECT2(ArrowFunction, JavaScriptFunctionObject) |
| 305 | V4_INTERNALCLASS(ArrowFunction) |
| 306 | enum { |
| 307 | NInlineProperties = 3, |
| 308 | IsTailCallable = true, |
| 309 | }; |
| 310 | |
| 311 | static void virtualCallWithMetaTypes(const FunctionObject *f, QObject *thisObject, |
| 312 | void **a, const QMetaType *types, int argc); |
| 313 | static ReturnedValue virtualCall(const QV4::FunctionObject *f, const QV4::Value *thisObject, |
| 314 | const QV4::Value *argv, int argc); |
| 315 | }; |
| 316 | |
| 317 | struct ScriptFunction : ArrowFunction { |
| 318 | V4_OBJECT2(ScriptFunction, ArrowFunction) |
| 319 | V4_INTERNALCLASS(ScriptFunction) |
| 320 | |
| 321 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); |
| 322 | |
| 323 | Heap::InternalClass *classForConstructor() const; |
| 324 | }; |
| 325 | |
| 326 | struct MemberFunction : ArrowFunction { |
| 327 | V4_OBJECT2(MemberFunction, ArrowFunction) |
| 328 | V4_INTERNALCLASS(MemberFunction) |
| 329 | }; |
| 330 | |
| 331 | struct ConstructorFunction : ScriptFunction { |
| 332 | V4_OBJECT2(ConstructorFunction, ScriptFunction) |
| 333 | V4_INTERNALCLASS(ConstructorFunction) |
| 334 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); |
| 335 | static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
| 336 | }; |
| 337 | |
| 338 | struct DefaultClassConstructorFunction : FunctionObject { |
| 339 | V4_OBJECT2(DefaultClassConstructorFunction, FunctionObject) |
| 340 | |
| 341 | Heap::ExecutionContext *scope() const { return d()->scope; } |
| 342 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); |
| 343 | static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
| 344 | }; |
| 345 | |
| 346 | struct BoundFunction: JavaScriptFunctionObject { |
| 347 | V4_OBJECT2(BoundFunction, JavaScriptFunctionObject) |
| 348 | |
| 349 | Heap::FunctionObject *target() const { return d()->target; } |
| 350 | Value boundThis() const { return d()->boundThis; } |
| 351 | Heap::MemberData *boundArgs() const { return d()->boundArgs; } |
| 352 | |
| 353 | static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); |
| 354 | }; |
| 355 | |
| 356 | struct BoundConstructor: BoundFunction { |
| 357 | V4_OBJECT2(BoundConstructor, BoundFunction) |
| 358 | |
| 359 | static ReturnedValue virtualCallAsConstructor( |
| 360 | const FunctionObject *f, const Value *argv, int argc, const Value *); |
| 361 | }; |
| 362 | |
| 363 | inline bool FunctionObject::isBoundFunction() const |
| 364 | { |
| 365 | const VTable *vtable = d()->vtable(); |
| 366 | return vtable == BoundFunction::staticVTable() || vtable == BoundConstructor::staticVTable(); |
| 367 | } |
| 368 | |
| 369 | inline ReturnedValue checkedResult(QV4::ExecutionEngine *v4, ReturnedValue result) |
| 370 | { |
| 371 | return v4->hasException ? QV4::Encode::undefined() : result; |
| 372 | } |
| 373 | |
| 374 | } |
| 375 | |
| 376 | QT_END_NAMESPACE |
| 377 | |
| 378 | #endif // QMLJS_OBJECTS_H |
| 379 | |