• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 #ifndef ECMASCRIPT_IC_IC_HANDLER_H
17 #define ECMASCRIPT_IC_IC_HANDLER_H
18 
19 #include "ecmascript/ecma_macros.h"
20 #include "ecmascript/js_tagged_value-inl.h"
21 #include "ecmascript/js_typed_array.h"
22 #include "ecmascript/mem/tagged_object.h"
23 
24 namespace panda::ecmascript {
25 class HandlerBase {
26 public:
27     static constexpr uint32_t KIND_BIT_LENGTH = 3;
28     static constexpr uint32_t STORE_KIND_BIT_LENGTH = 2;
29     enum HandlerKind {
30         NONE = 0,
31         FIELD,
32         ELEMENT,
33         DICTIONARY,
34         STRING,
35         STRING_LENGTH,
36         TYPED_ARRAY,
37         NON_EXIST,
38         TOTAL_KINDS,
39     };
40 
41     // Store Handler kind combined with KindBit called SWholeKindBit. Which used to quickly check S_FIELD kind
42     enum StoreHandlerKind {
43         S_NONE = HandlerKind::NONE,
44         S_FIELD,
45         S_ELEMENT,
46         S_TOTAL_KINDS,
47     };
48 
49     // For Load
50     using KindBit = BitField<HandlerKind, 0, KIND_BIT_LENGTH>;
51     using InlinedPropsBit = KindBit::NextFlag;
52     using AccessorBit = InlinedPropsBit::NextFlag;
53     using InternalAccessorBit = AccessorBit::NextFlag;
54     using IsJSArrayBit = InternalAccessorBit::NextFlag;
55     using OffsetBit = IsJSArrayBit::NextField<uint32_t, PropertyAttributes::OFFSET_BITFIELD_NUM>;
56     using RepresentationBit = OffsetBit::NextField<Representation, PropertyAttributes::REPRESENTATION_NUM>;
57     using AttrIndexBit = RepresentationBit::NextField<uint32_t, PropertyAttributes::OFFSET_BITFIELD_NUM>;
58     using IsOnHeapBit = AttrIndexBit::NextFlag;
59     using NeedSkipInPGODumpBit = IsOnHeapBit::NextFlag;
60     static_assert(static_cast<size_t>(HandlerKind::TOTAL_KINDS) <= (1 << KIND_BIT_LENGTH));
61 
62     // For Store
63     using SKindBit = BitField<StoreHandlerKind, 0, STORE_KIND_BIT_LENGTH>;
64     using SSharedBit = SKindBit::NextFlag;
65     using SWholeKindBit = KindBit;
66     static_assert(SKindBit::START_BIT == SWholeKindBit::START_BIT);
67     static_assert(SSharedBit::Mask() || SKindBit::Mask() == KindBit::Mask());
68     using STrackTypeBit = AttrIndexBit::NextField<uint32_t, PropertyAttributes::TRACK_TYPE_NUM>;
69     static_assert(static_cast<size_t>(StoreHandlerKind::S_TOTAL_KINDS) <= (1 << STORE_KIND_BIT_LENGTH));
70 
71     HandlerBase() = default;
72     virtual ~HandlerBase() = default;
73 
IsAccessor(uint32_t handler)74     static inline bool IsAccessor(uint32_t handler)
75     {
76         return AccessorBit::Get(handler);
77     }
78 
IsInternalAccessor(uint32_t handler)79     static inline bool IsInternalAccessor(uint32_t handler)
80     {
81         return InternalAccessorBit::Get(handler);
82     }
83 
GetTrackType(uint32_t handler)84     static inline TrackType GetTrackType(uint32_t handler)
85     {
86         return static_cast<TrackType>(STrackTypeBit::Get(handler));
87     }
88 
IsNonExist(uint32_t handler)89     static inline bool IsNonExist(uint32_t handler)
90     {
91         return GetKind(handler) == HandlerKind::NON_EXIST;
92     }
93 
IsField(uint32_t handler)94     static inline bool IsField(uint32_t handler)
95     {
96         return GetKind(handler) == HandlerKind::FIELD;
97     }
98 
IsNonSharedStoreField(uint32_t handler)99     static inline bool IsNonSharedStoreField(uint32_t handler)
100     {
101         return static_cast<StoreHandlerKind>(GetKind(handler)) == StoreHandlerKind::S_FIELD;
102     }
103 
IsStoreShared(uint32_t handler)104     static inline bool IsStoreShared(uint32_t handler)
105     {
106         return SSharedBit::Get(handler);
107     }
108 
ClearSharedStoreKind(uint32_t & handler)109     static inline void ClearSharedStoreKind(uint32_t &handler)
110     {
111         SSharedBit::Set<uint32_t>(false, &handler);
112     }
113 
IsString(uint32_t handler)114     static inline bool IsString(uint32_t handler)
115     {
116         return GetKind(handler) == HandlerKind::STRING;
117     }
118 
IsStringLength(uint32_t handler)119     static inline bool IsStringLength(uint32_t handler)
120     {
121         return GetKind(handler) == HandlerKind::STRING_LENGTH;
122     }
123 
IsElement(uint32_t handler)124     static inline bool IsElement(uint32_t handler)
125     {
126         return IsNormalElement(handler) || IsStringElement(handler) || IsTypedArrayElement(handler);
127     }
128 
IsNormalElement(uint32_t handler)129     static inline bool IsNormalElement(uint32_t handler)
130     {
131         return GetKind(handler) == HandlerKind::ELEMENT;
132     }
133 
IsStringElement(uint32_t handler)134     static inline bool IsStringElement(uint32_t handler)
135     {
136         return GetKind(handler) == HandlerKind::STRING;
137     }
138 
IsTypedArrayElement(uint32_t handler)139     static inline bool IsTypedArrayElement(uint32_t handler)
140     {
141         return GetKind(handler) == HandlerKind::TYPED_ARRAY;
142     }
143 
IsDictionary(uint32_t handler)144     static inline bool IsDictionary(uint32_t handler)
145     {
146         return GetKind(handler) == HandlerKind::DICTIONARY;
147     }
148 
IsInlinedProps(uint32_t handler)149     static inline bool IsInlinedProps(uint32_t handler)
150     {
151         return InlinedPropsBit::Get(handler);
152     }
153 
GetKind(uint32_t handler)154     static inline HandlerKind GetKind(uint32_t handler)
155     {
156         return KindBit::Get(handler);
157     }
158 
IsJSArray(uint32_t handler)159     static inline bool IsJSArray(uint32_t handler)
160     {
161         return IsJSArrayBit::Get(handler);
162     }
163 
NeedSkipInPGODump(uint32_t handler)164     static inline bool NeedSkipInPGODump(uint32_t handler)
165     {
166         return NeedSkipInPGODumpBit::Get(handler);
167     }
168 
GetOffset(uint32_t handler)169     static inline int GetOffset(uint32_t handler)
170     {
171         return OffsetBit::Get(handler);
172     }
173 
IsOnHeap(uint32_t handler)174     static inline bool IsOnHeap(uint32_t handler)
175     {
176         return IsOnHeapBit::Get(handler);
177     }
178 };
179 
180 class LoadHandler final : public HandlerBase {
181 public:
LoadProperty(const JSThread * thread,const ObjectOperator & op)182     static inline JSHandle<JSTaggedValue> LoadProperty(const JSThread *thread, const ObjectOperator &op)
183     {
184         uint32_t handler = 0;
185         ASSERT(!op.IsElement());
186         if (!op.IsFound()) {
187             KindBit::Set<uint32_t>(HandlerKind::NON_EXIST, &handler);
188             return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler));
189         }
190         ASSERT(op.IsFastMode());
191 
192         JSTaggedValue val = op.GetValue();
193         if (val.IsPropertyBox()) {
194             return JSHandle<JSTaggedValue>(thread, val);
195         }
196         bool hasAccessor = op.IsAccessorDescriptor();
197         AccessorBit::Set<uint32_t>(hasAccessor, &handler);
198 
199         if (!hasAccessor) {
200             if (op.GetReceiver()->IsString()) {
201                 JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString();
202                 EcmaString *proKey = nullptr;
203                 if (op.GetKey()->IsString()) {
204                     proKey = EcmaString::Cast(op.GetKey()->GetTaggedObject());
205                 }
206                 if (EcmaStringAccessor::StringsAreEqual(proKey, EcmaString::Cast(lenKey.GetTaggedObject()))) {
207                     KindBit::Set<uint32_t>(HandlerKind::STRING_LENGTH, &handler);
208                 } else {
209                     KindBit::Set<uint32_t>(HandlerKind::STRING, &handler);
210                 }
211             } else {
212                 KindBit::Set<uint32_t>(HandlerKind::FIELD, &handler);
213             }
214         }
215 
216         if (op.IsInlinedProps()) {
217             InlinedPropsBit::Set<uint32_t>(true, &handler);
218             JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder());
219             auto index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex());
220             OffsetBit::Set<uint32_t>(index, &handler);
221             AttrIndexBit::Set<uint32_t>(op.GetIndex(), &handler);
222             RepresentationBit::Set(op.GetRepresentation(), &handler);
223             return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler));
224         }
225         if (op.IsFastMode()) {
226             JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder());
227             uint32_t inlinePropNum = holder->GetJSHClass()->GetInlinedProperties();
228             AttrIndexBit::Set<uint32_t>(op.GetIndex() + inlinePropNum, &handler);
229             OffsetBit::Set<uint32_t>(op.GetIndex(), &handler);
230             RepresentationBit::Set(Representation::TAGGED, &handler);
231             return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler));
232         }
233         LOG_ECMA(FATAL) << "this branch is unreachable";
234         UNREACHABLE();
235     }
236 
LoadElement(const JSThread * thread,const ObjectOperator & op)237     static inline JSHandle<JSTaggedValue> LoadElement(const JSThread *thread, const ObjectOperator &op)
238     {
239         uint32_t handler = 0;
240         KindBit::Set<uint32_t>(HandlerKind::ELEMENT, &handler);
241 
242         // To avoid logical errors and Deopt, temporarily skipping PGO Profiling.
243         // logical errors:
244         //     When accessing an element of an object, AOT does not have a chain-climbing operation,
245         //     so if the element is on a prototype, it will not be able to get the correct element.
246         // deopt:
247         //     Currently there is no way to save the type of the key in pgo file, even if the type of the key
248         //     is string, it will be treated as a number type by the AOT, leading to deopt at runtime.
249         if (op.GetReceiver() != op.GetHolder() ||
250             op.KeyFromStringType()) {
251             NeedSkipInPGODumpBit::Set<uint32_t>(true, &handler);
252         }
253 
254         if (op.GetReceiver()->IsJSArray()) {
255             IsJSArrayBit::Set<uint32_t>(true, &handler);
256         }
257         return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler));
258     }
259 
LoadStringElement(const JSThread * thread)260     static inline JSHandle<JSTaggedValue> LoadStringElement(const JSThread *thread)
261     {
262         uint32_t handler = 0;
263         KindBit::Set<uint32_t>(HandlerKind::STRING, &handler);
264         return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler));
265     }
266 
LoadTypedArrayElement(const JSThread * thread,JSHandle<JSTypedArray> typedArray)267     static inline JSHandle<JSTaggedValue> LoadTypedArrayElement(const JSThread *thread,
268                                                                 JSHandle<JSTypedArray> typedArray)
269     {
270         uint32_t handler = 0;
271         KindBit::Set<uint32_t>(HandlerKind::TYPED_ARRAY, &handler);
272         IsOnHeapBit::Set<uint32_t>(JSHandle<TaggedObject>(typedArray)->GetClass()->IsOnHeapFromBitField(), &handler);
273         return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler));
274     }
275 };
276 
277 class StoreHandler final : public HandlerBase {
278 public:
StoreProperty(const JSThread * thread,const ObjectOperator & op)279     static inline JSHandle<JSTaggedValue> StoreProperty(const JSThread *thread, const ObjectOperator &op)
280     {
281         uint32_t handler = 0;
282         JSHandle<JSObject> receiver = JSHandle<JSObject>::Cast(op.GetReceiver());
283         SSharedBit::Set<uint32_t>(op.GetReceiver()->IsJSShared(), &handler);
284         TaggedArray *array = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject());
285         if (!array->IsDictionaryMode()) {
286             STrackTypeBit::Set(static_cast<uint32_t>(op.GetAttr().GetTrackType()), &handler);
287         } else {
288             STrackTypeBit::Set(static_cast<uint32_t>(op.GetAttr().GetDictTrackType()), &handler);
289         }
290         if (op.IsElement()) {
291             return StoreElement(thread, op.GetReceiver(), handler);
292         }
293         JSTaggedValue val = op.GetValue();
294         if (val.IsPropertyBox()) {
295             return JSHandle<JSTaggedValue>(thread, val);
296         }
297         bool hasSetter = op.IsAccessorDescriptor();
298         AccessorBit::Set<uint32_t>(hasSetter, &handler);
299         if (!hasSetter) {
300             SKindBit::Set<uint32_t>(StoreHandlerKind::S_FIELD, &handler);
301         }
302         if (op.IsInlinedProps()) {
303             InlinedPropsBit::Set<uint32_t>(true, &handler);
304             uint32_t index = 0;
305             if (!hasSetter) {
306                 index = receiver->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex());
307             } else {
308                 JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder());
309                 index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex());
310             }
311             AttrIndexBit::Set<uint32_t>(op.GetIndex(), &handler);
312             OffsetBit::Set<uint32_t>(index, &handler);
313             RepresentationBit::Set(op.GetRepresentation(), &handler);
314             return JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<int32_t>(handler)));
315         }
316         ASSERT(op.IsFastMode());
317         uint32_t inlinePropNum = receiver->GetJSHClass()->GetInlinedProperties();
318         AttrIndexBit::Set<uint32_t>(op.GetIndex() + inlinePropNum, &handler);
319         OffsetBit::Set<uint32_t>(op.GetIndex(), &handler);
320         RepresentationBit::Set(Representation::TAGGED, &handler);
321         return JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<int32_t>(handler)));
322     }
323 
StoreElement(const JSThread * thread,JSHandle<JSTaggedValue> receiver,uint32_t handler)324     static inline JSHandle<JSTaggedValue> StoreElement(const JSThread *thread,
325                                                        JSHandle<JSTaggedValue> receiver, uint32_t handler)
326     {
327         SKindBit::Set<uint32_t>(StoreHandlerKind::S_ELEMENT, &handler);
328 
329         if (receiver->IsJSArray()) {
330             IsJSArrayBit::Set<uint32_t>(true, &handler);
331         }
332         return JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<int32_t>(handler)));
333     }
334 };
335 
336 class TransitionHandler : public TaggedObject {
337 public:
Cast(TaggedObject * object)338     static TransitionHandler *Cast(TaggedObject *object)
339     {
340         ASSERT(JSTaggedValue(object).IsTransitionHandler());
341         return static_cast<TransitionHandler *>(object);
342     }
343 
StoreTransition(const JSThread * thread,const ObjectOperator & op)344     static inline JSHandle<JSTaggedValue> StoreTransition(const JSThread *thread, const ObjectOperator &op)
345     {
346         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
347         JSHandle<TransitionHandler> handler = factory->NewTransitionHandler();
348         JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op);
349         handler->SetHandlerInfo(thread, handlerInfo);
350         auto hclass = JSObject::Cast(op.GetReceiver()->GetTaggedObject())->GetJSHClass();
351         handler->SetTransitionHClass(thread, JSTaggedValue(hclass));
352         return JSHandle<JSTaggedValue>::Cast(handler);
353     }
354 
355     static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize();
356 
357     ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET)
358 
359     ACCESSORS(TransitionHClass, TRANSITION_HCLASS_OFFSET, SIZE)
360 
361     DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE)
362     DECL_DUMP()
363 };
364 
365 class PrototypeHandler : public TaggedObject {
366 public:
Cast(TaggedObject * object)367     static PrototypeHandler *Cast(TaggedObject *object)
368     {
369         ASSERT(JSTaggedValue(object).IsPrototypeHandler());
370         return static_cast<PrototypeHandler *>(object);
371     }
372 
LoadPrototype(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)373     static inline JSHandle<JSTaggedValue> LoadPrototype(const JSThread *thread, const ObjectOperator &op,
374                                                         const JSHandle<JSHClass> &hclass)
375     {
376         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
377         JSHandle<JSTaggedValue> handlerInfo = LoadHandler::LoadProperty(thread, op);
378         JSHandle<PrototypeHandler> handler = factory->NewPrototypeHandler();
379         handler->SetHandlerInfo(thread, handlerInfo);
380         if (op.IsFound()) {
381             handler->SetHolder(thread, op.GetHolder());
382         }
383         auto result = JSHClass::EnableProtoChangeMarker(thread, hclass);
384         handler->SetProtoCell(thread, result);
385         return JSHandle<JSTaggedValue>::Cast(handler);
386     }
StorePrototype(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)387     static inline JSHandle<JSTaggedValue> StorePrototype(const JSThread *thread, const ObjectOperator &op,
388                                                          const JSHandle<JSHClass> &hclass)
389     {
390         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
391         JSHandle<PrototypeHandler> handler = factory->NewPrototypeHandler();
392         JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op);
393         handler->SetHandlerInfo(thread, handlerInfo);
394         handler->SetHolder(thread, op.GetHolder());
395         auto result = JSHClass::EnableProtoChangeMarker(thread, hclass);
396         handler->SetProtoCell(thread, result);
397         return JSHandle<JSTaggedValue>::Cast(handler);
398     }
399 
400     static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize();
401 
402     ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET)
403 
404     ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, HOLDER_OFFSET)
405 
406     ACCESSORS(Holder, HOLDER_OFFSET, SIZE)
407 
408     DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE)
409     DECL_DUMP()
410 };
411 
412 class TransWithProtoHandler : public TaggedObject {
413 public:
Cast(TaggedObject * object)414     static TransWithProtoHandler *Cast(TaggedObject *object)
415     {
416         ASSERT(JSTaggedValue(object).IsTransWithProtoHandler());
417         return static_cast<TransWithProtoHandler *>(object);
418     }
419 
StoreTransition(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)420     static inline JSHandle<JSTaggedValue> StoreTransition(const JSThread *thread, const ObjectOperator &op,
421                                                           const JSHandle<JSHClass> &hclass)
422     {
423         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
424         JSHandle<TransWithProtoHandler> handler = factory->NewTransWithProtoHandler();
425         JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op);
426         handler->SetHandlerInfo(thread, handlerInfo);
427         auto result = JSHClass::EnableProtoChangeMarker(thread, hclass);
428         handler->SetProtoCell(thread, result);
429         handler->SetTransitionHClass(thread, hclass.GetTaggedValue());
430 
431         return JSHandle<JSTaggedValue>::Cast(handler);
432     }
433 
434     static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize();
435 
436     ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET)
437 
438     ACCESSORS(TransitionHClass, TRANSITION_HCLASS_OFFSET, PROTO_CELL_OFFSET)
439 
440     ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, SIZE)
441 
442     DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE)
443     DECL_DUMP()
444 };
445 
446 class StoreTSHandler : public TaggedObject {
447 public:
Cast(TaggedObject * object)448     static StoreTSHandler *Cast(TaggedObject *object)
449     {
450         ASSERT(JSTaggedValue(object).IsStoreTSHandler());
451         return static_cast<StoreTSHandler *>(object);
452     }
453 
StoreAOT(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)454     static inline JSHandle<JSTaggedValue> StoreAOT(const JSThread *thread, const ObjectOperator &op,
455                                                    const JSHandle<JSHClass> &hclass)
456     {
457         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
458         JSHandle<StoreTSHandler> handler = factory->NewStoreTSHandler();
459         JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op);
460         handler->SetHandlerInfo(thread, handlerInfo);
461         handler->SetHolder(thread, op.GetHolder());
462         auto result = JSHClass::EnableProtoChangeMarker(thread, hclass);
463         handler->SetProtoCell(thread, result);
464         return JSHandle<JSTaggedValue>::Cast(handler);
465     }
466 
467     static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize();
468 
469     ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET)
470 
471     ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, HOLDER_OFFSET)
472 
473     ACCESSORS(Holder, HOLDER_OFFSET, SIZE)
474 
475     DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE)
476     DECL_DUMP()
477 };
478 }  // namespace panda::ecmascript
479 #endif  // ECMASCRIPT_IC_IC_HANDLER_H
480