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