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