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_JSHANDLE_H
17 #define ECMASCRIPT_JSHANDLE_H
18
19 #include <type_traits>
20
21 #include "ecmascript/ecma_handle_scope.h"
22 #include "ecmascript/js_tagged_value.h"
23 #include "ecmascript/mem/assert_scope.h"
24
25 /*
26 * JSHandle: A JSHandle provides a reference to an object that survives relocation by the garbage collector.
27 *
28 * HandleStorage: Handles are only valid within a HandleScope. When a JSHandle is created for an object a cell is
29 * allocated in the current HandleScope.
30 *
31 * HandleStorage: HandleStorage is the storage structure of the object pointer. GC will use the stored pointer as root
32 * and update the stored value after the object is moved
33 *
34 * JSHandle ---- HandleStorage ----- heap
35 * | | |
36 * address-----> store: T* ------> object
37 *
38 * {
39 * EcmaHandleScope scope2(thread);
40 * JSHandle<T> jhandle(thread, obj4);
41 * JSHandle<T> jhandle(thread, obj5);
42 * JSHandle<T> jhandle(thread, obj6);
43 * JSHandle<T> jhandle(thread, obj7);
44 * }
45 *
46 * // out of scope, The obj pointer in node will be free (obj7, obj6, obj5, obj4) and PopTopNode(top_node = prev_node)
47 *
48 * | | | obj5 |
49 * | | scope2-> | obj4 |
50 * | | | obj3 |
51 * | obj7 | | obj2 |
52 * |__obj6__| scope1-> |__obj1___|
53 * top_node ---------> prev_node------>nullptr
54 *
55 * example:
56 * JSHandle<T> handle;
57 * {
58 * EcmaHandleScope(thread);
59 * JSHandle<T> jshandle(thread, T*);
60 * jshandle->method(); // to invoke method of T
61 * handle = jshandle;
62 * }
63 * handle->method(); // error! do not used handle out of scope
64 */
65
66 namespace panda::test {
67 class JSHandleTest;
68 } // namespace panda::test
69
70 namespace panda::ecmascript {
71 class TaggedArray;
72 class LinkedHashMap;
73 class LinkedHashSet;
74 class NameDictionary;
75
76 template <typename T>
77 class JSHandle {
78 public:
JSHandle()79 inline JSHandle() : address_(reinterpret_cast<uintptr_t>(nullptr)) {}
80 ~JSHandle() = default;
81 DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSHandle);
82 DEFAULT_COPY_SEMANTIC(JSHandle);
83
JSHandle(const JSThread *thread, JSTaggedValue value)84 JSHandle(const JSThread *thread, JSTaggedValue value)
85 {
86 address_ = EcmaHandleScope::NewHandle(const_cast<JSThread *>(thread), value.GetRawData());
87 }
88
JSHandle(const JSThread *thread, JSTaggedValue value, bool isPrimitive)89 JSHandle(const JSThread *thread, JSTaggedValue value, bool isPrimitive)
90 {
91 if (LIKELY(isPrimitive)) {
92 address_ = EcmaHandleScope::NewPrimitiveHandle(
93 const_cast<JSThread *>(thread), value.GetRawData());
94 return;
95 }
96 address_ = EcmaHandleScope::NewHandle(const_cast<JSThread *>(thread), value.GetRawData());
97 }
98
JSHandle(const JSThread *thread, const TaggedObject *value)99 JSHandle(const JSThread *thread, const TaggedObject *value)
100 {
101 address_ = EcmaHandleScope::NewHandle(const_cast<JSThread *>(thread), JSTaggedValue(value).GetRawData());
102 }
103
GetAddress() const104 inline uintptr_t GetAddress() const
105 {
106 return address_;
107 }
108
109 template <typename S>
JSHandle(const JSHandle<S> &handle)110 explicit JSHandle(const JSHandle<S> &handle) : address_(handle.GetAddress()) {}
111
112 template <typename S>
Cast(const JSHandle<S> &handle)113 inline static JSHandle<T> Cast(const JSHandle<S> &handle)
114 {
115 T::Cast(handle.GetTaggedValue().GetTaggedObject());
116 return JSHandle<T>(handle.GetAddress());
117 }
118
GetTaggedValue() const119 inline JSTaggedValue GetTaggedValue() const
120 {
121 CHECK_NO_DEREF_HANDLE;
122 if (GetAddress() == 0U) {
123 return JSTaggedValue::Undefined();
124 }
125 return *(reinterpret_cast<JSTaggedValue *>(GetAddress())); // NOLINT(clang-analyzer-core.NullDereference)
126 }
127
GetTaggedType() const128 inline JSTaggedType GetTaggedType() const
129 {
130 CHECK_NO_DEREF_HANDLE;
131 if (GetAddress() == 0U) {
132 return JSTaggedValue::Undefined().GetRawData();
133 }
134 return *reinterpret_cast<JSTaggedType *>(GetAddress()); // NOLINT(clang-analyzer-core.NullDereference)
135 }
136
operator *() const137 inline T *operator*() const
138 {
139 return T::Cast(GetTaggedValue().GetTaggedObject());
140 }
141
operator ->() const142 inline T *operator->() const
143 {
144 return T::Cast(GetTaggedValue().GetTaggedObject());
145 }
146
operator ==(const JSHandle<T> &other) const147 inline bool operator==(const JSHandle<T> &other) const
148 {
149 return GetTaggedType() == other.GetTaggedType();
150 }
151
operator !=(const JSHandle<T> &other) const152 inline bool operator!=(const JSHandle<T> &other) const
153 {
154 return GetTaggedType() != other.GetTaggedType();
155 }
156
IsEmpty() const157 inline bool IsEmpty() const
158 {
159 return GetAddress() == 0U;
160 }
161
162 template <typename R>
GetObject() const163 R *GetObject() const
164 {
165 return reinterpret_cast<R *>(GetTaggedValue().GetTaggedObject());
166 }
167
JSHandle(uintptr_t slot)168 inline explicit JSHandle(uintptr_t slot) : address_(slot)
169 {
170 if (!std::is_convertible<T *, JSTaggedValue *>::value) {
171 ASSERT(slot != 0);
172 if ((*reinterpret_cast<JSTaggedValue *>(slot)).IsHeapObject()) {
173 T::Cast((*reinterpret_cast<JSTaggedValue *>(slot)).GetTaggedObject());
174 }
175 }
176 }
177
178 void Dump() const DUMP_API_ATTR
179 {
180 GetTaggedValue().D();
181 }
182
183 private:
JSHandle(const JSTaggedType *slot)184 inline explicit JSHandle(const JSTaggedType *slot) : address_(reinterpret_cast<uintptr_t>(slot)) {}
JSHandle(const T *const *slot)185 inline explicit JSHandle(const T *const *slot) : address_(reinterpret_cast<uintptr_t>(slot)) {}
186
187 uintptr_t address_; // NOLINT(misc-non-private-member-variables-in-classes)
188 friend class EcmaVM;
189 friend class GlobalEnv;
190 friend class JSHandleTest;
191 friend class GlobalHandleCollection;
192 friend class RuntimeStubs;
193 };
194
195 template <>
operator ->() const196 inline JSTaggedValue *JSHandle<JSTaggedValue>::operator->() const
197 {
198 return reinterpret_cast<JSTaggedValue *>(GetAddress());
199 }
200
201 template <>
operator *() const202 inline JSTaggedValue *JSHandle<JSTaggedValue>::operator*() const
203 {
204 return reinterpret_cast<JSTaggedValue *>(GetAddress());
205 }
206
207 template <typename T>
208 class JSMutableHandle : public JSHandle<T> {
209 public:
210 JSMutableHandle() = default;
211 ~JSMutableHandle() = default;
212 DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSMutableHandle);
213 DEFAULT_COPY_SEMANTIC(JSMutableHandle);
214
JSMutableHandle(const JSThread *thread, JSTaggedValue value)215 JSMutableHandle(const JSThread *thread, JSTaggedValue value) : JSHandle<T>(thread, value) {}
JSMutableHandle(const JSThread *thread, const TaggedArray *value)216 JSMutableHandle(const JSThread *thread, const TaggedArray *value) : JSHandle<T>(thread, value) {}
217 template <typename S>
JSMutableHandle(const JSThread *thread, const JSHandle<S> &handle)218 JSMutableHandle(const JSThread *thread, const JSHandle<S> &handle)
219 : JSHandle<T>(thread, handle.GetTaggedValue())
220 {
221 }
JSMutableHandle(uintptr_t slot)222 inline explicit JSMutableHandle(uintptr_t slot) : JSHandle<T>(slot)
223 {
224 }
225
226 template <typename S>
Cast(const JSMutableHandle<S> &handle)227 inline static JSMutableHandle<T> Cast(const JSMutableHandle<S> &handle)
228 {
229 JSHandle<T>::Cast(handle);
230 return JSMutableHandle<T>(handle.GetAddress());
231 }
232
Update(JSTaggedValue value)233 void Update(JSTaggedValue value)
234 {
235 auto addr = reinterpret_cast<JSTaggedValue *>(this->GetAddress());
236 ASSERT(addr != nullptr);
237 *addr = value;
238 }
239
240 template <typename S>
Update(const JSHandle<S> &handle)241 void Update(const JSHandle<S> &handle)
242 {
243 auto addr = reinterpret_cast<JSTaggedValue *>(this->GetAddress());
244 *addr = handle.GetTaggedValue();
245 }
246 };
247
248 template<typename>
249 struct IsJSHandle : std::false_type {};
250
251 template<typename Value>
252 struct IsJSHandle<JSHandle<Value>> : std::true_type {};
253
254 template<typename Value>
255 struct IsJSHandle<JSMutableHandle<Value>> : std::true_type {};
256 } // namespace panda::ecmascript
257
258 #endif // ECMASCRIPT_JSHANDLE_H
259