/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ECMASCRIPT_JSFUCNTION_H #define ECMASCRIPT_JSFUCNTION_H #include "ecmascript/accessor_data.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_runtime_call_info.h" #include "ecmascript/js_function_extra_info.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/lexical_env.h" namespace panda::ecmascript { using panda::coretypes::DynClass; class JSThread; class JSFunctionBase : public JSObject { public: static JSFunctionBase *Cast(ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsJSFunctionBase()); return static_cast(object); } static const JSFunctionBase *ConstCast(const ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsJSFunctionBase()); return static_cast(object); } inline void SetConstructor(bool flag) { JSHClass *hclass = GetJSHClass(); hclass->SetConstructor(flag); } static bool SetFunctionName(JSThread *thread, const JSHandle &func, const JSHandle &name, const JSHandle &prefix); static JSHandle GetFunctionName(JSThread *thread, const JSHandle &func); void SetCallTarget([[maybe_unused]] const JSThread *thread, JSMethod *p) { SetMethod(p); } static constexpr size_t METHOD_OFFSET = JSObject::SIZE; SET_GET_NATIVE_FIELD(Method, JSMethod, METHOD_OFFSET, SIZE) DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, SIZE, SIZE) }; class JSFunction : public JSFunctionBase { public: static constexpr int LENGTH_OF_INLINE_PROPERTIES = 3; static constexpr int LENGTH_INLINE_PROPERTY_INDEX = 0; static constexpr int NAME_INLINE_PROPERTY_INDEX = 1; static constexpr int PROTOTYPE_INLINE_PROPERTY_INDEX = 2; static constexpr int CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX = 1; /* -------------- Common API Begin, Don't change those interface!!! ----------------- */ static JSFunction *Cast(ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsJSFunction()); return static_cast(object); } static void InitializeJSFunction(JSThread *thread, const JSHandle &env, const JSHandle &func, FunctionKind kind = FunctionKind::NORMAL_FUNCTION, bool strict = true); // ecma6 7.3 static bool OrdinaryHasInstance(JSThread *thread, const JSHandle &constructor, const JSHandle &obj); static JSTaggedValue SpeciesConstructor(const JSHandle &func, const JSHandle &defaultConstructor); // ecma6 9.2 // 7.3.12 Call(F, V, argumentsList) static JSTaggedValue Call(JSThread *thread, const JSHandle &func, const JSHandle &thisArg, uint32_t argc, const JSTaggedType argv[]); static JSTaggedValue Construct(JSThread *thread, const JSHandle &func, uint32_t argc, const JSTaggedType argv[], const JSHandle &newTarget); static JSTaggedValue Invoke(JSThread *thread, const JSHandle &thisArg, const JSHandle &key, uint32_t argc, const JSTaggedType argv[]); // 9.2.1[[Call]](thisArgument, argumentsList) // 9.3.1[[Call]](thisArgument, argumentsList) static JSTaggedValue CallInternal(JSThread *thread, const JSHandle &func, const JSHandle &thisArg, uint32_t argc, const JSTaggedType argv[]); // 9.2.2[[Construct]](argumentsList, newTarget) // 9.3.2[[Construct]](argumentsList, newTarget) static JSTaggedValue ConstructInternal(JSThread *thread, const JSHandle &func, uint32_t argc, const JSTaggedType argv[], const JSHandle &newTarget); static bool AddRestrictedFunctionProperties(const JSHandle &func, const JSHandle &realm); static bool MakeConstructor(JSThread *thread, const JSHandle &func, const JSHandle &proto, bool writable = true); static bool MakeClassConstructor(JSThread *thread, const JSHandle &cls, const JSHandle &clsPrototype); static void MakeMethod(const JSThread *thread, const JSHandle &func, const JSHandle &homeObject); static bool SetFunctionLength(JSThread *thread, const JSHandle &func, JSTaggedValue length, bool cfg = true); static JSHandle NewJSFunctionPrototype(JSThread *thread, ObjectFactory *factory, const JSHandle &func); static DynClass *GetOrCreateInitialDynClass(JSThread *thread, const JSHandle &fun); static JSTaggedValue AccessCallerArgumentsThrowTypeError(EcmaRuntimeCallInfo *argv); static bool IsDynClass(JSTaggedValue object); static JSTaggedValue PrototypeGetter(JSThread *thread, const JSHandle &self); static bool PrototypeSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, bool mayThrow); static JSTaggedValue NameGetter(JSThread *thread, const JSHandle &self); static bool NameSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, bool mayThrow); static void SetFunctionNameNoPrefix(JSThread *thread, JSFunction *func, JSTaggedValue name); static JSHandle GetInstanceDynClass(JSThread *thread, JSHandle constructor, JSHandle newTarget); inline JSTaggedValue GetFunctionPrototype() const { ASSERT(HasFunctionPrototype()); JSTaggedValue protoOrDyn = GetProtoOrDynClass(); if (protoOrDyn.IsJSHClass()) { return JSHClass::Cast(protoOrDyn.GetTaggedObject())->GetPrototype(); } return protoOrDyn; } inline void SetFunctionPrototype(const JSThread *thread, JSTaggedValue proto) { SetProtoOrDynClass(thread, proto); if (proto.IsJSHClass()) { proto = JSHClass::Cast(proto.GetTaggedObject())->GetPrototype(); } if (proto.IsECMAObject()) { proto.GetTaggedObject()->GetClass()->SetIsPrototype(true); } } inline bool HasInitialDynClass() const { JSTaggedValue protoOrDyn = GetProtoOrDynClass(); return protoOrDyn.IsJSHClass(); } inline bool HasFunctionPrototype() const { JSTaggedValue protoOrDyn = GetProtoOrDynClass(); return !protoOrDyn.IsHole(); } inline DynClass *GetInitialDynClass() const { ASSERT(HasInitialDynClass()); JSTaggedValue protoOrDyn = GetProtoOrDynClass(); return reinterpret_cast(protoOrDyn.GetTaggedObject()); } inline void SetFunctionLength(const JSThread *thread, JSTaggedValue length) { ASSERT(!IsPropertiesDict()); SetPropertyInlinedProps(thread, LENGTH_INLINE_PROPERTY_INDEX, length); } inline bool IsBase() const { FunctionKind kind = GetFunctionKind(); return kind <= FunctionKind::CLASS_CONSTRUCTOR; } inline bool IsDerivedConstructor() const { FunctionKind kind = GetFunctionKind(); return kind == FunctionKind::DERIVED_CONSTRUCTOR || kind == FunctionKind::DEFAULT_DERIVED_CONSTRUCTOR; } inline void SetFunctionKind(const JSThread *thread, FunctionKind kind) { JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); SetFunctionInfoFlag(thread, JSTaggedValue(FunctionKindBit::Update(oldValue, kind))); } inline void SetStrict(const JSThread *thread, bool flag) { JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); SetFunctionInfoFlag(thread, JSTaggedValue(StrictBit::Update(oldValue, flag))); } inline void SetClassConstructor(const JSThread *thread, bool flag) { JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); SetFunctionInfoFlag(thread, JSTaggedValue(ClassConstructorBit::Update(oldValue, flag))); } inline void SetResolved(const JSThread *thread) { TaggedType oldValue = GetFunctionInfoFlag().GetRawData(); SetFunctionInfoFlag(thread, JSTaggedValue(ResolvedBit::Update(oldValue, true))); } inline bool IsResolved() const { return ResolvedBit::Decode(GetFunctionInfoFlag().GetInt()); } inline void SetFunctionMode(const JSThread *thread, FunctionMode mode) { JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); SetFunctionInfoFlag(thread, JSTaggedValue(ThisModeBit::Update(oldValue, mode))); } inline FunctionKind GetFunctionKind() const { return FunctionKindBit::Decode(GetFunctionInfoFlag().GetInt()); } inline bool IsStrict() const { return StrictBit::Decode(GetFunctionInfoFlag().GetInt()); } inline bool IsClassConstructor() const { return ClassConstructorBit::Decode(GetFunctionInfoFlag().GetInt()); } inline FunctionMode GetFunctionMode() const { return ThisModeBit::Decode(GetFunctionInfoFlag().GetInt()); } inline static bool IsArrowFunction(FunctionKind kind) { return (kind >= ARROW_FUNCTION) && (kind <= ASYNC_ARROW_FUNCTION); } inline static bool IsClassConstructor(FunctionKind kind) { return (kind >= CLASS_CONSTRUCTOR) && (kind <= DERIVED_CONSTRUCTOR); } inline static bool IsConstructorKind(FunctionKind kind) { return (kind >= BUILTIN_PROXY_CONSTRUCTOR) && (kind <= DERIVED_CONSTRUCTOR); } inline static bool IsBuiltinConstructor(FunctionKind kind) { return kind >= BUILTIN_PROXY_CONSTRUCTOR && kind <= BUILTIN_CONSTRUCTOR; } inline static bool HasPrototype(FunctionKind kind) { return kind >= BUILTIN_CONSTRUCTOR && kind <= GENERATOR_FUNCTION; } /* -------------- Common API End, Don't change those interface!!! ----------------- */ static void InitializeJSFunction(JSThread *thread, const JSHandle &func, FunctionKind kind = FunctionKind::NORMAL_FUNCTION, bool strict = true); static JSHClass *GetOrCreateInitialJSHClass(JSThread *thread, const JSHandle &fun); static JSHandle GetInstanceJSHClass(JSThread *thread, JSHandle constructor, JSHandle newTarget); static constexpr size_t PROTO_OR_DYNCLASS_OFFSET = JSFunctionBase::SIZE; ACCESSORS(ProtoOrDynClass, PROTO_OR_DYNCLASS_OFFSET, LEXICAL_ENV_OFFSET) ACCESSORS(LexicalEnv, LEXICAL_ENV_OFFSET, HOME_OBJECT_OFFSET) ACCESSORS(HomeObject, HOME_OBJECT_OFFSET, FUNCTION_INFO_FLAG_OFFSET) ACCESSORS(FunctionInfoFlag, FUNCTION_INFO_FLAG_OFFSET, FUNCTION_EXTRA_INFO_OFFSET) ACCESSORS(FunctionExtraInfo, FUNCTION_EXTRA_INFO_OFFSET, CONSTANT_POOL_OFFSET) ACCESSORS(ConstantPool, CONSTANT_POOL_OFFSET, PROFILE_TYPE_INFO_OFFSET) ACCESSORS(ProfileTypeInfo, PROFILE_TYPE_INFO_OFFSET, SIZE) static constexpr uint32_t FUNCTION_KIND_BIT_NUM = 5; using FunctionKindBit = BitField; using StrictBit = FunctionKindBit::NextFlag; using ClassConstructorBit = StrictBit::NextFlag; using ResolvedBit = ClassConstructorBit::NextFlag; using ThisModeBit = ResolvedBit::NextField; // 2: means this flag occupies two digits. DECL_DUMP() DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunctionBase, PROTO_OR_DYNCLASS_OFFSET, SIZE) private: static JSHandle GetOrCreateDerivedJSHClass(JSThread *thread, JSHandle derived, JSHandle constructor, JSHandle ctorInitialDynClass); }; class JSGeneratorFunction : public JSFunction { public: static JSGeneratorFunction *Cast(ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsGeneratorFunction()); return static_cast(object); } static constexpr size_t SIZE = JSFunction::SIZE; DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, SIZE, SIZE) DECL_DUMP() }; class JSBoundFunction : public JSFunctionBase { public: static JSBoundFunction *Cast(ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsBoundFunction()); return static_cast(object); } // 9.4.1.1[[Call]](thisArgument, argumentsList) static JSTaggedValue CallInternal(JSThread *thread, const JSHandle &func); // 9.4.1.2[[Construct]](argumentsList, newTarget) static JSTaggedValue ConstructInternal(JSThread *thread, const JSHandle &func, const JSHandle &newTarget); static constexpr size_t BOUND_TARGET_OFFSET = JSFunctionBase::SIZE; ACCESSORS(BoundTarget, BOUND_TARGET_OFFSET, BOUND_THIS_OFFSET); ACCESSORS(BoundThis, BOUND_THIS_OFFSET, BOUND_ARGUMENTS_OFFSET); ACCESSORS(BoundArguments, BOUND_ARGUMENTS_OFFSET, SIZE); DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunctionBase, BOUND_TARGET_OFFSET, SIZE) DECL_DUMP() }; class JSProxyRevocFunction : public JSFunction { public: static JSProxyRevocFunction *Cast(ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsProxyRevocFunction()); return static_cast(object); } static void ProxyRevocFunctions(const JSThread *thread, const JSHandle &revoker); static constexpr size_t REVOCABLE_PROXY_OFFSET = JSFunction::SIZE; ACCESSORS(RevocableProxy, REVOCABLE_PROXY_OFFSET, SIZE); DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, REVOCABLE_PROXY_OFFSET, SIZE) DECL_DUMP() }; // ResolveFunction/RejectFunction class JSPromiseReactionsFunction : public JSFunction { public: static JSPromiseReactionsFunction *Cast(ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsJSPromiseReactionFunction()); return static_cast(object); } static constexpr size_t PROMISE_OFFSET = JSFunction::SIZE; ACCESSORS(Promise, PROMISE_OFFSET, ALREADY_RESOLVED_OFFSET); ACCESSORS(AlreadyResolved, ALREADY_RESOLVED_OFFSET, SIZE); DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, PROMISE_OFFSET, SIZE) DECL_DUMP() }; // ExecutorFunction class JSPromiseExecutorFunction : public JSFunction { public: static JSPromiseExecutorFunction *Cast(ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsJSPromiseExecutorFunction()); return static_cast(object); } static constexpr size_t CAPABILITY_OFFSET = JSFunction::SIZE; ACCESSORS(Capability, CAPABILITY_OFFSET, SIZE); DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, CAPABILITY_OFFSET, SIZE) DECL_DUMP() }; class JSPromiseAllResolveElementFunction : public JSFunction { public: static JSPromiseAllResolveElementFunction *Cast(ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsJSPromiseAllResolveElementFunction()); return static_cast(object); } static constexpr size_t INDEX_OFFSET = JSFunction::SIZE; ACCESSORS(Index, INDEX_OFFSET, VALUES_OFFSET); ACCESSORS(Values, VALUES_OFFSET, CAPABILITIES_OFFSET); ACCESSORS(Capabilities, CAPABILITIES_OFFSET, REMAINING_ELEMENTS_OFFSET); ACCESSORS(RemainingElements, REMAINING_ELEMENTS_OFFSET, ALREADY_CALLED_OFFSET); ACCESSORS(AlreadyCalled, ALREADY_CALLED_OFFSET, SIZE); DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, INDEX_OFFSET, SIZE) DECL_DUMP() }; class JSIntlBoundFunction : public JSFunction { public: static JSIntlBoundFunction *Cast(ObjectHeader *object) { ASSERT(JSTaggedValue(object).IsJSIntlBoundFunction()); return static_cast(object); } static JSTaggedValue IntlNameGetter(JSThread *thread, const JSHandle &self); static constexpr size_t NUMBER_FORMAT_OFFSET = JSFunction::SIZE; ACCESSORS(NumberFormat, NUMBER_FORMAT_OFFSET, DATETIME_FORMAT_OFFSET); ACCESSORS(DateTimeFormat, DATETIME_FORMAT_OFFSET, COLLATOR_OFFSET); ACCESSORS(Collator, COLLATOR_OFFSET, SIZE); DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, NUMBER_FORMAT_OFFSET, SIZE) }; } // namespace panda::ecmascript #endif // ECMASCRIPT_JSFUCNTION_H