• 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 
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