diff options
Diffstat (limited to 'src/qml/jsruntime/qv4functionobject_p.h')
-rw-r--r-- | src/qml/jsruntime/qv4functionobject_p.h | 210 |
1 files changed, 136 insertions, 74 deletions
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 573848f62a..f4a2935b5a 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -28,36 +28,41 @@ namespace QV4 { struct IndexedBuiltinFunction; struct JSCallData; -namespace Heap { - +// A FunctionObject is generally something that can be called, either with a JavaScript +// signature (QV4::Value etc) or with a C++ signature (QMetaType etc). For this, it has +// the Call and CallWithMetaTypes VTable entries. +// Some FunctionObjects need to select the actual implementation of the call at run time. +// This comese in two flavors: +// 1. The implementation is a JavaScript function. For these we have +// JavaScriptFunctionObject that holds a QV4::Function member to defer the call to. +// 2. The implementation is a C++ function. For these we have DynamicFunctionObject that +// holds another Call member in the heap object to defer the call to. +// In addition, a FunctionObject may want to be called as constructor. For this we have +// another VTable entry and a flag in the heap object. -#define FunctionObjectMembers(class, Member) \ - Member(class, Pointer, ExecutionContext *, scope) \ - Member(class, NoMark, Function *, function) \ - Member(class, NoMark, VTable::Call, jsCall) \ - Member(class, NoMark, VTable::CallAsConstructor, jsConstruct) \ - Member(class, NoMark, VTable::CallWithMetaTypes, jsCallWithMetaTypes) \ - Member(class, NoMark, bool, canBeTailCalled) +namespace Heap { +#define FunctionObjectMembers(class, Member) DECLARE_HEAP_OBJECT(FunctionObject, Object) { - DECLARE_MARKOBJECTS(FunctionObject) enum { Index_ProtoConstructor = 0, Index_Prototype = 0, Index_HasInstance = 1, }; - bool isConstructor() const { - return jsConstruct != nullptr; - } - - Q_QML_EXPORT void init( - QV4::ExecutionContext *scope, QV4::String *name, - VTable::Call call, VTable::CallWithMetaTypes callWithMetaTypes = nullptr); - Q_QML_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); - Q_QML_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); - Q_QML_EXPORT void init(QV4::ExecutionContext *scope, const QString &name); + Q_QML_EXPORT void init(QV4::ExecutionEngine *engine, QV4::String *name = nullptr); + Q_QML_EXPORT void init(QV4::ExecutionEngine *engine, const QString &name); Q_QML_EXPORT void init(); +}; + +#define JavaScriptFunctionObjectMembers(class, Member) \ + Member(class, Pointer, ExecutionContext *, scope) \ + Member(class, NoMark, Function *, function) + +DECLARE_HEAP_OBJECT(JavaScriptFunctionObject, FunctionObject) { + DECLARE_MARKOBJECTS(JavaScriptFunctionObject) + + void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); Q_QML_EXPORT void destroy(); void setFunction(Function *f); @@ -66,8 +71,18 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; } }; +#define DynamicFunctionObjectMembers(class, Member) \ + Member(class, NoMark, VTable::Call, jsCall) + +DECLARE_HEAP_OBJECT(DynamicFunctionObject, FunctionObject) { + // NB: We might add a CallWithMetaTypes member to this struct and implement our + // builtins with metatypes, to be called from C++ code. This would make them + // available to qmlcachegen's C++ code generation. + void init(ExecutionEngine *engine, QV4::String *name, VTable::Call call); +}; + struct FunctionCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionEngine *engine); }; struct FunctionPrototype : FunctionObject { @@ -76,12 +91,12 @@ struct FunctionPrototype : FunctionObject { // A function object with an additional index into a list. // Used by Models to refer to property roles. -struct IndexedBuiltinFunction : FunctionObject { - inline void init(QV4::ExecutionContext *scope, qsizetype index, VTable::Call call); +struct IndexedBuiltinFunction : DynamicFunctionObject { + inline void init(QV4::ExecutionEngine *engine, qsizetype index, VTable::Call call); qsizetype index; }; -struct ArrowFunction : FunctionObject { +struct ArrowFunction : JavaScriptFunctionObject { enum { Index_Name = Index_HasInstance + 1, Index_Length @@ -116,9 +131,15 @@ DECLARE_HEAP_OBJECT(ConstructorFunction, ScriptFunction) { bool isDerivedConstructor; }; -struct DefaultClassConstructorFunction : FunctionObject -{ +#define DefaultClassConstructorFunctionMembers(class, Member) \ + Member(class, Pointer, ExecutionContext *, scope) + +DECLARE_HEAP_OBJECT(DefaultClassConstructorFunction, FunctionObject) { + DECLARE_MARKOBJECTS(DefaultClassConstructorFunction) + bool isDerivedConstructor; + + void init(QV4::ExecutionContext *scope); }; #define BoundFunctionMembers(class, Member) \ @@ -126,66 +147,72 @@ struct DefaultClassConstructorFunction : FunctionObject Member(class, HeapValue, HeapValue, boundThis) \ Member(class, Pointer, MemberData *, boundArgs) -DECLARE_HEAP_OBJECT(BoundFunction, FunctionObject) { +DECLARE_HEAP_OBJECT(BoundFunction, JavaScriptFunctionObject) { DECLARE_MARKOBJECTS(BoundFunction) - void init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); + void init(QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); }; +struct BoundConstructor : BoundFunction {}; + } struct Q_QML_EXPORT FunctionObject: Object { - enum { - IsFunctionObject = true - }; V4_OBJECT2(FunctionObject, Object) Q_MANAGED_TYPE(FunctionObject) V4_INTERNALCLASS(FunctionObject) V4_PROTOTYPE(functionPrototype) - V4_NEEDS_DESTROY enum { NInlineProperties = 1 }; - bool canBeTailCalled() const { return d()->canBeTailCalled; } - Heap::ExecutionContext *scope() const { return d()->scope; } - Function *function() const { return d()->function; } + bool canBeTailCalled() const { return vtable()->isTailCallable; } ReturnedValue name() const; - unsigned int formalParameterCount() const { return d()->formalParameterCount(); } - unsigned int varCount() const { return d()->varCount(); } void setName(String *name) { defineReadonlyConfigurableProperty(engine()->id_name(), *name); } void createDefaultPrototypeProperty(uint protoConstructorSlot); - inline ReturnedValue callAsConstructor(const JSCallData &data) const; - ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget = nullptr) const { - if (!d()->jsConstruct) - return engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); - return d()->jsConstruct(this, argv, argc, newTarget ? newTarget : this); + ReturnedValue callAsConstructor( + const Value *argv, int argc, const Value *newTarget = nullptr) const + { + if (const auto callAsConstructor = vtable()->callAsConstructor) + return callAsConstructor(this, argv, argc, newTarget ? newTarget : this); + return failCallAsConstructor(); } - inline ReturnedValue call(const JSCallData &data) const; - ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const { - if (!d()->jsCall) - return engine()->throwTypeError(QStringLiteral("Function can only be called with |new|.")); - return d()->jsCall(this, thisObject, argv, argc); + + ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const + { + if (const auto call = vtable()->call) + return call(this, thisObject, argv, argc); + return failCall(); } - static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); - void call(QObject *thisObject, void **a, const QMetaType *types, int argc); - static void virtualCallWithMetaTypes(const FunctionObject *f, QObject *thisObject, - void **a, const QMetaType *types, int argc); + + void call(QObject *thisObject, void **argv, const QMetaType *types, int argc) const + { + if (const auto callWithMetaTypes = vtable()->callWithMetaTypes) + callWithMetaTypes(this, thisObject, argv, types, argc); + else + failCall(); + } + + inline ReturnedValue callAsConstructor(const JSCallData &data) const; + inline ReturnedValue call(const JSCallData &data) const; + + ReturnedValue failCall() const; + ReturnedValue failCallAsConstructor() const; + static void virtualConvertAndCall( + const FunctionObject *f, QObject *thisObject, + void **argv, const QMetaType *types, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor); static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, String *name); static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount); - bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } bool isBinding() const; bool isBoundFunction() const; - bool isConstructor() const { - return d()->isConstructor(); - } + bool isConstructor() const { return vtable()->callAsConstructor; } ReturnedValue getHomeObject() const; @@ -195,15 +222,40 @@ struct Q_QML_EXPORT FunctionObject: Object { bool hasHasInstanceProperty() const { return !internalClass()->propertyData.at(Heap::FunctionObject::Index_HasInstance).isEmpty(); } - - QQmlSourceLocation sourceLocation() const; }; template<> inline const FunctionObject *Value::as() const { - return isManaged() && m()->internalClass->vtable->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr; + if (!isManaged()) + return nullptr; + + const VTable *vtable = m()->internalClass->vtable; + return (vtable->call || vtable->callAsConstructor) + ? reinterpret_cast<const FunctionObject *>(this) + : nullptr; } +struct Q_QML_EXPORT JavaScriptFunctionObject: FunctionObject +{ + V4_OBJECT2(JavaScriptFunctionObject, FunctionObject) + V4_NEEDS_DESTROY + + Heap::ExecutionContext *scope() const { return d()->scope; } + + Function *function() const { return d()->function; } + unsigned int formalParameterCount() const { return d()->formalParameterCount(); } + unsigned int varCount() const { return d()->varCount(); } + bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } + QQmlSourceLocation sourceLocation() const; +}; + +struct Q_QML_EXPORT DynamicFunctionObject: FunctionObject +{ + V4_OBJECT2(DynamicFunctionObject, FunctionObject) + + static ReturnedValue virtualCall( + const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; struct FunctionCtor: FunctionObject { @@ -225,6 +277,9 @@ struct FunctionPrototype: FunctionObject void init(ExecutionEngine *engine, Object *ctor); + static ReturnedValue virtualCall( + const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -232,23 +287,26 @@ struct FunctionPrototype: FunctionObject static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; -struct Q_QML_EXPORT IndexedBuiltinFunction : FunctionObject +struct Q_QML_EXPORT IndexedBuiltinFunction : DynamicFunctionObject { - V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) + V4_OBJECT2(IndexedBuiltinFunction, DynamicFunctionObject) }; void Heap::IndexedBuiltinFunction::init( - QV4::ExecutionContext *scope, qsizetype index, VTable::Call call) + QV4::ExecutionEngine *engine, qsizetype index, VTable::Call call) { - Heap::FunctionObject::init(scope); + Heap::FunctionObject::init(engine); this->jsCall = call; this->index = index; } -struct ArrowFunction : FunctionObject { - V4_OBJECT2(ArrowFunction, FunctionObject) +struct ArrowFunction : JavaScriptFunctionObject { + V4_OBJECT2(ArrowFunction, JavaScriptFunctionObject) V4_INTERNALCLASS(ArrowFunction) - enum { NInlineProperties = 3 }; + enum { + NInlineProperties = 3, + IsTailCallable = true, + }; static void virtualCallWithMetaTypes(const FunctionObject *f, QObject *thisObject, void **a, const QMetaType *types, int argc); @@ -279,29 +337,33 @@ struct ConstructorFunction : ScriptFunction { struct DefaultClassConstructorFunction : FunctionObject { V4_OBJECT2(DefaultClassConstructorFunction, FunctionObject) + + Heap::ExecutionContext *scope() const { return d()->scope; } static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; -struct BoundFunction: FunctionObject { - V4_OBJECT2(BoundFunction, FunctionObject) - - static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) - { - return scope->engine()->memoryManager->allocate<BoundFunction>(scope, target, boundThis, boundArgs); - } +struct BoundFunction: JavaScriptFunctionObject { + V4_OBJECT2(BoundFunction, JavaScriptFunctionObject) Heap::FunctionObject *target() const { return d()->target; } Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; +struct BoundConstructor: BoundFunction { + V4_OBJECT2(BoundConstructor, BoundFunction) + + static ReturnedValue virtualCallAsConstructor( + const FunctionObject *f, const Value *argv, int argc, const Value *); +}; + inline bool FunctionObject::isBoundFunction() const { - return d()->vtable() == BoundFunction::staticVTable(); + const VTable *vtable = d()->vtable(); + return vtable == BoundFunction::staticVTable() || vtable == BoundConstructor::staticVTable(); } inline ReturnedValue checkedResult(QV4::ExecutionEngine *v4, ReturnedValue result) |