/* * Copyright (c) 2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include namespace JS { class HandleImpl : public RefCounted { AK_MAKE_NONCOPYABLE(HandleImpl); AK_MAKE_NONMOVABLE(HandleImpl); public: ~HandleImpl(); Cell* cell() { return m_cell; } Cell const* cell() const { return m_cell; } SourceLocation const& source_location() const { return m_location; } private: template friend class Handle; explicit HandleImpl(Cell*, SourceLocation location); GCPtr m_cell; SourceLocation m_location; IntrusiveListNode m_list_node; public: using List = IntrusiveList<&HandleImpl::m_list_node>; }; template class Handle { public: Handle() = default; static Handle create(T* cell, SourceLocation location = SourceLocation::current()) { return Handle(adopt_ref(*new HandleImpl(const_cast*>(cell), location))); } Handle(T* cell, SourceLocation location = SourceLocation::current()) { if (cell) m_impl = adopt_ref(*new HandleImpl(cell, location)); } Handle(T& cell, SourceLocation location = SourceLocation::current()) : m_impl(adopt_ref(*new HandleImpl(&cell, location))) { } Handle(GCPtr cell, SourceLocation location = SourceLocation::current()) : Handle(cell.ptr(), location) { } Handle(NonnullGCPtr cell, SourceLocation location = SourceLocation::current()) : Handle(*cell, location) { } T* cell() const { if (!m_impl) return nullptr; return static_cast(m_impl->cell()); } T* ptr() const { return cell(); } bool is_null() const { return m_impl.is_null(); } T* operator->() const { return cell(); } T& operator*() const { return *cell(); } bool operator!() const { return !cell(); } operator bool() const { return cell(); } operator T*() const { return cell(); } private: explicit Handle(NonnullRefPtr impl) : m_impl(move(impl)) { } RefPtr m_impl; }; template inline Handle make_handle(T* cell, SourceLocation location = SourceLocation::current()) { if (!cell) return Handle {}; return Handle::create(cell, location); } template inline Handle make_handle(T& cell, SourceLocation location = SourceLocation::current()) { return Handle::create(&cell, location); } template inline Handle make_handle(GCPtr cell, SourceLocation location = SourceLocation::current()) { if (!cell) return Handle {}; return Handle::create(cell.ptr(), location); } template inline Handle make_handle(NonnullGCPtr cell, SourceLocation location = SourceLocation::current()) { return Handle::create(cell.ptr(), location); } template<> class Handle { public: Handle() = default; static Handle create(Value value, SourceLocation location) { if (value.is_cell()) return Handle(value, &value.as_cell(), location); return Handle(value); } auto cell() { return m_handle.cell(); } auto cell() const { return m_handle.cell(); } auto value() const { return *m_value; } bool is_null() const { return m_handle.is_null() && !m_value.has_value(); } bool operator==(Value const& value) const { return value == m_value; } bool operator==(Handle const& other) const { return other.m_value == this->m_value; } private: explicit Handle(Value value) : m_value(value) { } explicit Handle(Value value, Cell* cell, SourceLocation location) : m_value(value) , m_handle(Handle::create(cell, location)) { } Optional m_value; Handle m_handle; }; inline Handle make_handle(Value value, SourceLocation location = SourceLocation::current()) { return Handle::create(value, location); } } namespace AK { template struct Traits> : public DefaultTraits> { static unsigned hash(JS::Handle const& handle) { return Traits::hash(handle); } }; template<> struct Traits> : public DefaultTraits> { static unsigned hash(JS::Handle const& handle) { return Traits::hash(handle.value()); } }; namespace Detail { template inline constexpr bool IsHashCompatible, T> = true; template inline constexpr bool IsHashCompatible> = true; } }