* 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<JSTaggedValue> receiver = op.GetReceiver();
if (receiver->IsString()) {
JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString();
JSHandle<JSTaggedValue> 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<uint64_t>(HandlerBase::HandlerKind::STRING_LENGTH, &handler);
} else {
HandlerBase::KindBit::Set<uint64_t>(HandlerBase::HandlerKind::STRING, &handler);
}
} else if (receiver->IsNumber()) {
HandlerBase::KindBit::Set<uint64_t>(HandlerBase::HandlerKind::NUMBER, &handler);
} else if (receiver->IsBoolean()) {
HandlerBase::KindBit::Set<uint64_t>(HandlerBase::HandlerKind::BOOLEAN, &handler);
} else {
HandlerBase::KindBit::Set<uint64_t>(HandlerBase::HandlerKind::FIELD, &handler);
}
}
JSHandle<JSTaggedValue> LoadHandler::LoadProperty(const JSThread *thread, const ObjectOperator &op)
{
uint64_t handler = 0;
ASSERT(!op.IsElement());
if (!op.IsFound()) {
KindBit::Set<uint64_t>(HandlerKind::NON_EXIST, &handler);
return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
}
ASSERT(op.IsFastMode());
JSTaggedValue val = op.GetValue();
if (val.IsPropertyBox()) {
return JSHandle<JSTaggedValue>(thread, val);
}
bool hasAccessor = op.IsAccessorDescriptor();
AccessorBit::Set<uint64_t>(hasAccessor, &handler);
if (!hasAccessor) {
UpdateHandlerKind(thread, op, handler);
}
if (op.IsInlinedProps()) {
InlinedPropsBit::Set<uint64_t>(true, &handler);
JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder());
auto index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex());
OffsetBit::Set<uint64_t>(index, &handler);
AttrIndexBit::Set<uint64_t>(op.GetIndex(), &handler);
RepresentationBit::Set<uint64_t>(op.GetRepresentation(), &handler);
return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
}
if (op.IsFastMode()) {
JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder());
uint32_t inlinePropNum = holder->GetJSHClass()->GetInlinedProperties();
AttrIndexBit::Set<uint64_t>(op.GetIndex() + inlinePropNum, &handler);
OffsetBit::Set<uint64_t>(op.GetIndex(), &handler);
RepresentationBit::Set<uint64_t>(Representation::TAGGED, &handler);
return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
}
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
JSHandle<JSTaggedValue> LoadHandler::LoadElement(const JSThread *thread, const ObjectOperator &op)
{
uint64_t handler = 0;
KindBit::Set<uint64_t>(HandlerKind::ELEMENT, &handler);
if (op.GetReceiver() != op.GetHolder() || op.KeyFromStringType()) {
NeedSkipInPGODumpBit::Set<uint64_t>(true, &handler);
}
if (op.GetReceiver()->IsJSArray()) {
IsJSArrayBit::Set<uint64_t>(true, &handler);
}
return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
}
JSHandle<JSTaggedValue> StoreHandler::StoreProperty(const JSThread *thread, const ObjectOperator &op)
{
uint64_t handler = 0;
JSHandle<JSObject> receiver = JSHandle<JSObject>::Cast(op.GetReceiver());
SFieldTypeBitSet(thread, op, receiver, &handler);
if (op.IsElement()) {
SOutOfBoundsBit::Set<uint64_t>(op.GetElementOutOfBounds(), &handler);
return StoreElement(thread, op.GetReceiver(), handler);
}
JSTaggedValue val = op.GetValue();
if (val.IsPropertyBox()) {
return JSHandle<JSTaggedValue>(thread, val);
}
bool hasSetter = op.IsAccessorDescriptor();
AccessorBit::Set<uint64_t>(hasSetter, &handler);
if (!hasSetter) {
SKindBit::Set<uint64_t>(StoreHandlerKind::S_FIELD, &handler);
}
if (op.IsInlinedProps()) {
InlinedPropsBit::Set<uint64_t>(true, &handler);
uint32_t index = 0;
if (!hasSetter) {
index = receiver->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex());
} else {
JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder());
index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex());
}
AttrIndexBit::Set<uint64_t>(op.GetIndex(), &handler);
OffsetBit::Set<uint64_t>(index, &handler);
RepresentationBit::Set(op.GetRepresentation(), &handler);
return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
}
ASSERT(op.IsFastMode());
uint32_t inlinePropNum = receiver->GetJSHClass()->GetInlinedProperties();
AttrIndexBit::Set<uint64_t>(op.GetIndex() + inlinePropNum, &handler);
OffsetBit::Set<uint64_t>(op.GetIndex(), &handler);
RepresentationBit::Set(Representation::TAGGED, &handler);
return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
}
JSHandle<JSTaggedValue> TransitionHandler::StoreTransition(const JSThread *thread, const ObjectOperator &op)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TransitionHandler> handler = factory->NewTransitionHandler();
JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op);
handler->SetHandlerInfo(thread, handlerInfo);
auto hclass = JSObject::Cast(op.GetReceiver()->GetTaggedObject())->GetJSHClass();
handler->SetTransitionHClass(thread, JSTaggedValue(hclass));
return JSHandle<JSTaggedValue>::Cast(handler);
}
JSHandle<JSTaggedValue> PrototypeHandler::LoadPrototype(const JSThread *thread, const ObjectOperator &op,
const JSHandle<JSHClass> &hclass)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> handlerInfo = LoadHandler::LoadProperty(thread, op);
JSHandle<PrototypeHandler> handler = factory->NewPrototypeHandler();
handler->SetHandlerInfo(thread, handlerInfo);
if (op.IsFound()) {
handler->SetHolder(thread, op.GetHolder());
} else {
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<JSFunction> func;
if (LIKELY(getter.IsJSFunction())) {
func = JSHandle<JSFunction>(thread, getter);
} else if (getter.IsJSProxy()) {
JSHandle<JSTaggedValue> target(thread, JSProxy::Cast(getter)->GetTarget(thread));
func = JSHandle<JSFunction>::Cast(target);
} else {
ASSERT(getter.IsBoundFunction());
JSHandle<JSTaggedValue> target(thread, JSBoundFunction::Cast(getter)->GetBoundTarget(thread));
func = JSHandle<JSFunction>::Cast(target);
}
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<JSTaggedValue>::Cast(handler);
}
JSHandle<JSTaggedValue> PrototypeHandler::StorePrototype(const JSThread *thread, const ObjectOperator &op,
const JSHandle<JSHClass> &hclass)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<PrototypeHandler> handler = factory->NewPrototypeHandler();
JSHandle<JSTaggedValue> 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<JSFunction> func(thread, setter);
handler->SetAccessorMethodId(
Method::Cast(func->GetMethod(thread))->GetMethodId().GetOffset());
handler->SetAccessorJSFunction(thread, setter);
}
}
if (!hclass->IsJSShared()) {
auto result = JSHClass::EnableProtoChangeMarker(thread, hclass);
handler->SetProtoCell(thread, result);
}
return JSHandle<JSTaggedValue>::Cast(handler);
}
JSHandle<JSTaggedValue> TransWithProtoHandler::StoreTransition(const JSThread *thread, const ObjectOperator &op,
const JSHandle<JSHClass> &hclass)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TransWithProtoHandler> handler = factory->NewTransWithProtoHandler();
JSHandle<JSTaggedValue> 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<JSTaggedValue>::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<StoreHandlerKind>(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<uint32_t>(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<uint32_t>(SFieldTypeBit::Get(handler));
break;
case StoreHandlerKind::S_ELEMENT:
os << "S_ELEMENT"
<< ", is JSArray = "
<< IsJSArrayBit::Get(handler)
<< ", SOutOfBoundsBit = "
<< SOutOfBoundsBit::Get(handler)
<< ", SFieldTypeBit = "
<< static_cast<uint32_t>(SFieldTypeBit::Get(handler));
break;
default:
UNREACHABLE();
}
os << std::endl;
#endif
}
}