/* * 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. */ #include "ecmascript/ic/ic_handler.h" #include "ecmascript/global_env_constants-inl.h" namespace panda::ecmascript { void UpdateHandlerKind(const JSThread *thread, const ObjectOperator &op, uint64_t &handler) { JSHandle receiver = op.GetReceiver(); if (receiver->IsString()) { JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString(); JSHandle key = op.GetKey(); EcmaString *proKey = key->IsString() ? EcmaString::Cast(key->GetTaggedObject()) : nullptr; if (proKey != nullptr && EcmaStringAccessor::StringsAreEqual(thread, proKey, EcmaString::Cast(lenKey.GetTaggedObject()))) { HandlerBase::KindBit::Set(HandlerBase::HandlerKind::STRING_LENGTH, &handler); } else { HandlerBase::KindBit::Set(HandlerBase::HandlerKind::STRING, &handler); } } else if (receiver->IsNumber()) { HandlerBase::KindBit::Set(HandlerBase::HandlerKind::NUMBER, &handler); } else if (receiver->IsBoolean()) { HandlerBase::KindBit::Set(HandlerBase::HandlerKind::BOOLEAN, &handler); } else { HandlerBase::KindBit::Set(HandlerBase::HandlerKind::FIELD, &handler); } } JSHandle LoadHandler::LoadProperty(const JSThread *thread, const ObjectOperator &op) { uint64_t handler = 0; ASSERT(!op.IsElement()); if (!op.IsFound()) { KindBit::Set(HandlerKind::NON_EXIST, &handler); return JSHandle(thread, JSTaggedValue::WrapUint64(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) { UpdateHandlerKind(thread, op, 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::WrapUint64(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::WrapUint64(handler)); } LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } JSHandle LoadHandler::LoadElement(const JSThread *thread, const ObjectOperator &op) { uint64_t handler = 0; KindBit::Set(HandlerKind::ELEMENT, &handler); // To avoid logical errors and Deopt, temporarily skipping PGO Profiling. // logical errors: // When accessing an element of an object, AOT does not have a chain-climbing operation, // so if the element is on a prototype, it will not be able to get the correct element. // deopt: // Currently there is no way to save the type of the key in pgo file, even if the type of the key // is string, it will be treated as a number type by the AOT, leading to deopt at runtime. if (op.GetReceiver() != op.GetHolder() || op.KeyFromStringType()) { NeedSkipInPGODumpBit::Set(true, &handler); } if (op.GetReceiver()->IsJSArray()) { IsJSArrayBit::Set(true, &handler); } return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); } JSHandle StoreHandler::StoreProperty(const JSThread *thread, const ObjectOperator &op) { uint64_t handler = 0; JSHandle receiver = JSHandle::Cast(op.GetReceiver()); SFieldTypeBitSet(thread, op, receiver, &handler); if (op.IsElement()) { SOutOfBoundsBit::Set(op.GetElementOutOfBounds(), &handler); return StoreElement(thread, op.GetReceiver(), handler); } JSTaggedValue val = op.GetValue(); if (val.IsPropertyBox()) { return JSHandle(thread, val); } bool hasSetter = op.IsAccessorDescriptor(); AccessorBit::Set(hasSetter, &handler); if (!hasSetter) { SKindBit::Set(StoreHandlerKind::S_FIELD, &handler); } if (op.IsInlinedProps()) { InlinedPropsBit::Set(true, &handler); uint32_t index = 0; if (!hasSetter) { 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::WrapUint64(handler)); } ASSERT(op.IsFastMode()); 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::WrapUint64(handler)); } JSHandle TransitionHandler::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); } JSHandle PrototypeHandler::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()); } else { // In "Not Found" case we set holder to Undefined(). handler->SetHolder(thread, JSTaggedValue::Undefined()); } if (op.IsAccessorDescriptor()) { JSTaggedValue result = op.GetValue(); if (result.IsPropertyBox()) { result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(thread); } AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject()); if (!accessor->IsInternal()) { JSTaggedValue getter = accessor->GetGetter(thread); if (!getter.IsUndefined()) { JSHandle func(thread, getter); uint32_t methodOffset = Method::Cast(func->GetMethod(thread))->GetMethodId().GetOffset(); handler->SetAccessorMethodId(methodOffset); handler->SetAccessorJSFunction(thread, getter); } } } auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); handler->SetProtoCell(thread, result); return JSHandle::Cast(handler); } JSHandle PrototypeHandler::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()); if (op.IsAccessorDescriptor()) { JSTaggedValue result = op.GetValue(); if (result.IsPropertyBox()) { result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(thread); } AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject()); if (!accessor->IsInternal() && accessor->HasSetter(thread)) { JSTaggedValue setter = accessor->GetSetter(thread); JSHandle func(thread, setter); handler->SetAccessorMethodId( Method::Cast(func->GetMethod(thread))->GetMethodId().GetOffset()); handler->SetAccessorJSFunction(thread, setter); } } // ShareToLocal is prohibited if (!hclass->IsJSShared()) { auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); handler->SetProtoCell(thread, result); } return JSHandle::Cast(handler); } JSHandle TransWithProtoHandler::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); } void HandlerBase::PrintLoadHandler([[maybe_unused]] uint64_t handler, [[maybe_unused]] std::ostream& os) { #if ECMASCRIPT_ENABLE_TRACE_IC HandlerKind kind = GetKind(handler); os << "kind = "; switch (kind) { case HandlerKind::NONE: os << "NONE" << ", is InlinedPropsBit = " << InlinedPropsBit::Get(handler) << ", is AccessorBit = " << AccessorBit::Get(handler) << ", OffsetBit = " << OffsetBit::Get(handler) << ", AttrIndexBit = " << AttrIndexBit::Get(handler); break; case HandlerKind::FIELD: os << "FIELD" << ", is InlinedPropsBit = " << InlinedPropsBit::Get(handler) << ", is AccessorBit = " << AccessorBit::Get(handler) << ", OffsetBit = " << OffsetBit::Get(handler) << ", AttrIndexBit = " << AttrIndexBit::Get(handler); break; case HandlerKind::ELEMENT: os << "ELEMENT" << ", is JSArray = " << IsJSArrayBit::Get(handler); break; case HandlerKind::DICTIONARY: os << "DICTIONARY"; break; case HandlerKind::STRING: os << "STRING"; break; case HandlerKind::STRING_LENGTH: os << "STRING_LENGTH"; break; case HandlerKind::TYPED_ARRAY: os << "TYPED_ARRAY," << "is OnHeap = " << IsOnHeapBit::Get(handler); break; case HandlerKind::NUMBER: os << "NUMBER"; break; case HandlerKind::NON_EXIST: os << "NON_EXIST"; break; default: UNREACHABLE(); } os << std::endl; #endif } void HandlerBase::PrintStoreHandler([[maybe_unused]] uint64_t handler, [[maybe_unused]] std::ostream& os) { #if ECMASCRIPT_ENABLE_TRACE_IC StoreHandlerKind kind = static_cast(GetKind(handler)); os << "kind = "; switch (kind) { case StoreHandlerKind::S_NONE: os << "NONE" << ", is InlinedPropsBit = " << InlinedPropsBit::Get(handler) << ", is AccessorBit = " << AccessorBit::Get(handler) << ", OffsetBit = " << OffsetBit::Get(handler) << ", AttrIndexBit = " << AttrIndexBit::Get(handler) << ", SFieldTypeBit = " << static_cast(SFieldTypeBit::Get(handler)); break; case StoreHandlerKind::S_FIELD: os << "S_FIELD" << ", is InlinedPropsBit = " << InlinedPropsBit::Get(handler) << ", is AccessorBit = " << AccessorBit::Get(handler) << ", OffsetBit = " << OffsetBit::Get(handler) << ", AttrIndexBit = " << AttrIndexBit::Get(handler) << ", SFieldTypeBit = " << static_cast(SFieldTypeBit::Get(handler)); break; case StoreHandlerKind::S_ELEMENT: os << "S_ELEMENT" << ", is JSArray = " << IsJSArrayBit::Get(handler) << ", SOutOfBoundsBit = " << SOutOfBoundsBit::Get(handler) << ", SFieldTypeBit = " << static_cast(SFieldTypeBit::Get(handler)); break; default: UNREACHABLE(); } os << std::endl; #endif } } // namespace panda::ecmascript