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_OBJECT_OPERATOR_H
17#define ECMASCRIPT_OBJECT_OPERATOR_H
18
19#include "ecmascript/js_handle.h"
20#include "ecmascript/js_object.h"
21#include "ecmascript/js_thread.h"
22#include "ecmascript/property_attributes.h"
23
24#include "ecmascript/ecma_string.h"
25#include "libpandabase/utils/bit_field.h"
26
27namespace panda::ecmascript {
28class PropertyDescriptor;
29class JSObject;
30using SCheckMode = JSShared::SCheckMode;
31
32enum class OperatorType : uint8_t {
33    PROTOTYPE_CHAIN,
34    OWN,
35};
36
37class ObjectOperator final {
38public:
39    ObjectOperator() = default;
40
41    ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &key,
42                   OperatorType type = OperatorType::PROTOTYPE_CHAIN);
43
44    ObjectOperator(JSThread *thread, const JSHandle<JSObject> &holder, const JSHandle<JSTaggedValue> &key,
45                   OperatorType type = OperatorType::PROTOTYPE_CHAIN);
46
47    ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder, const JSHandle<JSTaggedValue> &key,
48                   OperatorType type = OperatorType::PROTOTYPE_CHAIN);
49
50    ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder,
51                   const JSHandle<JSTaggedValue> &receiver, const JSHandle<JSTaggedValue> &key,
52                   OperatorType type = OperatorType::PROTOTYPE_CHAIN);
53
54    ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder, uint32_t index,
55                   OperatorType type = OperatorType::PROTOTYPE_CHAIN);
56    // op for fast path, name can only string and symbol, and can't be number.
57    ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
58                   OperatorType type = OperatorType::PROTOTYPE_CHAIN);
59    // op for fast add
60    ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
61                   const PropertyAttributes &attr);
62
63    static void FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
64                        const JSHandle<JSTaggedValue> &value, const PropertyAttributes &attr);
65
66    void UpdateDetector();
67    static void PUBLIC_API UpdateDetector(const JSThread *thread, JSTaggedValue receiver, JSTaggedValue key);
68    static void UpdateDetectorOnSetPrototype(const JSThread *thread, JSTaggedValue receiver);
69    static bool IsDetectorName(const JSThread *thread, JSTaggedValue key);
70
71    NO_COPY_SEMANTIC(ObjectOperator);
72    DEFAULT_NOEXCEPT_MOVE_SEMANTIC(ObjectOperator);
73    ~ObjectOperator() = default;
74
75    /**
76     * Create ObjectOperator instance by new operator is forbidden, for the member holder is a JSHandle type. it must
77     * be created and destroyed on stack
78     */
79    void *operator new([[maybe_unused]] size_t t) = delete;
80    void operator delete([[maybe_unused]] void *ptr) = delete;
81
82    inline bool IsFound() const
83    {
84        return index_ != NOT_FOUND_INDEX;
85    }
86
87    inline bool IsFastMode() const
88    {
89        return IsFastModeField::Get(metaData_);
90    }
91
92    inline void SetFastMode(bool flag)
93    {
94        IsFastModeField::Set(flag, &metaData_);
95    }
96
97    inline void SetFoundDict(bool flag)
98    {
99        IsFoundDictField::Set(flag, &metaData_);
100    }
101
102    inline bool IsFoundDict()
103    {
104        return IsFoundDictField::Get(metaData_);
105    }
106
107    inline bool IsElement() const
108    {
109        return key_.IsEmpty();
110    }
111
112    inline bool GetThroughElement() const
113    {
114        uint32_t len = EcmaStringAccessor(holder_->GetTaggedObject()).GetLength();
115        bool flag = elementIndex_ < len;
116        return key_.IsEmpty() && flag;
117    }
118
119    inline bool GetStringLength() const
120    {
121        JSTaggedValue lenKey = thread_->GlobalConstants()->GetLengthString();
122        if (GetKey()->IsUndefined() || !GetKey()->IsString()) {
123            return false;
124        }
125        EcmaString *proKey = EcmaString::Cast(GetKey()->GetTaggedObject());
126        return receiver_->IsString() && EcmaStringAccessor::StringsAreEqual(proKey,
127            EcmaString::Cast(lenKey.GetTaggedObject()));
128    }
129
130    inline bool IsOnPrototype() const
131    {
132        return IsOnPrototypeField::Get(metaData_);
133    }
134
135    inline void SetIsOnPrototype(bool flag)
136    {
137        IsOnPrototypeField::Set(flag, &metaData_);
138    }
139
140    inline bool HasReceiver() const
141    {
142        return HasReceiverField::Get(metaData_);
143    }
144
145    inline void SetHasReceiver(bool flag)
146    {
147        HasReceiverField::Set(flag, &metaData_);
148    }
149
150    inline bool IsTransition() const
151    {
152        return IsTransitionField::Get(metaData_);
153    }
154
155    inline void SetIsTransition(bool flag)
156    {
157        IsTransitionField::Set(flag, &metaData_);
158    }
159
160    inline bool IsTSHClass() const
161    {
162        return IsTSHClassField::Get(metaData_);
163    }
164
165    inline void SetIsTSHClass(bool flag)
166    {
167        IsTSHClassField::Set(flag, &metaData_);
168    }
169
170    inline PropertyAttributes GetAttr() const
171    {
172        return attributes_;
173    }
174
175    inline void SetAttr(uint64_t attr)
176    {
177        attributes_ = PropertyAttributes(attr);
178    }
179
180    inline void SetAttr(const PropertyAttributes &attr)
181    {
182        attributes_ = attr;
183    }
184
185    inline bool IsPrimitiveAttr() const
186    {
187        return !attributes_.GetValue();
188    }
189
190    inline bool IsWritable() const
191    {
192        return GetAttr().IsWritable();
193    }
194
195    inline bool IsEnumerable() const
196    {
197        return GetAttr().IsEnumerable();
198    }
199
200    inline bool IsConfigurable() const
201    {
202        return GetAttr().IsConfigurable();
203    }
204
205    inline bool IsAccessorDescriptor() const
206    {
207        return GetAttr().IsAccessor();
208    }
209
210    inline bool IsInlinedProps() const
211    {
212        return GetAttr().IsInlinedProps();
213    }
214
215    inline void SetIsInlinedProps(bool flag)
216    {
217        attributes_.SetIsInlinedProps(flag);
218    }
219
220    inline Representation GetRepresentation() const
221    {
222        return GetAttr().GetRepresentation();
223    }
224
225    inline JSTaggedValue GetValue() const
226    {
227        if (value_.IsEmpty()) {
228            return JSTaggedValue::Undefined();
229        }
230        return value_.GetTaggedValue();
231    }
232
233    JSHandle<JSTaggedValue> FastGetValue();
234    inline void SetValue(JSTaggedValue value)
235    {
236        if (value_.IsEmpty()) {
237            value_ = JSMutableHandle<JSTaggedValue>(thread_, value);
238        }
239        value_.Update(value);
240    }
241
242    inline void SetIndex(uint32_t index)
243    {
244        index_ = index;
245    }
246
247    inline uint32_t GetIndex() const
248    {
249        return index_;
250    }
251
252    inline void SetElementOutOfBounds(bool val)
253    {
254        elementsOutOfBounds_ = val;
255    }
256
257    inline bool GetElementOutOfBounds() const
258    {
259        return elementsOutOfBounds_;
260    }
261
262    inline bool HasHolder() const
263    {
264        return !holder_.IsEmpty();
265    }
266
267    inline JSHandle<JSTaggedValue> GetHolder() const
268    {
269        return holder_;
270    }
271
272    inline JSHandle<JSTaggedValue> GetReceiver() const
273    {
274        return receiver_;
275    }
276
277    inline JSHandle<JSTaggedValue> GetKey() const
278    {
279        if (key_.IsEmpty()) {
280            return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
281        }
282        return key_;
283    }
284
285    inline bool KeyFromStringType() const
286    {
287        return keyFromStringType_;
288    }
289
290    inline uint32_t GetElementIndex() const
291    {
292        return elementIndex_;
293    }
294
295    inline JSThread *GetThread() const
296    {
297        return thread_;
298    }
299
300    void ToPropertyDescriptor(PropertyDescriptor &desc) const;
301    SharedFieldType GetSharedFieldType() const;
302    void LookupProperty();
303    void GlobalLookupProperty();
304    inline void ReLookupPropertyInReceiver()
305    {
306        ResetState();
307        return LookupPropertyInlinedProps(JSHandle<JSObject>(receiver_));
308    }
309    inline void SetAsDefaultAttr()
310    {
311        SetFound(NOT_FOUND_INDEX, JSTaggedValue::Undefined(), PropertyAttributes::GetDefaultAttributes(), false, false);
312    }
313    bool UpdateDataValue(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
314                         bool isInternalAccessor, bool mayThrow = false);
315    bool WriteDataPropertyInHolder(const PropertyDescriptor &desc)
316    {
317        JSHandle<JSObject> receiver(holder_);
318        return WriteDataProperty(receiver, desc);
319    }
320    bool WriteDataProperty(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc);
321    bool AddProperty(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value, PropertyAttributes attr);
322    inline bool AddPropertyInHolder(const JSHandle<JSTaggedValue> &value, PropertyAttributes attr)
323    {
324        JSHandle<JSObject> obj(holder_);
325        return AddProperty(obj, value, attr);
326    }
327    void DeletePropertyInHolder();
328    static constexpr uint32_t NOT_FOUND_INDEX = std::numeric_limits<uint32_t>::max();
329    static JSTaggedValue ToHolder(const JSHandle<JSTaggedValue> &holder);
330    void AddPropertyInternal(const JSHandle<JSTaggedValue> &value);
331    void DefineSetter(const JSHandle<JSTaggedValue> &value);
332    void DefineGetter(const JSHandle<JSTaggedValue> &value);
333
334private:
335    static constexpr uint64_t ATTR_LENGTH = 5;
336    static constexpr uint64_t INDEX_LENGTH = 32;
337
338    using IsFastModeField = BitField<bool, 0, 1>;
339    using IsOnPrototypeField = IsFastModeField::NextFlag;  // 1: on prototype
340    using HasReceiverField = IsOnPrototypeField::NextFlag;
341    using IsTransitionField = HasReceiverField::NextFlag;
342    using IsTSHClassField = IsTransitionField::NextFlag;
343    // found dictionary obj between receriver and holder
344    using IsFoundDictField = IsTSHClassField::NextFlag;
345
346    void UpdateHolder();
347    void UpdateIsTSHClass();
348    void StartLookUp(OperatorType type);
349    void StartGlobalLookUp(OperatorType type);
350    void HandleKey(const JSHandle<JSTaggedValue> &key);
351    uint32_t ComputeElementCapacity(uint32_t oldCapacity);
352    void SetFound(uint32_t index, JSTaggedValue value, uint64_t attr, bool mode, bool transition = false);
353    void UpdateFound(uint32_t index, uint64_t attr, bool mode, bool transition);
354    void ResetState();
355    void ResetStateForAddProperty();
356    inline void LookupPropertyInHolder()
357    {
358        JSHandle<JSObject> obj(holder_);
359        LookupPropertyInlinedProps(obj);
360    }
361    inline void GlobalLookupPropertyInHolder()
362    {
363        JSHandle<JSObject> obj(holder_);
364        LookupGlobal(obj);
365    }
366    void LookupGlobal(const JSHandle<JSObject> &obj);
367    void LookupPropertyInlinedProps(const JSHandle<JSObject> &obj);
368    void LookupElementInlinedProps(const JSHandle<JSObject> &obj);
369    void WriteElement(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc);
370    void WriteElement(const JSHandle<JSObject> &receiver, JSHandle<JSTaggedValue> value) const;
371    void DeleteElementInHolder() const;
372    bool UpdateValueAndDetails(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
373                               PropertyAttributes attr, bool attrChanged);
374    void TransitionForAttributeChanged(const JSHandle<JSObject> &receiver, PropertyAttributes attr);
375    JSThread *thread_{nullptr};
376    JSMutableHandle<JSTaggedValue> value_{};
377    JSMutableHandle<JSTaggedValue> holder_{};
378    JSMutableHandle<JSTaggedValue> receiver_{};
379    JSHandle<JSTaggedValue> key_{};
380    uint32_t elementIndex_{NOT_FOUND_INDEX};
381    uint32_t index_{NOT_FOUND_INDEX};
382    PropertyAttributes attributes_;
383    uint32_t metaData_{0};
384    int receiverHoleEntry_{-1};
385    bool keyFromStringType_{false};
386    bool elementsOutOfBounds_{false};
387};
388}  // namespace panda::ecmascript
389#endif  // ECMASCRIPT_OBJECT_OPERATOR_H
390