/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ECMASCRIPT_JSHANDLE_H #define ECMASCRIPT_JSHANDLE_H #include #include "ecmascript/ecma_handle_scope.h" #include "ecmascript/js_tagged_value.h" /* * JSHandle: A JSHandle provides a reference to an object that survives relocation by the garbage collector. * * HandleStorage: Handles are only valid within a HandleScope. When a JSHandle is created for an object a cell is * allocated in the current HandleScope. * * HandleStorage: HandleStorage is the storage structure of the object pointer. GC will use the stored pointer as root * and update the stored value after the object is moved * * JSHandle ---- HandleStorage ----- heap * | | | * address-----> store: T* ------> object * * { * EcmaHandleScope scope2(thread); * JSHandle jhandle(thread, obj4); * JSHandle jhandle(thread, obj5); * JSHandle jhandle(thread, obj6); * JSHandle jhandle(thread, obj7); * } * * // out of scope, The obj pointer in node will be free (obj7, obj6, obj5, obj4) and PopTopNode(top_node = prev_node) * * | | | obj5 | * | | scope2-> | obj4 | * | | | obj3 | * | obj7 | | obj2 | * |__obj6__| scope1-> |__obj1___| * top_node ---------> prev_node------>nullptr * * example: * JSHandle handle; * { * EcmaHandleScope(thread); * JSHandle jshandle(thread, T*); * jshandle->method(); // to invoke method of T * handle = jshandle; * } * handle->method(); // error! do not used handle out of scope */ namespace panda::test { class JSHandleTest; } // namespace panda::test namespace panda::ecmascript { class TaggedArray; class LinkedHashMap; class LinkedHashSet; class NameDictionary; template class JSHandle { public: inline explicit JSHandle() : address_(reinterpret_cast(nullptr)) {} ~JSHandle() = default; DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSHandle); DEFAULT_COPY_SEMANTIC(JSHandle); explicit JSHandle(const JSThread *thread, JSTaggedValue value) { address_ = EcmaHandleScope::NewHandle(const_cast(thread), value.GetRawData()); } explicit JSHandle(const JSThread *thread, const TaggedObject *value) { address_ = EcmaHandleScope::NewHandle(const_cast(thread), JSTaggedValue(value).GetRawData()); } inline uintptr_t GetAddress() const { return address_; } template explicit JSHandle(const JSHandle &handle) : address_(handle.GetAddress()) {} template inline static JSHandle Cast(const JSHandle &handle) { T::Cast(handle.GetTaggedValue().GetTaggedObject()); return JSHandle(handle.GetAddress()); } inline JSTaggedValue GetTaggedValue() const { if (GetAddress() == 0U) { return JSTaggedValue::Undefined(); } return *(reinterpret_cast(GetAddress())); // NOLINT(clang-analyzer-core.NullDereference) } inline JSTaggedType GetTaggedType() const { if (GetAddress() == 0U) { return JSTaggedValue::Undefined().GetRawData(); } return *reinterpret_cast(GetAddress()); // NOLINT(clang-analyzer-core.NullDereference) } inline T *operator*() const { return T::Cast(GetTaggedValue().GetTaggedObject()); } inline T *operator->() const { return T::Cast(GetTaggedValue().GetTaggedObject()); } inline bool operator==(const JSHandle &other) const { return GetTaggedType() == other.GetTaggedType(); } inline bool operator!=(const JSHandle &other) const { return GetTaggedType() != other.GetTaggedType(); } inline bool IsEmpty() const { return GetAddress() == 0U; } template R *GetObject() const { return reinterpret_cast(GetTaggedValue().GetTaggedObject()); } inline explicit JSHandle(uintptr_t slot) : address_(slot) { if (!std::is_convertible::value) { ASSERT(slot != 0); T::Cast((*reinterpret_cast(slot)).GetTaggedObject()); } } void Dump() const DUMP_API_ATTR { GetTaggedValue().D(); } private: inline explicit JSHandle(const JSTaggedType *slot) : address_(reinterpret_cast(slot)) {} inline explicit JSHandle(const T *const *slot) : address_(reinterpret_cast(slot)) {} uintptr_t address_; // NOLINT(misc-non-private-member-variables-in-classes) friend class EcmaVM; friend class GlobalEnv; friend class JSHandleTest; friend class GlobalHandleCollection; friend class RuntimeStubs; }; template <> inline JSTaggedValue *JSHandle::operator->() const { return reinterpret_cast(GetAddress()); } template <> inline JSTaggedValue *JSHandle::operator*() const { return reinterpret_cast(GetAddress()); } template <> inline JSTaggedNumber *JSHandle::operator->() const { return reinterpret_cast(GetAddress()); } template <> inline JSTaggedNumber *JSHandle::operator*() const { return reinterpret_cast(GetAddress()); } template class JSMutableHandle : public JSHandle { public: JSMutableHandle() = default; ~JSMutableHandle() = default; DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSMutableHandle); DEFAULT_COPY_SEMANTIC(JSMutableHandle); explicit JSMutableHandle(const JSThread *thread, JSTaggedValue value) : JSHandle(thread, value) {} explicit JSMutableHandle(const JSThread *thread, const TaggedArray *value) : JSHandle(thread, value) {} template explicit JSMutableHandle(const JSThread *thread, const JSHandle &handle) : JSHandle(thread, handle.GetTaggedValue()) { } inline explicit JSMutableHandle(uintptr_t slot) : JSHandle(slot) { } template inline static JSMutableHandle Cast(const JSMutableHandle &handle) { JSHandle::Cast(handle); return JSMutableHandle(handle.GetAddress()); } void Update(JSTaggedValue value) { auto addr = reinterpret_cast(this->GetAddress()); ASSERT(addr != nullptr); *addr = value; } template void Update(const JSHandle &handle) { auto addr = reinterpret_cast(this->GetAddress()); *addr = handle.GetTaggedValue(); } }; } // namespace panda::ecmascript #endif // ECMASCRIPT_JSHANDLE_H