1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/ic/ic_handler.h"
17 #include "ecmascript/global_env_constants-inl.h"
18
19 namespace panda::ecmascript {
20
UpdateHandlerKind(const JSThread * thread,const ObjectOperator & op,uint64_t & handler)21 void UpdateHandlerKind(const JSThread *thread, const ObjectOperator &op, uint64_t &handler)
22 {
23 JSHandle<JSTaggedValue> receiver = op.GetReceiver();
24 if (receiver->IsString()) {
25 JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString();
26 JSHandle<JSTaggedValue> key = op.GetKey();
27 EcmaString *proKey = key->IsString() ? EcmaString::Cast(key->GetTaggedObject()) : nullptr;
28 if (proKey != nullptr &&
29 EcmaStringAccessor::StringsAreEqual(thread, proKey, EcmaString::Cast(lenKey.GetTaggedObject()))) {
30 HandlerBase::KindBit::Set<uint64_t>(HandlerBase::HandlerKind::STRING_LENGTH, &handler);
31 } else {
32 HandlerBase::KindBit::Set<uint64_t>(HandlerBase::HandlerKind::STRING, &handler);
33 }
34 } else if (receiver->IsNumber()) {
35 HandlerBase::KindBit::Set<uint64_t>(HandlerBase::HandlerKind::NUMBER, &handler);
36 } else if (receiver->IsBoolean()) {
37 HandlerBase::KindBit::Set<uint64_t>(HandlerBase::HandlerKind::BOOLEAN, &handler);
38 } else {
39 HandlerBase::KindBit::Set<uint64_t>(HandlerBase::HandlerKind::FIELD, &handler);
40 }
41 }
42
LoadProperty(const JSThread * thread,const ObjectOperator & op)43 JSHandle<JSTaggedValue> LoadHandler::LoadProperty(const JSThread *thread, const ObjectOperator &op)
44 {
45 uint64_t handler = 0;
46 ASSERT(!op.IsElement());
47 if (!op.IsFound()) {
48 KindBit::Set<uint64_t>(HandlerKind::NON_EXIST, &handler);
49 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
50 }
51 ASSERT(op.IsFastMode());
52
53 JSTaggedValue val = op.GetValue();
54 if (val.IsPropertyBox()) {
55 return JSHandle<JSTaggedValue>(thread, val);
56 }
57 bool hasAccessor = op.IsAccessorDescriptor();
58 AccessorBit::Set<uint64_t>(hasAccessor, &handler);
59
60 if (!hasAccessor) {
61 UpdateHandlerKind(thread, op, handler);
62 }
63
64 if (op.IsInlinedProps()) {
65 InlinedPropsBit::Set<uint64_t>(true, &handler);
66 JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder());
67 auto index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex());
68 OffsetBit::Set<uint64_t>(index, &handler);
69 AttrIndexBit::Set<uint64_t>(op.GetIndex(), &handler);
70 RepresentationBit::Set<uint64_t>(op.GetRepresentation(), &handler);
71 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
72 }
73 if (op.IsFastMode()) {
74 JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder());
75 uint32_t inlinePropNum = holder->GetJSHClass()->GetInlinedProperties();
76 AttrIndexBit::Set<uint64_t>(op.GetIndex() + inlinePropNum, &handler);
77 OffsetBit::Set<uint64_t>(op.GetIndex(), &handler);
78 RepresentationBit::Set<uint64_t>(Representation::TAGGED, &handler);
79 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
80 }
81 LOG_ECMA(FATAL) << "this branch is unreachable";
82 UNREACHABLE();
83 }
84
LoadElement(const JSThread * thread,const ObjectOperator & op)85 JSHandle<JSTaggedValue> LoadHandler::LoadElement(const JSThread *thread, const ObjectOperator &op)
86 {
87 uint64_t handler = 0;
88 KindBit::Set<uint64_t>(HandlerKind::ELEMENT, &handler);
89 // To avoid logical errors and Deopt, temporarily skipping PGO Profiling.
90 // logical errors:
91 // When accessing an element of an object, AOT does not have a chain-climbing operation,
92 // so if the element is on a prototype, it will not be able to get the correct element.
93 // deopt:
94 // Currently there is no way to save the type of the key in pgo file, even if the type of the key
95 // is string, it will be treated as a number type by the AOT, leading to deopt at runtime.
96 if (op.GetReceiver() != op.GetHolder() || op.KeyFromStringType()) {
97 NeedSkipInPGODumpBit::Set<uint64_t>(true, &handler);
98 }
99 if (op.GetReceiver()->IsJSArray()) {
100 IsJSArrayBit::Set<uint64_t>(true, &handler);
101 }
102 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
103 }
104
StoreProperty(const JSThread * thread,const ObjectOperator & op)105 JSHandle<JSTaggedValue> StoreHandler::StoreProperty(const JSThread *thread, const ObjectOperator &op)
106 {
107 uint64_t handler = 0;
108 JSHandle<JSObject> receiver = JSHandle<JSObject>::Cast(op.GetReceiver());
109 SFieldTypeBitSet(thread, op, receiver, &handler);
110 if (op.IsElement()) {
111 SOutOfBoundsBit::Set<uint64_t>(op.GetElementOutOfBounds(), &handler);
112 return StoreElement(thread, op.GetReceiver(), handler);
113 }
114 JSTaggedValue val = op.GetValue();
115 if (val.IsPropertyBox()) {
116 return JSHandle<JSTaggedValue>(thread, val);
117 }
118 bool hasSetter = op.IsAccessorDescriptor();
119 AccessorBit::Set<uint64_t>(hasSetter, &handler);
120 if (!hasSetter) {
121 SKindBit::Set<uint64_t>(StoreHandlerKind::S_FIELD, &handler);
122 }
123 if (op.IsInlinedProps()) {
124 InlinedPropsBit::Set<uint64_t>(true, &handler);
125 uint32_t index = 0;
126 if (!hasSetter) {
127 index = receiver->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex());
128 } else {
129 JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder());
130 index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex());
131 }
132 AttrIndexBit::Set<uint64_t>(op.GetIndex(), &handler);
133 OffsetBit::Set<uint64_t>(index, &handler);
134 RepresentationBit::Set(op.GetRepresentation(), &handler);
135 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
136 }
137 ASSERT(op.IsFastMode());
138 uint32_t inlinePropNum = receiver->GetJSHClass()->GetInlinedProperties();
139 AttrIndexBit::Set<uint64_t>(op.GetIndex() + inlinePropNum, &handler);
140 OffsetBit::Set<uint64_t>(op.GetIndex(), &handler);
141 RepresentationBit::Set(Representation::TAGGED, &handler);
142 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::WrapUint64(handler));
143 }
144
StoreTransition(const JSThread * thread,const ObjectOperator & op)145 JSHandle<JSTaggedValue> TransitionHandler::StoreTransition(const JSThread *thread, const ObjectOperator &op)
146 {
147 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
148 JSHandle<TransitionHandler> handler = factory->NewTransitionHandler();
149 JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op);
150 handler->SetHandlerInfo(thread, handlerInfo);
151 auto hclass = JSObject::Cast(op.GetReceiver()->GetTaggedObject())->GetJSHClass();
152 handler->SetTransitionHClass(thread, JSTaggedValue(hclass));
153 return JSHandle<JSTaggedValue>::Cast(handler);
154 }
155
LoadPrototype(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)156 JSHandle<JSTaggedValue> PrototypeHandler::LoadPrototype(const JSThread *thread, const ObjectOperator &op,
157 const JSHandle<JSHClass> &hclass)
158 {
159 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
160 JSHandle<JSTaggedValue> handlerInfo = LoadHandler::LoadProperty(thread, op);
161 JSHandle<PrototypeHandler> handler = factory->NewPrototypeHandler();
162 handler->SetHandlerInfo(thread, handlerInfo);
163 if (op.IsFound()) {
164 handler->SetHolder(thread, op.GetHolder());
165 } else {
166 // In "Not Found" case we set holder to Undefined().
167 handler->SetHolder(thread, JSTaggedValue::Undefined());
168 }
169 if (op.IsAccessorDescriptor()) {
170 JSTaggedValue result = op.GetValue();
171 if (result.IsPropertyBox()) {
172 result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(thread);
173 }
174 AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject());
175 if (!accessor->IsInternal()) {
176 JSTaggedValue getter = accessor->GetGetter(thread);
177 if (!getter.IsUndefined()) {
178 JSHandle<JSFunction> func(thread, getter);
179 uint32_t methodOffset = Method::Cast(func->GetMethod(thread))->GetMethodId().GetOffset();
180 handler->SetAccessorMethodId(methodOffset);
181 handler->SetAccessorJSFunction(thread, getter);
182 }
183 }
184 }
185 auto result = JSHClass::EnableProtoChangeMarker(thread, hclass);
186 handler->SetProtoCell(thread, result);
187 return JSHandle<JSTaggedValue>::Cast(handler);
188 }
189
StorePrototype(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)190 JSHandle<JSTaggedValue> PrototypeHandler::StorePrototype(const JSThread *thread, const ObjectOperator &op,
191 const JSHandle<JSHClass> &hclass)
192 {
193 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
194 JSHandle<PrototypeHandler> handler = factory->NewPrototypeHandler();
195 JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op);
196 handler->SetHandlerInfo(thread, handlerInfo);
197 handler->SetHolder(thread, op.GetHolder());
198 if (op.IsAccessorDescriptor()) {
199 JSTaggedValue result = op.GetValue();
200 if (result.IsPropertyBox()) {
201 result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(thread);
202 }
203 AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject());
204 if (!accessor->IsInternal() && accessor->HasSetter(thread)) {
205 JSTaggedValue setter = accessor->GetSetter(thread);
206 JSHandle<JSFunction> func(thread, setter);
207 handler->SetAccessorMethodId(
208 Method::Cast(func->GetMethod(thread))->GetMethodId().GetOffset());
209 handler->SetAccessorJSFunction(thread, setter);
210 }
211 }
212 // ShareToLocal is prohibited
213 if (!hclass->IsJSShared()) {
214 auto result = JSHClass::EnableProtoChangeMarker(thread, hclass);
215 handler->SetProtoCell(thread, result);
216 }
217 return JSHandle<JSTaggedValue>::Cast(handler);
218 }
219
StoreTransition(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)220 JSHandle<JSTaggedValue> TransWithProtoHandler::StoreTransition(const JSThread *thread, const ObjectOperator &op,
221 const JSHandle<JSHClass> &hclass)
222 {
223 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
224 JSHandle<TransWithProtoHandler> handler = factory->NewTransWithProtoHandler();
225 JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op);
226 handler->SetHandlerInfo(thread, handlerInfo);
227 auto result = JSHClass::EnableProtoChangeMarker(thread, hclass);
228 handler->SetProtoCell(thread, result);
229 handler->SetTransitionHClass(thread, hclass.GetTaggedValue());
230
231 return JSHandle<JSTaggedValue>::Cast(handler);
232 }
233
PrintLoadHandler(uint64_t handler,std::ostream & os)234 void HandlerBase::PrintLoadHandler([[maybe_unused]] uint64_t handler, [[maybe_unused]] std::ostream& os)
235 {
236 #if ECMASCRIPT_ENABLE_TRACE_IC
237 HandlerKind kind = GetKind(handler);
238 os << "kind = ";
239 switch (kind) {
240 case HandlerKind::NONE:
241 os << "NONE"
242 << ", is InlinedPropsBit = "
243 << InlinedPropsBit::Get(handler)
244 << ", is AccessorBit = "
245 << AccessorBit::Get(handler)
246 << ", OffsetBit = "
247 << OffsetBit::Get(handler)
248 << ", AttrIndexBit = "
249 << AttrIndexBit::Get(handler);
250 break;
251 case HandlerKind::FIELD:
252 os << "FIELD"
253 << ", is InlinedPropsBit = "
254 << InlinedPropsBit::Get(handler)
255 << ", is AccessorBit = "
256 << AccessorBit::Get(handler)
257 << ", OffsetBit = "
258 << OffsetBit::Get(handler)
259 << ", AttrIndexBit = "
260 << AttrIndexBit::Get(handler);
261 break;
262 case HandlerKind::ELEMENT:
263 os << "ELEMENT"
264 << ", is JSArray = "
265 << IsJSArrayBit::Get(handler);
266 break;
267 case HandlerKind::DICTIONARY:
268 os << "DICTIONARY";
269 break;
270 case HandlerKind::STRING:
271 os << "STRING";
272 break;
273 case HandlerKind::STRING_LENGTH:
274 os << "STRING_LENGTH";
275 break;
276 case HandlerKind::TYPED_ARRAY:
277 os << "TYPED_ARRAY,"
278 << "is OnHeap = "
279 << IsOnHeapBit::Get(handler);
280 break;
281 case HandlerKind::NUMBER:
282 os << "NUMBER";
283 break;
284 case HandlerKind::NON_EXIST:
285 os << "NON_EXIST";
286 break;
287 default:
288 UNREACHABLE();
289 }
290 os << std::endl;
291 #endif
292 }
293
PrintStoreHandler(uint64_t handler,std::ostream & os)294 void HandlerBase::PrintStoreHandler([[maybe_unused]] uint64_t handler, [[maybe_unused]] std::ostream& os)
295 {
296 #if ECMASCRIPT_ENABLE_TRACE_IC
297 StoreHandlerKind kind = static_cast<StoreHandlerKind>(GetKind(handler));
298 os << "kind = ";
299 switch (kind) {
300 case StoreHandlerKind::S_NONE:
301 os << "NONE"
302 << ", is InlinedPropsBit = "
303 << InlinedPropsBit::Get(handler)
304 << ", is AccessorBit = "
305 << AccessorBit::Get(handler)
306 << ", OffsetBit = "
307 << OffsetBit::Get(handler)
308 << ", AttrIndexBit = "
309 << AttrIndexBit::Get(handler)
310 << ", SFieldTypeBit = "
311 << static_cast<uint32_t>(SFieldTypeBit::Get(handler));
312 break;
313 case StoreHandlerKind::S_FIELD:
314 os << "S_FIELD"
315 << ", is InlinedPropsBit = "
316 << InlinedPropsBit::Get(handler)
317 << ", is AccessorBit = "
318 << AccessorBit::Get(handler)
319 << ", OffsetBit = "
320 << OffsetBit::Get(handler)
321 << ", AttrIndexBit = "
322 << AttrIndexBit::Get(handler)
323 << ", SFieldTypeBit = "
324 << static_cast<uint32_t>(SFieldTypeBit::Get(handler));
325 break;
326 case StoreHandlerKind::S_ELEMENT:
327 os << "S_ELEMENT"
328 << ", is JSArray = "
329 << IsJSArrayBit::Get(handler)
330 << ", SOutOfBoundsBit = "
331 << SOutOfBoundsBit::Get(handler)
332 << ", SFieldTypeBit = "
333 << static_cast<uint32_t>(SFieldTypeBit::Get(handler));
334 break;
335 default:
336 UNREACHABLE();
337 }
338 os << std::endl;
339 #endif
340 }
341 } // namespace panda::ecmascript