/* * Copyright (c) 2024, Andrew Kaster * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace GC { template struct ForeignRef; template struct ForeignPtr; #define FOREIGN_CELL(class_, base_class) \ using Base = base_class; \ friend class GC::Heap; class ForeignCell : public Cell { FOREIGN_CELL(ForeignCell, Cell); public: struct Vtable { // Holds a pointer to the foreign vtable information such as // a jclass in Java, or a Swift type metadata pointer void* class_metadata_pointer = nullptr; // FIXME: FlyString? The class name must be owned by the ForeignCell so it can vend StringViews // We should properly cache the name and class info pointer to avoid string churn String class_name; size_t alignment { 1 }; void (*initialize)(void* thiz, void* clazz, Ref); void (*destroy)(void* thiz, void* clazz); void (*finalize)(void* thiz, void* clazz); void (*visit_edges)(void* thiz, void* clazz, Cell::Visitor&); }; static Ref create(Heap&, size_t size, Vtable); void* foreign_data() SWIFT_RETURNS_INDEPENDENT_VALUE; // technically lying to swift, but it's fiiiiine // ^Cell virtual void finalize() override; virtual void visit_edges(Cell::Visitor& visitor) override; virtual StringView class_name() const override { return m_vtable.class_name; } ~ForeignCell(); private: ForeignCell(Vtable vtable); Vtable m_vtable; } SWIFT_IMMORTAL_REFERENCE; template struct ForeignRef { friend struct ForeignPtr; template static ForeignRef allocate(Heap& heap, Args... args) { DeferGC const defer_gc(heap); auto* cell = T::create(&heap, forward(args)...); if constexpr (IsSame) { return ForeignRef(*verify_cast(cell)); } else { static_assert(IsSame); auto* cast_cell = static_cast(cell); return ForeignRef(*verify_cast(cast_cell)); } } ForeignRef() = delete; // This constructor should only be called directly after allocating a foreign cell by calling an FFI create method ForeignRef(ForeignCell& cell) : m_cell(cell) { // FIXME: This is super dangerous. How can we assert that the cell is actually a T? m_data = static_cast(m_cell->foreign_data()); } ~ForeignRef() = default; ForeignRef(ForeignRef const& other) = default; ForeignRef& operator=(ForeignRef const& other) = default; RETURNS_NONNULL T* operator->() const { return m_data; } [[nodiscard]] T& operator*() const { return *m_data; } RETURNS_NONNULL T* ptr() const { return m_data; } RETURNS_NONNULL operator T*() const { return m_data; } operator T&() const { return *m_data; } Ref cell() const { return m_cell; } void visit_edges(Cell::Visitor& visitor) { visitor.visit(m_cell); } private: Ref m_cell; T* m_data { nullptr }; }; template struct ForeignPtr { constexpr ForeignPtr() = default; // This constructor should only be called directly after allocating a foreign cell by calling an FFI create method ForeignPtr(ForeignCell& cell) : m_cell(&cell) { // FIXME: This is super dangerous. How can we assert that the cell is actually a T? m_data = static_cast(m_cell->foreign_data()); } // This constructor should only be called directly after allocating a foreign cell by calling an FFI create method ForeignPtr(ForeignCell* cell) : m_cell(cell) { // FIXME: This is super dangerous. How can we assert that the cell is actually a T? m_data = m_cell ? static_cast(m_cell->foreign_data()) : nullptr; } ForeignPtr(ForeignRef const& other) : m_cell(other.m_cell) , m_data(other.m_data) { } ForeignPtr(nullptr_t) : m_cell(nullptr) { } ForeignPtr(ForeignPtr const& other) = default; ForeignPtr& operator=(ForeignPtr const& other) = default; T* operator->() const { ASSERT(m_cell && m_data); return m_data; } [[nodiscard]] T& operator*() const { ASSERT(m_cell && m_data); return *m_data; } operator T*() const { return m_data; } T* ptr() const { return m_data; } explicit operator bool() const { return !!m_cell; } bool operator!() const { return !m_cell; } Ptr cell() const { return m_cell; } void visit_edges(Cell::Visitor& visitor) { visitor.visit(m_cell); } private: Ptr m_cell; T* m_data { nullptr }; }; }