/* * 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_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" namespace panda::ecmascript { class HandlerBase { public: static constexpr uint32_t KIND_BIT_LENGTH = 3; enum HandlerKind { NONE = 0, FIELD, ELEMENT, DICTIONARY, STRING, STRING_LENGTH, TYPED_ARRAY, NON_EXIST, TOTAL_KINDS, }; using KindBit = BitField; using InlinedPropsBit = KindBit::NextFlag; using AccessorBit = InlinedPropsBit::NextFlag; using InternalAccessorBit = AccessorBit::NextFlag; using IsJSArrayBit = InternalAccessorBit::NextFlag; using OffsetBit = IsJSArrayBit::NextField; using RepresentationBit = OffsetBit::NextField; using AttrIndexBit = RepresentationBit::NextField; using IsOnHeapBit = AttrIndexBit::NextFlag; static_assert(static_cast(HandlerKind::TOTAL_KINDS) <= (1 << KIND_BIT_LENGTH)); HandlerBase() = default; virtual ~HandlerBase() = default; static inline bool IsAccessor(uint32_t handler) { return AccessorBit::Get(handler); } static inline bool IsInternalAccessor(uint32_t handler) { return InternalAccessorBit::Get(handler); } static inline bool IsNonExist(uint32_t handler) { return GetKind(handler) == HandlerKind::NON_EXIST; } static inline bool IsField(uint32_t handler) { return GetKind(handler) == HandlerKind::FIELD; } static inline bool IsString(uint32_t handler) { return GetKind(handler) == HandlerKind::STRING; } static inline bool IsStringLength(uint32_t handler) { return GetKind(handler) == HandlerKind::STRING_LENGTH; } static inline bool IsElement(uint32_t handler) { return IsNormalElement(handler) || IsStringElement(handler) || IsTypedArrayElement(handler); } static inline bool IsNormalElement(uint32_t handler) { return GetKind(handler) == HandlerKind::ELEMENT; } static inline bool IsStringElement(uint32_t handler) { return GetKind(handler) == HandlerKind::STRING; } static inline bool IsTypedArrayElement(uint32_t handler) { return GetKind(handler) == HandlerKind::TYPED_ARRAY; } static inline bool IsDictionary(uint32_t handler) { return GetKind(handler) == HandlerKind::DICTIONARY; } static inline bool IsInlinedProps(uint32_t handler) { return InlinedPropsBit::Get(handler); } static inline HandlerKind GetKind(uint32_t handler) { return KindBit::Get(handler); } static inline bool IsJSArray(uint32_t handler) { return IsJSArrayBit::Get(handler); } static inline int GetOffset(uint32_t handler) { return OffsetBit::Get(handler); } static inline bool IsOnHeap(uint32_t handler) { return IsOnHeapBit::Get(handler); } }; class LoadHandler final : public HandlerBase { public: static inline JSHandle LoadProperty(const JSThread *thread, const ObjectOperator &op) { uint32_t handler = 0; ASSERT(!op.IsElement()); if (!op.IsFound()) { KindBit::Set(HandlerKind::NON_EXIST, &handler); return JSHandle(thread, JSTaggedValue(handler)); } ASSERT(op.IsFastMode()); JSTaggedValue val = op.GetValue(); if (val.IsPropertyBox()) { return JSHandle(thread, val); } bool hasAccessor = op.IsAccessorDescriptor(); AccessorBit::Set(hasAccessor, &handler); if (!hasAccessor) { if (op.GetReceiver()->IsString()) { JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString(); EcmaString *proKey = nullptr; if (op.GetKey()->IsString()) { proKey = EcmaString::Cast(op.GetKey()->GetTaggedObject()); } if (EcmaStringAccessor::StringsAreEqual(proKey, EcmaString::Cast(lenKey.GetTaggedObject()))) { KindBit::Set(HandlerKind::STRING_LENGTH, &handler); } else { KindBit::Set(HandlerKind::STRING, &handler); } } else { KindBit::Set(HandlerKind::FIELD, &handler); } } if (op.IsInlinedProps()) { InlinedPropsBit::Set(true, &handler); JSHandle holder = JSHandle::Cast(op.GetHolder()); auto index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); OffsetBit::Set(index, &handler); AttrIndexBit::Set(op.GetIndex(), &handler); RepresentationBit::Set(op.GetRepresentation(), &handler); return JSHandle(thread, JSTaggedValue(handler)); } if (op.IsFastMode()) { JSHandle holder = JSHandle::Cast(op.GetHolder()); uint32_t inlinePropNum = holder->GetJSHClass()->GetInlinedProperties(); AttrIndexBit::Set(op.GetIndex() + inlinePropNum, &handler); OffsetBit::Set(op.GetIndex(), &handler); RepresentationBit::Set(Representation::TAGGED, &handler); return JSHandle(thread, JSTaggedValue(handler)); } LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } static inline JSHandle LoadElement(const JSThread *thread, const ObjectOperator &op) { uint32_t handler = 0; KindBit::Set(HandlerKind::ELEMENT, &handler); if (op.GetReceiver()->IsJSArray()) { IsJSArrayBit::Set(true, &handler); } return JSHandle(thread, JSTaggedValue(handler)); } static inline JSHandle LoadStringElement(const JSThread *thread) { uint32_t handler = 0; KindBit::Set(HandlerKind::STRING, &handler); return JSHandle(thread, JSTaggedValue(handler)); } static inline JSHandle LoadTypedArrayElement(const JSThread *thread, JSHandle typedArray) { uint32_t handler = 0; KindBit::Set(HandlerKind::TYPED_ARRAY, &handler); IsOnHeapBit::Set(typedArray->GetIsOnHeap(), &handler); return JSHandle(thread, JSTaggedValue(handler)); } }; class StoreHandler final : public HandlerBase { public: static inline JSHandle StoreProperty(const JSThread *thread, const ObjectOperator &op) { if (op.IsElement()) { return StoreElement(thread, op.GetReceiver()); } uint32_t handler = 0; JSTaggedValue val = op.GetValue(); if (val.IsPropertyBox()) { return JSHandle(thread, val); } bool hasSetter = op.IsAccessorDescriptor(); AccessorBit::Set(hasSetter, &handler); if (!hasSetter) { KindBit::Set(HandlerKind::FIELD, &handler); } if (op.IsInlinedProps()) { InlinedPropsBit::Set(true, &handler); uint32_t index = 0; if (!hasSetter) { JSHandle receiver = JSHandle::Cast(op.GetReceiver()); index = receiver->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); } else { JSHandle holder = JSHandle::Cast(op.GetHolder()); index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); } AttrIndexBit::Set(op.GetIndex(), &handler); OffsetBit::Set(index, &handler); RepresentationBit::Set(op.GetRepresentation(), &handler); return JSHandle(thread, JSTaggedValue(handler)); } ASSERT(op.IsFastMode()); JSHandle receiver = JSHandle::Cast(op.GetReceiver()); uint32_t inlinePropNum = receiver->GetJSHClass()->GetInlinedProperties(); AttrIndexBit::Set(op.GetIndex() + inlinePropNum, &handler); OffsetBit::Set(op.GetIndex(), &handler); RepresentationBit::Set(Representation::TAGGED, &handler); return JSHandle(thread, JSTaggedValue(handler)); } static inline JSHandle StoreElement(const JSThread *thread, JSHandle receiver) { uint32_t handler = 0; KindBit::Set(HandlerKind::ELEMENT, &handler); if (receiver->IsJSArray()) { IsJSArrayBit::Set(true, &handler); } return JSHandle(thread, JSTaggedValue(handler)); } }; class TransitionHandler : public TaggedObject { public: static TransitionHandler *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsTransitionHandler()); return static_cast(object); } static inline JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handler = factory->NewTransitionHandler(); JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); handler->SetHandlerInfo(thread, handlerInfo); auto hclass = JSObject::Cast(op.GetReceiver()->GetTaggedObject())->GetJSHClass(); handler->SetTransitionHClass(thread, JSTaggedValue(hclass)); return JSHandle::Cast(handler); } 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 inline JSHandle LoadPrototype(const JSThread *thread, const ObjectOperator &op, const JSHandle &hclass) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handlerInfo = LoadHandler::LoadProperty(thread, op); JSHandle handler = factory->NewPrototypeHandler(); handler->SetHandlerInfo(thread, handlerInfo); if (op.IsFound()) { handler->SetHolder(thread, op.GetHolder()); } auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); handler->SetProtoCell(thread, result); return JSHandle::Cast(handler); } static inline JSHandle StorePrototype(const JSThread *thread, const ObjectOperator &op, const JSHandle &hclass) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handler = factory->NewPrototypeHandler(); JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); handler->SetHandlerInfo(thread, handlerInfo); handler->SetHolder(thread, op.GetHolder()); auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); handler->SetProtoCell(thread, result); return JSHandle::Cast(handler); } 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() }; class TransWithProtoHandler : public TaggedObject { public: static TransWithProtoHandler *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsTransWithProtoHandler()); return static_cast(object); } static inline JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op, const JSHandle &hclass) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handler = factory->NewTransWithProtoHandler(); JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); handler->SetHandlerInfo(thread, handlerInfo); auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); handler->SetProtoCell(thread, result); handler->SetTransitionHClass(thread, hclass.GetTaggedValue()); return JSHandle::Cast(handler); } 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 StoreTSHandler : public TaggedObject { public: static StoreTSHandler *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsStoreTSHandler()); return static_cast(object); } static inline JSHandle StoreAOT(const JSThread *thread, const ObjectOperator &op, const JSHandle &hclass) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle handler = factory->NewStoreTSHandler(); JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); handler->SetHandlerInfo(thread, handlerInfo); handler->SetHolder(thread, op.GetHolder()); auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); handler->SetProtoCell(thread, result); return JSHandle::Cast(handler); } 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