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