// Copyright (c) 2026 Cloudflare, Inc. // Licensed under the Apache 2.0 license found in the LICENSE file or at: // https://opensource.org/licenses/Apache-2.0 #pragma once // This header contains template implementations that require complete type definitions. // It should be included after workerd/rust/jsg/v8.rs.h which defines the complete types. #include #include namespace workerd::rust::jsg { // Local static_assert(sizeof(v8::Local) == 8, "Size should match"); static_assert(alignof(v8::Local) == 8, "Alignment should match"); template inline Local to_ffi(v8::Local&& value) { size_t result; auto ptr_void = reinterpret_cast(&result); new (ptr_void) v8::Local(kj::mv(value)); return Local{result}; } template inline v8::Local local_from_ffi(Local&& value) { auto ptr_void = reinterpret_cast(&value.ptr); return *reinterpret_cast*>(ptr_void); } template inline const v8::Local& local_as_ref_from_ffi(const Local& value) { auto ptr_void = reinterpret_cast(&value.ptr); return *reinterpret_cast*>(ptr_void); } // MaybeLocal // v8::MaybeLocal is exactly one Local field at offset 0, so it has the same // size and layout as ffi::Local (one pointer-sized word, zero means empty). static_assert(sizeof(v8::MaybeLocal) == sizeof(MaybeLocal), "Size should match"); static_assert(alignof(v8::MaybeLocal) == alignof(MaybeLocal), "Alignment should match"); template inline MaybeLocal maybe_local_to_ffi(v8::MaybeLocal value) { size_t result; auto ptr_void = reinterpret_cast(&result); new (ptr_void) v8::MaybeLocal(value); return MaybeLocal{result}; } // Global // // ffi::Global stores only the strong v8::Global in `ptr`. // The traced v8::TracedReference slot lives in Global::traced_ptr // on the Rust side (as UnsafeCell), passed by raw pointer to // wrappable_visit_global and wrappable_global_reset. static_assert(sizeof(v8::Global) == sizeof(size_t), "Global must be pointer-sized"); static_assert(sizeof(v8::TracedReference) == sizeof(size_t), "TracedReference must be pointer-sized"); static_assert(sizeof(Global) == sizeof(size_t), "ffi::Global must hold exactly one pointer slot"); static_assert(alignof(v8::Global) == alignof(Global), "Alignment should match"); template inline Global to_ffi(v8::Global&& value) { size_t strong_slot; auto ptr_void = reinterpret_cast(&strong_slot); new (ptr_void) v8::Global(kj::mv(value)); return Global{strong_slot}; } template inline v8::Global global_from_ffi(Global&& value) { auto ptr_void = reinterpret_cast(&value.ptr); return kj::mv(*reinterpret_cast*>(ptr_void)); } template inline const v8::Global& global_as_ref_from_ffi(const Global& value) { auto ptr_void = reinterpret_cast(&value.ptr); return *reinterpret_cast*>(ptr_void); } template inline v8::Global* global_as_ref_from_ffi(Global& value) { auto ptr_void = reinterpret_cast(&value.ptr); return reinterpret_cast*>(ptr_void); } // TracedReference static_assert(sizeof(v8::TracedReference) == sizeof(TracedReference), "TracedReference size must match"); static_assert(alignof(v8::TracedReference) == alignof(TracedReference), "TracedReference alignment must match"); inline v8::TracedReference& traced_ref_from_ffi(TracedReference& value) { return *reinterpret_cast*>(&value); } // GcVisitor - wraps a pointer to jsg::GcVisitor static_assert(sizeof(::workerd::jsg::GcVisitor*) == sizeof(GcVisitor), "Size should match"); static_assert(alignof(::workerd::jsg::GcVisitor*) == alignof(GcVisitor), "Alignment should match"); inline GcVisitor to_ffi(::workerd::jsg::GcVisitor* visitor) { return GcVisitor{reinterpret_cast(visitor)}; } inline ::workerd::jsg::GcVisitor* gc_visitor_from_ffi(GcVisitor* value) { return reinterpret_cast<::workerd::jsg::GcVisitor*>(value->ptr); } } // namespace workerd::rust::jsg