• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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