/* * Copyright (c) 2021-2024 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_IC_IC_HANDLER_H #define ECMASCRIPT_IC_IC_HANDLER_H #include "ecmascript/ecma_macros.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/js_typed_array.h" #include "ecmascript/mem/tagged_object.h" #include "ecmascript/object_operator.h" #include "ecmascript/js_function.h" #include "ecmascript/js_primitive_ref.h" namespace panda::ecmascript { class HandlerBase { public: static constexpr uint32_t KIND_BIT_LENGTH = 4; static constexpr uint32_t STORE_KIND_BIT_LENGTH = 2; static constexpr uint32_t MAX_BIT_SIZE = 48; enum HandlerKind { NONE = 0, FIELD, ELEMENT, DICTIONARY, STRING, STRING_LENGTH, TYPED_ARRAY, NUMBER, BOOLEAN, NON_EXIST, TOTAL_KINDS, }; static_assert(static_cast(HandlerKind::TOTAL_KINDS) <= (1 << KIND_BIT_LENGTH)); // Store Handler kind combined with KindBit called SWholeKindBit. Which used to quickly check S_FIELD kind enum StoreHandlerKind { S_NONE = HandlerKind::NONE, S_FIELD, S_ELEMENT, S_TOTAL_KINDS, }; static_assert(static_cast(StoreHandlerKind::S_TOTAL_KINDS) <= (1 << STORE_KIND_BIT_LENGTH)); // For Load using KindBit = BitField; // [0, 4) using InlinedPropsBit = KindBit::NextFlag; // [4, 5) using AccessorBit = InlinedPropsBit::NextFlag; // [5, 6) using IsJSArrayBit = AccessorBit::NextFlag; // [6, 7) using OffsetBit = IsJSArrayBit::NextField; // [7, 17) using RepresentationBit = OffsetBit::NextField; // [17, 19) using AttrIndexBit = RepresentationBit::NextField; // [19, 29) using IsOnHeapBit = AttrIndexBit::NextFlag; // [29, 30) using NeedSkipInPGODumpBit = IsOnHeapBit::NextFlag; // [30, 31) static_assert(NeedSkipInPGODumpBit::END_BIT <= MAX_BIT_SIZE, "load handler overflow"); // For Store using SWholeKindBit = KindBit; using SKindBit = BitField; // [0, 2) static_assert(SKindBit::START_BIT == KindBit::START_BIT); using SSharedBit = SKindBit::NextFlag; // [2, 4) static_assert((SKindBit::SIZE + SSharedBit::SIZE) <= KindBit::SIZE); // reuse: [0, 4) bits // shared with Load bits: [4, 30) using SOutOfBoundsBit = IsOnHeapBit::NextFlag; // reuse: [30, 31) bit using SFieldTypeBit = SOutOfBoundsBit::NextField; // [31, 39) static_assert(SFieldTypeBit::END_BIT <= MAX_BIT_SIZE, "store handler overflow"); using Type = uint64_t; static_assert(sizeof(Type) <= JSTaggedValue::TaggedTypeSize()); HandlerBase() = default; virtual ~HandlerBase() = default; static inline bool IsAccessor(Type handler) { return AccessorBit::Get(handler); } static inline SharedFieldType GetFieldType(Type handler) { return static_cast(SFieldTypeBit::Get(handler)); } static inline bool IsNonExist(Type handler) { return GetKind(handler) == HandlerKind::NON_EXIST; } static inline bool IsField(Type handler) { return GetKind(handler) == HandlerKind::FIELD; } static inline bool IsNonSharedStoreField(Type handler) { return static_cast(GetKind(handler)) == StoreHandlerKind::S_FIELD; } static inline bool IsStoreShared(Type handler) { return SSharedBit::Get(handler); } static inline void ClearSharedStoreKind(Type &handler) { SSharedBit::Set(false, &handler); } static inline bool IsStoreOutOfBounds(Type handler) { return SOutOfBoundsBit::Get(handler); } static inline void ClearStoreOutOfBounds(Type &handler) { SOutOfBoundsBit::Set(false, &handler); } static inline bool IsString(Type handler) { return GetKind(handler) == HandlerKind::STRING; } static inline bool IsNumber(Type handler) { return GetKind(handler) == HandlerKind::NUMBER; } static inline bool IsBoolean(Type handler) { return GetKind(handler) == HandlerKind::BOOLEAN; } static inline bool IsSupportedPrimitiveTypeICHandler(Type handler) { return IsString(handler) || IsNumber(handler) || IsBoolean(handler); } static inline bool IsStringLength(Type handler) { return GetKind(handler) == HandlerKind::STRING_LENGTH; } static inline PrimitiveType TryGetPrimitiveType(Type handler) { switch (GetKind(handler)) { case HandlerKind::NUMBER: return PrimitiveType::PRIMITIVE_NUMBER; case HandlerKind::BOOLEAN: return PrimitiveType::PRIMITIVE_BOOLEAN; default: return PrimitiveType::PRIMITIVE_TYPE_INVALID; } } static inline bool IsElement(Type handler) { return IsNormalElement(handler) || IsStringElement(handler) || IsTypedArrayElement(handler); } static inline bool IsNormalElement(Type handler) { return GetKind(handler) == HandlerKind::ELEMENT; } static inline bool IsStringElement(Type handler) { return GetKind(handler) == HandlerKind::STRING; } static inline bool IsTypedArrayElement(Type handler) { return GetKind(handler) == HandlerKind::TYPED_ARRAY; } static inline bool IsDictionary(Type handler) { return GetKind(handler) == HandlerKind::DICTIONARY; } static inline bool IsInlinedProps(Type handler) { return InlinedPropsBit::Get(handler); } static inline HandlerKind GetKind(Type handler) { return KindBit::Get(handler); } static inline bool IsJSArray(Type handler) { return IsJSArrayBit::Get(handler); } static inline bool NeedSkipInPGODump(Type handler) { return NeedSkipInPGODumpBit::Get(handler); } static inline int GetOffset(Type handler) { return OffsetBit::Get(handler); } static inline bool IsOnHeap(Type handler) { return IsOnHeapBit::Get(handler); } static void PrintLoadHandler(uint64_t handler, std::ostream& os); static void PrintStoreHandler(uint64_t handler, std::ostream& os); }; class LoadHandler final : public HandlerBase { public: static JSHandle LoadProperty(const JSThread *thread, const ObjectOperator &op); static JSHandle LoadElement(const JSThread *thread, const ObjectOperator &op); static inline JSHandle LoadStringElement(const JSThread *thread) { uint64_t handler = 0; KindBit::Set(HandlerKind::STRING, &handler); return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); } static inline JSHandle LoadTypedArrayElement(const JSThread *thread, JSHandle typedArray) { uint64_t handler = 0; KindBit::Set(HandlerKind::TYPED_ARRAY, &handler); IsOnHeapBit::Set(JSHandle(typedArray)->GetClass()->IsOnHeapFromBitField(), &handler); return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); } }; class StoreHandler final : public HandlerBase { public: static JSHandle StoreProperty(const JSThread *thread, const ObjectOperator &op); static inline JSHandle StoreElement(const JSThread *thread, JSHandle receiver, uint64_t handler) { SKindBit::Set(StoreHandlerKind::S_ELEMENT, &handler); if (receiver->IsJSArray()) { IsJSArrayBit::Set(true, &handler); } return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); } static inline void SFieldTypeBitSet(const JSThread *thread, const ObjectOperator &op, JSHandle &receiver, uint64_t *handler) { SSharedBit::Set(op.GetReceiver()->IsJSShared(), handler); TaggedArray *array = TaggedArray::Cast(receiver->GetProperties(thread).GetTaggedObject()); if (!array->IsDictionaryMode()) { SFieldTypeBit::Set(op.GetAttr().GetSharedFieldType(), handler); } else { SFieldTypeBit::Set(op.GetAttr().GetDictSharedFieldType(), handler); } } }; class TransitionHandler : public TaggedObject { public: static TransitionHandler *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsTransitionHandler()); return static_cast(object); } static JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op); static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET) ACCESSORS(TransitionHClass, TRANSITION_HCLASS_OFFSET, SIZE) DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) DECL_DUMP() }; class PrototypeHandler : public TaggedObject { public: static PrototypeHandler *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsPrototypeHandler()); return static_cast(object); } static JSHandle LoadPrototype(const JSThread *thread, const ObjectOperator &op, const JSHandle &hclass); static JSHandle StorePrototype(const JSThread *thread, const ObjectOperator &op, const JSHandle &hclass); static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET) ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, HOLDER_OFFSET) ACCESSORS(Holder, HOLDER_OFFSET, ACCESSOR_JSFUNCTION_OFFSET) ACCESSORS(AccessorJSFunction, ACCESSOR_JSFUNCTION_OFFSET, ACCESSOR_METHOD_ID_OFFSET) ACCESSORS_PRIMITIVE_FIELD(AccessorMethodId, uint32_t, ACCESSOR_METHOD_ID_OFFSET, LAST_OFFSET) DEFINE_ALIGN_SIZE(LAST_OFFSET); DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, ACCESSOR_METHOD_ID_OFFSET) DECL_DUMP() }; class TransWithProtoHandler : public TaggedObject { public: static TransWithProtoHandler *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsTransWithProtoHandler()); return static_cast(object); } static JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op, const JSHandle &hclass); static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET) ACCESSORS(TransitionHClass, TRANSITION_HCLASS_OFFSET, PROTO_CELL_OFFSET) ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, SIZE) DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) DECL_DUMP() }; class StoreAOTHandler : public TaggedObject { public: static StoreAOTHandler *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsStoreAOTHandler()); return static_cast(object); } static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET) ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, HOLDER_OFFSET) ACCESSORS(Holder, HOLDER_OFFSET, SIZE) DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) DECL_DUMP() }; } // namespace panda::ecmascript #endif // ECMASCRIPT_IC_IC_HANDLER_H