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