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_GLOBAL_ENV_H
17#define ECMASCRIPT_GLOBAL_ENV_H
18
19#include "ecmascript/accessor_data.h"
20#include "ecmascript/js_global_object.h"
21#include "ecmascript/js_thread.h"
22#include "ecmascript/global_env_constants-inl.h"
23#include "ecmascript/js_handle.h"
24#include "ecmascript/global_env_fields.h"
25#include "ecmascript/snapshot/mem/snapshot_env.h"
26
27namespace panda::ecmascript {
28class JSThread;
29class GlobalEnv : public TaggedObject {
30public:
31    using Field = GlobalEnvField;
32
33#define GLOBAL_ENV_SLOT(type, name, index) \
34    static constexpr uint16_t index = static_cast<uint16_t>(GlobalEnvField::index);
35
36    GLOBAL_ENV_FIELDS(GLOBAL_ENV_SLOT)
37    static constexpr uint16_t FIRST_DETECTOR_SYMBOL_INDEX = static_cast<uint16_t>(Field::REPLACE_SYMBOL_INDEX);
38    static constexpr uint16_t LAST_DETECTOR_SYMBOL_INDEX = static_cast<uint16_t>(Field::SPECIES_SYMBOL_INDEX);
39    static constexpr uint16_t FINAL_INDEX = static_cast<uint16_t>(GlobalEnvField::FINAL_INDEX);
40    static constexpr uint8_t RESERVED_LENGTH = 1; // divide the gc area
41    static constexpr uint16_t JSTHREAD_INDEX = FINAL_INDEX; // not need gc
42#undef GLOBAL_ENV_SLOT
43
44    JSTaggedValue GetGlobalObject() const
45    {
46        return GetJSGlobalObject().GetTaggedValue();
47    }
48
49    uintptr_t ComputeObjectAddress(size_t index) const
50    {
51        return reinterpret_cast<uintptr_t>(this) + HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize();
52    }
53
54    JSHandle<JSTaggedValue> GetGlobalEnvObjectByIndex(size_t index) const
55    {
56        ASSERT(index < FINAL_INDEX);
57        uintptr_t address = ComputeObjectAddress(index);
58        JSHandle<JSTaggedValue> result(address);
59        return result;
60    }
61
62    JSHandle<JSTaggedValue> GetNoLazyEnvObjectByIndex(size_t index) const
63    {
64        JSHandle<JSTaggedValue> result = GetGlobalEnvObjectByIndex(index);
65        if (result->IsInternalAccessor()) {
66            JSThread *thread = GetJSThread();
67            AccessorData *accessor = AccessorData::Cast(result->GetTaggedObject());
68            accessor->CallInternalGet(thread, JSHandle<JSObject>::Cast(GetJSGlobalObject()));
69        }
70        return result;
71    }
72
73    size_t GetGlobalEnvFieldSize() const
74    {
75        return FINAL_INDEX;
76    }
77
78    void Init(JSThread *thread);
79
80    static GlobalEnv *Cast(TaggedObject *object)
81    {
82        ASSERT(JSTaggedValue(object).IsJSGlobalEnv());
83        return reinterpret_cast<GlobalEnv *>(object);
84    }
85
86    JSThread* GetJSThread() const
87    {
88        uintptr_t address = ComputeObjectAddress(JSTHREAD_INDEX);
89        return *reinterpret_cast<JSThread**>(address);
90    }
91
92    void SetJSThread(JSThread *thread)
93    {
94        uintptr_t address = ComputeObjectAddress(JSTHREAD_INDEX);
95        *reinterpret_cast<JSThread**>(address) = thread;
96    }
97
98    // For work serialize, add initialized global env object to snapshot env map
99    void AddValueToSnapshotEnv(const JSThread *thread, JSTaggedValue value, uint16_t index, uint32_t offset)
100    {
101        if (!value.IsInternalAccessor()) {
102            SnapshotEnv *snapshotEnv = thread->GetEcmaVM()->GetSnapshotEnv();
103            if (!RemoveValueFromSnapshotEnv(snapshotEnv, value, offset)) {
104                return;
105            }
106            size_t globalConstCount = thread->GlobalConstants()->GetConstantCount();
107            snapshotEnv->Push(value.GetRawData(), index + globalConstCount);
108        }
109    }
110
111    // For work serialize, remove old value from snapshot env map
112    bool RemoveValueFromSnapshotEnv(SnapshotEnv *snapshotEnv, JSTaggedValue value, uint32_t offset)
113    {
114        JSTaggedValue oldValue(Barriers::GetValue<JSTaggedType>(this, offset));
115        if (oldValue == value) {
116            return false;
117        }
118        if (oldValue.IsHeapObject() && !oldValue.IsInternalAccessor()) {
119            // Remove old value
120            snapshotEnv->Remove(oldValue.GetRawData());
121        }
122        return true;
123    }
124
125    JSHandle<JSTaggedValue> GetSymbol(JSThread *thread, const JSHandle<JSTaggedValue> &string);
126    JSHandle<JSTaggedValue> GetStringFunctionByName(JSThread *thread, const char *name);
127    JSHandle<JSTaggedValue> GetStringPrototypeFunctionByName(JSThread *thread, const char *name);
128
129    static inline uintptr_t GetFirstDetectorSymbolAddr(const GlobalEnv *env)
130    {
131        constexpr size_t offset = HEADER_SIZE + FIRST_DETECTOR_SYMBOL_INDEX * JSTaggedValue::TaggedTypeSize();
132        uintptr_t addr = reinterpret_cast<uintptr_t>(env) + offset;
133        return *reinterpret_cast<uintptr_t *>(addr);
134    }
135
136    static uintptr_t GetLastDetectorSymbolAddr(const GlobalEnv *env)
137    {
138        constexpr size_t offset = HEADER_SIZE + LAST_DETECTOR_SYMBOL_INDEX * JSTaggedValue::TaggedTypeSize();
139        uintptr_t addr = reinterpret_cast<uintptr_t>(env) + offset;
140        return *reinterpret_cast<uintptr_t *>(addr);
141    }
142
143// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
144#define GLOBAL_ENV_FIELD_ACCESSORS(type, name, index)                                                   \
145    inline JSHandle<type> Get##name() const                                                             \
146    {                                                                                                   \
147        const uintptr_t address =                                                                       \
148            reinterpret_cast<uintptr_t>(this) + HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize();  \
149        JSHandle<type> result(address);                                                                 \
150        if (result.GetTaggedValue().IsInternalAccessor()) {                                             \
151            JSThread *thread = GetJSThread();                                                           \
152            AccessorData *accessor = AccessorData::Cast(result.GetTaggedValue().GetTaggedObject());     \
153            accessor->CallInternalGet(thread, JSHandle<JSObject>::Cast(GetJSGlobalObject()));           \
154        }                                                                                               \
155        return result;                                                                                  \
156    }                                                                                                   \
157    inline JSTaggedValue GetTagged##name() const                                                        \
158    {                                                                                                   \
159        uint32_t offset = HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize();                        \
160        JSTaggedValue result(Barriers::GetValue<JSTaggedType>(this, offset));                           \
161        if (result.IsInternalAccessor()) {                                                              \
162            JSThread *thread = GetJSThread();                                                           \
163            AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject());                      \
164            accessor->CallInternalGet(thread, JSHandle<JSObject>::Cast(GetJSGlobalObject()));           \
165        }                                                                                               \
166        return result;                                                                                  \
167    }                                                                                                   \
168    inline JSHandle<type> GetRaw##name() const                                                          \
169    {                                                                                                   \
170        const uintptr_t address =                                                                       \
171            reinterpret_cast<uintptr_t>(this) + HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize();  \
172        JSHandle<type> result(address);                                                                 \
173        return result;                                                                                  \
174    }                                                                                                   \
175    template<typename T>                                                                                \
176    inline void Set##name(const JSThread *thread, JSHandle<T> value, BarrierMode mode = WRITE_BARRIER)  \
177    {                                                                                                   \
178        uint32_t offset = HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize();                        \
179        if (mode == WRITE_BARRIER && value.GetTaggedValue().IsHeapObject()) {                           \
180            AddValueToSnapshotEnv(thread, value.GetTaggedValue(), index, offset);                       \
181            Barriers::SetObject<true>(thread, this, offset, value.GetTaggedValue().GetRawData());       \
182        } else {                                                                                        \
183            SnapshotEnv *snapshotEnv = thread->GetEcmaVM()->GetSnapshotEnv();                           \
184            RemoveValueFromSnapshotEnv(snapshotEnv, value.GetTaggedValue(), offset);                    \
185            Barriers::SetPrimitive<JSTaggedType>(this, offset, value.GetTaggedValue().GetRawData());    \
186        }                                                                                               \
187    }                                                                                                   \
188    inline void Set##name(const JSThread *thread, type value, BarrierMode mode = WRITE_BARRIER)         \
189    {                                                                                                   \
190        uint32_t offset = HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize();                        \
191        if (mode == WRITE_BARRIER && value.IsHeapObject()) {                                            \
192            AddValueToSnapshotEnv(thread, value, index, offset);                                        \
193            Barriers::SetObject<true>(thread, this, offset, value.GetRawData());                        \
194        } else {                                                                                        \
195            SnapshotEnv *snapshotEnv = thread->GetEcmaVM()->GetSnapshotEnv();                           \
196            RemoveValueFromSnapshotEnv(snapshotEnv, value, offset);                                     \
197            Barriers::SetPrimitive<JSTaggedType>(this, offset, value.GetRawData());                     \
198        }                                                                                               \
199    }
200    GLOBAL_ENV_FIELDS(GLOBAL_ENV_FIELD_ACCESSORS)
201#undef GLOBAL_ENV_FIELD_ACCESSORS
202
203    static constexpr size_t HEADER_SIZE = TaggedObjectSize();
204    static constexpr size_t DATA_SIZE = HEADER_SIZE + FINAL_INDEX * JSTaggedValue::TaggedTypeSize();
205    static constexpr size_t SIZE = DATA_SIZE + RESERVED_LENGTH * JSTaggedValue::TaggedTypeSize();
206
207    DECL_VISIT_OBJECT(HEADER_SIZE, DATA_SIZE);
208
209    DECL_DUMP()
210};
211}  // namespace panda::ecmascript
212
213#endif  // ECMASCRIPT_GLOBAL_ENV_H
214