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
66namespace panda::test {
67class JSHandleTest;
68}  // namespace panda::test
69
70namespace panda::ecmascript {
71class TaggedArray;
72class LinkedHashMap;
73class LinkedHashSet;
74class NameDictionary;
75
76template <typename T>
77class JSHandle {
78public:
79    inline JSHandle() : address_(reinterpret_cast<uintptr_t>(nullptr)) {}
80    ~JSHandle() = default;
81    DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSHandle);
82    DEFAULT_COPY_SEMANTIC(JSHandle);
83
84    JSHandle(const JSThread *thread, JSTaggedValue value)
85    {
86        address_ = EcmaHandleScope::NewHandle(const_cast<JSThread *>(thread), value.GetRawData());
87    }
88
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
99    JSHandle(const JSThread *thread, const TaggedObject *value)
100    {
101        address_ = EcmaHandleScope::NewHandle(const_cast<JSThread *>(thread), JSTaggedValue(value).GetRawData());
102    }
103
104    inline uintptr_t GetAddress() const
105    {
106        return address_;
107    }
108
109    template <typename S>
110    explicit JSHandle(const JSHandle<S> &handle) : address_(handle.GetAddress()) {}
111
112    template <typename S>
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
119    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
128    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
137    inline T *operator*() const
138    {
139        return T::Cast(GetTaggedValue().GetTaggedObject());
140    }
141
142    inline T *operator->() const
143    {
144        return T::Cast(GetTaggedValue().GetTaggedObject());
145    }
146
147    inline bool operator==(const JSHandle<T> &other) const
148    {
149        return GetTaggedType() == other.GetTaggedType();
150    }
151
152    inline bool operator!=(const JSHandle<T> &other) const
153    {
154        return GetTaggedType() != other.GetTaggedType();
155    }
156
157    inline bool IsEmpty() const
158    {
159        return GetAddress() == 0U;
160    }
161
162    template <typename R>
163    R *GetObject() const
164    {
165        return reinterpret_cast<R *>(GetTaggedValue().GetTaggedObject());
166    }
167
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
183private:
184    inline explicit JSHandle(const JSTaggedType *slot) : address_(reinterpret_cast<uintptr_t>(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
195template <>
196inline JSTaggedValue *JSHandle<JSTaggedValue>::operator->() const
197{
198    return reinterpret_cast<JSTaggedValue *>(GetAddress());
199}
200
201template <>
202inline JSTaggedValue *JSHandle<JSTaggedValue>::operator*() const
203{
204    return reinterpret_cast<JSTaggedValue *>(GetAddress());
205}
206
207template <typename T>
208class JSMutableHandle : public JSHandle<T> {
209public:
210    JSMutableHandle() = default;
211    ~JSMutableHandle() = default;
212    DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSMutableHandle);
213    DEFAULT_COPY_SEMANTIC(JSMutableHandle);
214
215    JSMutableHandle(const JSThread *thread, JSTaggedValue value) : JSHandle<T>(thread, value) {}
216    JSMutableHandle(const JSThread *thread, const TaggedArray *value) : JSHandle<T>(thread, value) {}
217    template <typename S>
218    JSMutableHandle(const JSThread *thread, const JSHandle<S> &handle)
219        : JSHandle<T>(thread, handle.GetTaggedValue())
220    {
221    }
222    inline explicit JSMutableHandle(uintptr_t slot) : JSHandle<T>(slot)
223    {
224    }
225
226    template <typename S>
227    inline static JSMutableHandle<T> Cast(const JSMutableHandle<S> &handle)
228    {
229        JSHandle<T>::Cast(handle);
230        return JSMutableHandle<T>(handle.GetAddress());
231    }
232
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>
241    void Update(const JSHandle<S> &handle)
242    {
243        auto addr = reinterpret_cast<JSTaggedValue *>(this->GetAddress());
244        *addr = handle.GetTaggedValue();
245    }
246};
247
248template<typename>
249struct IsJSHandle : std::false_type {};
250
251template<typename Value>
252struct IsJSHandle<JSHandle<Value>> : std::true_type {};
253
254template<typename Value>
255struct IsJSHandle<JSMutableHandle<Value>> : std::true_type {};
256}  // namespace panda::ecmascript
257
258#endif  // ECMASCRIPT_JSHANDLE_H
259