use super::super::super::context::{JITClosure, JITContext};
use crate::ffi::jit_kinds::*;
use crate::ffi::value_ffi::*;
#[deprecated(
note = "Closure-spec Phase H2: `emit_heap_closure` + `jit_finalize_heap_closure` \
is now the unconditional path for escaping closures. This FFI remains \
only for residual non-layout fallback paths; a follow-up phase deletes it."
)]
#[inline(always)]
pub extern "C" fn jit_make_closure(
ctx: *mut JITContext,
function_id: u16,
captures_count: u16,
) -> u64 {
unsafe {
if ctx.is_null() {
return box_function(function_id);
}
let ctx_ref = &mut *ctx;
let count = captures_count as usize;
if ctx_ref.stack_ptr < count || ctx_ref.stack_ptr > 512 {
return box_function(function_id);
}
let mut captures = Vec::with_capacity(count);
for _ in 0..count {
ctx_ref.stack_ptr -= 1;
captures.push(ctx_ref.stack[ctx_ref.stack_ptr]);
}
captures.reverse();
let closure = JITClosure::new(function_id, &captures);
unified_box(HK_CLOSURE, *closure)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_finalize_heap_closure(
header_ptr: *mut u8,
_function_id: u32,
captures_count: u32,
layout_ptr: *const shape_value::v2::closure_layout::ClosureLayout,
) -> u64 {
use shape_value::heap_value::HeapValue;
use shape_value::v2::closure_layout::ClosureLayout;
use shape_value::v2::closure_raw::OwnedClosureBlock;
use std::sync::Arc;
unsafe {
if header_ptr.is_null() || layout_ptr.is_null() {
return 0u64;
}
let layout_ref: &ClosureLayout = &*layout_ptr;
let count = captures_count as usize;
debug_assert_eq!(
count,
layout_ref.capture_count(),
"jit_finalize_heap_closure: captures_count {} != layout.capture_count() {}",
count,
layout_ref.capture_count()
);
let _ = count;
Arc::increment_strong_count(layout_ptr);
let layout_arc: Arc<ClosureLayout> = Arc::from_raw(layout_ptr);
let owned = OwnedClosureBlock::from_raw(header_ptr as *const u8, layout_arc);
Arc::into_raw(Arc::new(HeapValue::ClosureRaw(owned))) as u64
}
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_arc_closure_retain(bits: u64) {
use shape_value::heap_value::HeapValue;
use std::sync::Arc;
if bits == 0 {
return;
}
if crate::ffi::value_ffi::is_inline_function(bits) {
return;
}
unsafe {
Arc::increment_strong_count(bits as *const HeapValue);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn jit_arc_closure_release(bits: u64) {
use shape_value::heap_value::HeapValue;
use std::sync::Arc;
if bits == 0 {
return;
}
if crate::ffi::value_ffi::is_inline_function(bits) {
return;
}
unsafe {
Arc::decrement_strong_count(bits as *const HeapValue);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell(initial: u64) -> *mut u64 {
Box::into_raw(Box::new(initial))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_arc_shared_retain(ptr: u64) -> u64 {
use shape_value::v2::closure_layout::SharedCell;
use std::sync::Arc;
if ptr == 0 {
tracing::debug!(
target: "shape_jit",
"jit-shared-cell retain null (no-op)",
);
return 0;
}
unsafe {
Arc::<SharedCell>::increment_strong_count(ptr as *const SharedCell);
}
tracing::debug!(
target: "shape_jit",
ptr,
"jit-shared-cell retain",
);
ptr
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_shared_lock_contended(ptr: u64) {
use shape_value::v2::closure_layout::SharedCell;
if ptr == 0 {
return;
}
let cell: &SharedCell = unsafe { &*(ptr as *const SharedCell) };
cell.lock_contended();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_shared_unlock_contended(ptr: u64) {
use shape_value::v2::closure_layout::SharedCell;
if ptr == 0 {
return;
}
let cell: &SharedCell = unsafe { &*(ptr as *const SharedCell) };
unsafe { cell.unlock() };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_shared_cell(_initial_bits: u64) -> u64 {
todo!(
"phase-2c §2.7.8/Q10 / W10 jit-playbook §5: SharedCell kind \
companion — jit_alloc_shared_cell needs a NativeKind \
parameter per ADR-006 §2.7.8 (cell parallel-kind track). \
The bytecode-side AllocSharedLocal already threads \
value_kind (`shape-vm/src/executor/variables/mod.rs:1510`); \
the JIT lowering for the same opcode must thread the \
matching kind through the FFI signature per §2.7.5."
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_arc_shared_release(ptr: u64) {
use shape_value::v2::closure_layout::SharedCell;
use std::sync::Arc;
if ptr == 0 {
return;
}
tracing::debug!(
target: "shape_jit",
ptr,
"jit-shared-cell release",
);
unsafe {
drop(Arc::<SharedCell>::from_raw(ptr as *const SharedCell));
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_i64(initial: i64) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_i64(initial) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_i64(ptr: i64) -> i64 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_i64(ptr as *mut i64) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_i64(ptr: i64, value: i64) {
unsafe { shape_value::v2::closure_raw::write_owned_mutable_i64(ptr as *mut i64, value) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_u64(initial: i64) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_u64(initial as u64) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_u64(ptr: i64) -> i64 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_u64(ptr as *mut u64) as i64 }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_u64(ptr: i64, value: i64) {
unsafe {
shape_value::v2::closure_raw::write_owned_mutable_u64(ptr as *mut u64, value as u64)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_f64(initial: f64) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_f64(initial) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_f64(ptr: i64) -> f64 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_f64(ptr as *mut f64) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_f64(ptr: i64, value: f64) {
unsafe { shape_value::v2::closure_raw::write_owned_mutable_f64(ptr as *mut f64, value) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_i32(initial: i32) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_i32(initial) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_i32(ptr: i64) -> i32 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_i32(ptr as *mut i32) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_i32(ptr: i64, value: i32) {
unsafe { shape_value::v2::closure_raw::write_owned_mutable_i32(ptr as *mut i32, value) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_u32(initial: i32) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_u32(initial as u32) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_u32(ptr: i64) -> i32 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_u32(ptr as *mut u32) as i32 }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_u32(ptr: i64, value: i32) {
unsafe {
shape_value::v2::closure_raw::write_owned_mutable_u32(ptr as *mut u32, value as u32)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_i16(initial: i32) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_i16(initial as i16) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_i16(ptr: i64) -> i32 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_i16(ptr as *mut i16) as i32 }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_i16(ptr: i64, value: i32) {
unsafe {
shape_value::v2::closure_raw::write_owned_mutable_i16(ptr as *mut i16, value as i16)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_u16(initial: i32) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_u16(initial as u16) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_u16(ptr: i64) -> i32 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_u16(ptr as *mut u16) as i32 }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_u16(ptr: i64, value: i32) {
unsafe {
shape_value::v2::closure_raw::write_owned_mutable_u16(ptr as *mut u16, value as u16)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_i8(initial: i32) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_i8(initial as i8) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_i8(ptr: i64) -> i32 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_i8(ptr as *mut i8) as i32 }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_i8(ptr: i64, value: i32) {
unsafe {
shape_value::v2::closure_raw::write_owned_mutable_i8(ptr as *mut i8, value as i8)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_u8(initial: i32) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_u8(initial as u8) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_u8(ptr: i64) -> i32 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_u8(ptr as *mut u8) as i32 }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_u8(ptr: i64, value: i32) {
unsafe {
shape_value::v2::closure_raw::write_owned_mutable_u8(ptr as *mut u8, value as u8)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_bool(initial: i32) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_bool(initial != 0) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_bool(ptr: i64) -> i32 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_bool(ptr as *mut bool) as i32 }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_bool(ptr: i64, value: i32) {
unsafe {
shape_value::v2::closure_raw::write_owned_mutable_bool(ptr as *mut bool, value != 0)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_alloc_owned_mut_cell_ptr(initial: i64) -> i64 {
shape_value::v2::closure_raw::alloc_owned_mutable_ptr(initial as u64) as i64
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_owned_mut_cell_ptr(ptr: i64) -> i64 {
unsafe { shape_value::v2::closure_raw::read_owned_mutable_ptr(ptr as *mut u64) as i64 }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_owned_mut_cell_ptr(ptr: i64, value: i64) {
unsafe {
shape_value::v2::closure_raw::write_owned_mutable_ptr(ptr as *mut u64, value as u64)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_i64(cell_ptr: i64) -> i64 {
use shape_value::v2::closure_layout::SharedCell;
unsafe { shape_value::v2::closure_raw::read_shared_i64(cell_ptr as *const SharedCell) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_i64(cell_ptr: i64, value: i64) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_i64(cell_ptr as *const SharedCell, value)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_u64(cell_ptr: i64) -> i64 {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::read_shared_u64(cell_ptr as *const SharedCell) as i64
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_u64(cell_ptr: i64, value: i64) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_u64(
cell_ptr as *const SharedCell,
value as u64,
)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_f64(cell_ptr: i64) -> f64 {
use shape_value::v2::closure_layout::SharedCell;
unsafe { shape_value::v2::closure_raw::read_shared_f64(cell_ptr as *const SharedCell) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_f64(cell_ptr: i64, value: f64) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_f64(cell_ptr as *const SharedCell, value)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_i32(cell_ptr: i64) -> i32 {
use shape_value::v2::closure_layout::SharedCell;
unsafe { shape_value::v2::closure_raw::read_shared_i32(cell_ptr as *const SharedCell) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_i32(cell_ptr: i64, value: i32) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_i32(cell_ptr as *const SharedCell, value)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_u32(cell_ptr: i64) -> i32 {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::read_shared_u32(cell_ptr as *const SharedCell) as i32
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_u32(cell_ptr: i64, value: i32) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_u32(
cell_ptr as *const SharedCell,
value as u32,
)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_i16(cell_ptr: i64) -> i32 {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::read_shared_i16(cell_ptr as *const SharedCell) as i32
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_i16(cell_ptr: i64, value: i32) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_i16(
cell_ptr as *const SharedCell,
value as i16,
)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_u16(cell_ptr: i64) -> i32 {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::read_shared_u16(cell_ptr as *const SharedCell) as i32
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_u16(cell_ptr: i64, value: i32) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_u16(
cell_ptr as *const SharedCell,
value as u16,
)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_i8(cell_ptr: i64) -> i32 {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::read_shared_i8(cell_ptr as *const SharedCell) as i32
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_i8(cell_ptr: i64, value: i32) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_i8(
cell_ptr as *const SharedCell,
value as i8,
)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_u8(cell_ptr: i64) -> i32 {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::read_shared_u8(cell_ptr as *const SharedCell) as i32
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_u8(cell_ptr: i64, value: i32) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_u8(
cell_ptr as *const SharedCell,
value as u8,
)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_bool(cell_ptr: i64) -> i32 {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::read_shared_bool(cell_ptr as *const SharedCell) as i32
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_bool(cell_ptr: i64, value: i32) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_bool(
cell_ptr as *const SharedCell,
value != 0,
)
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_read_shared_cell_ptr(cell_ptr: i64) -> i64 {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::read_shared_ptr(cell_ptr as *const SharedCell) as i64
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_write_shared_cell_ptr(cell_ptr: i64, value: i64) {
use shape_value::v2::closure_layout::SharedCell;
unsafe {
shape_value::v2::closure_raw::write_shared_ptr(
cell_ptr as *const SharedCell,
value as u64,
)
};
}
#[cfg(any())]
#[cfg(test)]
mod a1e_shared_ffi_tests {
use super::*;
use shape_value::v2::closure_layout::SharedCell;
use shape_value::{ValueWord, ValueWordExt};
use std::sync::Arc;
#[test]
fn a1e_ffi_arc_shared_retain_increments_strong_count() {
let arc: Arc<SharedCell> = Arc::new(SharedCell::new(ValueWord::from_i64(1234)));
let observer = Arc::clone(&arc);
assert_eq!(Arc::strong_count(&observer), 2);
let raw_slot_share = Arc::into_raw(Arc::clone(&arc));
assert_eq!(Arc::strong_count(&observer), 3);
let returned = unsafe { jit_arc_shared_retain(raw_slot_share as u64) };
assert_eq!(returned, raw_slot_share as u64, "helper returns the pointer");
assert_eq!(
Arc::strong_count(&observer),
4,
"retain must bump the strong count by one"
);
unsafe {
Arc::<SharedCell>::from_raw(raw_slot_share);
Arc::<SharedCell>::from_raw(raw_slot_share);
}
assert_eq!(Arc::strong_count(&observer), 2);
drop(arc);
assert_eq!(Arc::strong_count(&observer), 1);
}
#[test]
fn a1e_ffi_shared_lock_unlock_contended_roundtrip() {
let cell = Box::new(SharedCell::new(ValueWord::from_i64(42)));
let ptr = Box::into_raw(cell);
unsafe {
jit_shared_lock_contended(ptr as u64);
let state = (*ptr)
.state
.load(std::sync::atomic::Ordering::Relaxed);
assert_eq!(state, 1, "lock helper must leave state byte = 1");
jit_shared_unlock_contended(ptr as u64);
let state = (*ptr)
.state
.load(std::sync::atomic::Ordering::Relaxed);
assert_eq!(state, 0, "unlock helper must leave state byte = 0");
drop(Box::from_raw(ptr));
}
}
#[test]
fn a1e_ffi_shared_helpers_handle_null_ptr_safely() {
unsafe {
jit_shared_lock_contended(0);
jit_shared_unlock_contended(0);
}
}
}
#[cfg(test)]
mod a1d_owned_mutable_cell_tests {
use super::*;
#[test]
fn a1d_ffi_alloc_owned_mut_cell_roundtrip() {
let initial: u64 = 42;
let ptr = unsafe { jit_alloc_owned_mut_cell(initial) };
assert!(!ptr.is_null(), "allocator must return a non-null pointer");
let read = unsafe { *ptr };
assert_eq!(read, initial, "deref of fresh cell must yield the initial bits");
let _boxed: Box<u64> = unsafe { Box::from_raw(ptr) };
}
#[test]
fn a1d_ffi_alloc_owned_mut_cell_independent_cells() {
let a = unsafe { jit_alloc_owned_mut_cell(10) };
let b = unsafe { jit_alloc_owned_mut_cell(20) };
assert_ne!(a, b, "distinct allocations must yield distinct pointers");
unsafe {
std::ptr::write(a, 999);
assert_eq!(*a, 999);
assert_eq!(*b, 20);
}
unsafe {
let _ = Box::from_raw(a);
let _ = Box::from_raw(b);
}
}
#[test]
fn a1d_ffi_alloc_owned_mut_cell_store_then_read() {
let ptr = unsafe { jit_alloc_owned_mut_cell(0) };
for new_bits in [7u64, 13, 99, u64::MAX, 0] {
unsafe { std::ptr::write(ptr, new_bits) };
let out = unsafe { std::ptr::read(ptr) };
assert_eq!(out, new_bits);
}
unsafe {
let _ = Box::from_raw(ptr);
}
}
}
#[cfg(any())]
#[cfg(test)]
mod phase_h2_finalizer_tests {
use super::*;
use shape_value::heap_value::HeapValue;
use shape_value::v2::closure_layout::{
CaptureKind, ClosureLayout, HEAP_CLOSURE_HEADER_SIZE, TypedClosureHeader,
};
use shape_value::v2::concrete_type::ConcreteType;
use shape_value::v2::heap_header::{HEAP_KIND_V2_CLOSURE, HeapHeader};
use shape_value::{ValueWord, ValueWordExt};
use std::sync::Arc;
fn immutable_layout(types: &[ConcreteType]) -> ClosureLayout {
let kinds = vec![CaptureKind::Immutable; types.len()];
ClosureLayout::from_capture_types(types, &kinds)
}
unsafe fn alloc_typed_closure_for_test(
layout: &ClosureLayout,
function_id: u16,
type_id: u32,
) -> *mut u8 {
let size = layout.total_heap_size();
let align = 8;
let alloc_layout =
std::alloc::Layout::from_size_align(size, align).expect("valid layout");
let ptr = unsafe { std::alloc::alloc_zeroed(alloc_layout) };
assert!(!ptr.is_null(), "alloc_zeroed returned null");
unsafe {
std::ptr::write(ptr as *mut HeapHeader, HeapHeader::new(HEAP_KIND_V2_CLOSURE));
let header = ptr as *mut TypedClosureHeader;
(*header).function_id = function_id as u32;
(*header).type_id = type_id;
}
ptr
}
fn assert_closure_raw(bits: u64, expected_fid: u16, expected_caps: usize) -> (u16, usize) {
let vw = unsafe { ValueWord::clone_from_bits(bits) };
let hv = vw.as_heap_ref().expect("finalizer should produce a heap value");
assert!(
matches!(hv, HeapValue::ClosureRaw(..)),
"expected ClosureRaw variant, got {:?}",
hv.type_name()
);
let handle = hv.as_closure_handle().expect("closure handle");
assert_eq!(handle.function_id() as u16, expected_fid);
assert_eq!(handle.capture_count(), expected_caps);
let out = (handle.function_id() as u16, handle.capture_count());
drop(vw);
out
}
#[test]
fn finalizer_empty_captures() {
let layout = Arc::new(immutable_layout(&[]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 42, 0) };
let bits = unsafe {
jit_finalize_heap_closure(ptr, 42, 0, Arc::as_ptr(&layout))
};
assert_closure_raw(bits, 42, 0);
unsafe { drop_bits_via_raw(bits) };
}
unsafe fn drop_bits_via_raw(bits: u64) {
let vw = unsafe { ValueWord::from_raw_bits(bits) };
drop(vw);
}
#[test]
fn finalizer_single_i64_capture() {
let layout = Arc::new(immutable_layout(&[ConcreteType::I64]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 7, 0) };
unsafe {
let off = layout.heap_capture_offset(0);
assert_eq!(off, HEAP_CLOSURE_HEADER_SIZE);
let raw = ValueWord::from_i64(123).into_raw_bits();
std::ptr::write(ptr.add(off) as *mut u64, raw);
}
let bits = unsafe {
jit_finalize_heap_closure(ptr, 7, 1, Arc::as_ptr(&layout))
};
let vw = unsafe { ValueWord::clone_from_bits(bits) };
let hv = vw.as_heap_ref().expect("should be heap value");
let handle = hv.as_closure_handle().expect("closure handle");
assert!(matches!(hv, HeapValue::ClosureRaw(..)));
assert_eq!(handle.function_id() as u16, 7);
assert_eq!(handle.capture_count(), 1);
assert_eq!(handle.capture_as_value(0).as_i64(), Some(123));
drop(vw);
unsafe { drop_bits_via_raw(bits) };
}
#[test]
fn finalizer_single_f64_capture() {
let layout = Arc::new(immutable_layout(&[ConcreteType::F64]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 9, 0) };
unsafe {
let off = layout.heap_capture_offset(0);
std::ptr::write(ptr.add(off) as *mut f64, 3.14);
}
let bits = unsafe {
jit_finalize_heap_closure(ptr, 9, 1, Arc::as_ptr(&layout))
};
let vw = unsafe { ValueWord::clone_from_bits(bits) };
let hv = vw.as_heap_ref().expect("heap");
assert!(matches!(hv, HeapValue::ClosureRaw(..)));
let handle = hv.as_closure_handle().expect("handle");
assert_eq!(handle.function_id() as u16, 9);
assert_eq!(handle.capture_count(), 1);
assert_eq!(handle.capture_as_value(0).as_f64(), Some(3.14));
drop(vw);
unsafe { drop_bits_via_raw(bits) };
}
#[test]
fn finalizer_bool_capture() {
let layout = Arc::new(immutable_layout(&[ConcreteType::Bool]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 1, 0) };
unsafe {
let off = layout.heap_capture_offset(0);
std::ptr::write(ptr.add(off) as *mut u8, 1u8);
}
let bits = unsafe {
jit_finalize_heap_closure(ptr, 1, 1, Arc::as_ptr(&layout))
};
let vw = unsafe { ValueWord::clone_from_bits(bits) };
let hv = vw.as_heap_ref().expect("heap");
assert!(matches!(hv, HeapValue::ClosureRaw(..)));
let handle = hv.as_closure_handle().expect("handle");
assert_eq!(handle.capture_as_value(0).as_bool(), Some(true));
drop(vw);
unsafe { drop_bits_via_raw(bits) };
}
#[test]
fn finalizer_i32_capture_zero_extended() {
let layout = Arc::new(immutable_layout(&[ConcreteType::I32]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 2, 0) };
unsafe {
let off = layout.heap_capture_offset(0);
std::ptr::write(ptr.add(off) as *mut i32, -12345);
}
let bits = unsafe {
jit_finalize_heap_closure(ptr, 2, 1, Arc::as_ptr(&layout))
};
let vw = unsafe { ValueWord::clone_from_bits(bits) };
let hv = vw.as_heap_ref().expect("heap");
assert!(matches!(hv, HeapValue::ClosureRaw(..)));
let handle = hv.as_closure_handle().expect("handle");
assert_eq!(handle.capture_as_value(0).as_i64(), Some(-12345));
drop(vw);
unsafe { drop_bits_via_raw(bits) };
}
#[test]
fn finalizer_mixed_f64_i32_captures() {
let layout = Arc::new(immutable_layout(&[
ConcreteType::F64,
ConcreteType::I32,
]));
assert_eq!(layout.heap_capture_offset(0), 16);
assert_eq!(layout.heap_capture_offset(1), 24);
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 100, 0) };
unsafe {
std::ptr::write(ptr.add(16) as *mut f64, 2.71);
std::ptr::write(ptr.add(24) as *mut i32, 99);
}
let bits = unsafe {
jit_finalize_heap_closure(ptr, 100, 2, Arc::as_ptr(&layout))
};
let vw = unsafe { ValueWord::clone_from_bits(bits) };
let hv = vw.as_heap_ref().expect("heap");
assert!(matches!(hv, HeapValue::ClosureRaw(..)));
let handle = hv.as_closure_handle().expect("handle");
assert_eq!(handle.function_id() as u16, 100);
assert_eq!(handle.capture_count(), 2);
assert_eq!(handle.capture_as_value(0).as_f64(), Some(2.71));
assert_eq!(handle.capture_as_value(1).as_i64(), Some(99));
drop(vw);
unsafe { drop_bits_via_raw(bits) };
}
#[test]
fn finalizer_heap_typed_string_capture_preserves_refcount() {
let layout = Arc::new(immutable_layout(&[ConcreteType::String]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 55, 0) };
let s = ValueWord::from_string(Arc::new("hello".to_string()));
let s_bits = s.into_raw_bits();
unsafe {
let off = layout.heap_capture_offset(0);
std::ptr::write(ptr.add(off) as *mut u64, s_bits);
let _retained = ValueWord::clone_from_bits(s_bits);
std::mem::forget(_retained);
}
let bits = unsafe {
jit_finalize_heap_closure(ptr, 55, 1, Arc::as_ptr(&layout))
};
let vw = unsafe { ValueWord::clone_from_bits(bits) };
let hv = vw.as_heap_ref().expect("heap");
assert!(matches!(hv, HeapValue::ClosureRaw(..)));
let handle = hv.as_closure_handle().expect("handle");
let captured_bits = handle.capture_as_value(0).into_raw_bits();
let captured = unsafe { ValueWord::clone_from_bits(captured_bits) };
let captured_str = captured.as_heap_ref().and_then(|h| match h {
HeapValue::String(s) => Some(s.as_str().to_string()),
_ => None,
});
assert_eq!(captured_str.as_deref(), Some("hello"));
drop(captured);
drop(vw);
unsafe { drop_bits_via_raw(bits) };
let _orig = unsafe { ValueWord::from_raw_bits(s_bits) };
drop(_orig);
}
#[test]
fn finalizer_multi_capture_layout_offsets() {
let layout = Arc::new(immutable_layout(&[
ConcreteType::I64,
ConcreteType::F64,
ConcreteType::String,
]));
assert_eq!(layout.heap_capture_offset(0), 16);
assert_eq!(layout.heap_capture_offset(1), 24);
assert_eq!(layout.heap_capture_offset(2), 32);
assert_eq!(layout.heap_capture_mask, 0b100);
}
#[test]
fn finalizer_preserves_function_id_from_header() {
let layout = Arc::new(immutable_layout(&[]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 777, 0) };
let bits = unsafe {
jit_finalize_heap_closure(ptr, 555, 0, Arc::as_ptr(&layout))
};
let vw = unsafe { ValueWord::clone_from_bits(bits) };
let hv = vw.as_heap_ref().expect("heap");
assert!(matches!(hv, HeapValue::ClosureRaw(..)));
let handle = hv.as_closure_handle().expect("handle");
assert_eq!(handle.function_id() as u16, 777);
drop(vw);
unsafe { drop_bits_via_raw(bits) };
}
#[test]
fn finalizer_null_header_returns_none_tag() {
let layout = Arc::new(immutable_layout(&[]));
let bits = unsafe {
jit_finalize_heap_closure(
std::ptr::null_mut(),
0,
0,
Arc::as_ptr(&layout),
)
};
let vw = unsafe { ValueWord::from_raw_bits(bits) };
assert!(vw.as_heap_ref().is_none(), "null-input must not decode as heap");
}
#[test]
fn finalizer_null_layout_returns_none_tag() {
let layout = Arc::new(immutable_layout(&[]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 0, 0) };
let bits = unsafe {
jit_finalize_heap_closure(ptr, 0, 0, std::ptr::null())
};
let vw = unsafe { ValueWord::from_raw_bits(bits) };
assert!(vw.as_heap_ref().is_none());
unsafe {
let size = layout.total_heap_size();
let dl = std::alloc::Layout::from_size_align_unchecked(size, 8);
std::alloc::dealloc(ptr, dl);
}
}
#[test]
fn finalizer_layout_total_size_matches_alloc_shim_contract() {
for types in [
vec![],
vec![ConcreteType::I64],
vec![ConcreteType::F64, ConcreteType::I32],
vec![ConcreteType::String, ConcreteType::F64, ConcreteType::Bool],
] {
let layout = immutable_layout(&types);
assert!(layout.total_heap_size() >= 16);
assert_eq!(layout.total_heap_size() % 8, 0);
}
}
#[test]
fn finalizer_interpreter_baseline_is_callable() {
let layout = Arc::new(immutable_layout(&[ConcreteType::I64]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 3, 0) };
unsafe {
let off = layout.heap_capture_offset(0);
std::ptr::write(
ptr.add(off) as *mut u64,
ValueWord::from_i64(7).into_raw_bits(),
);
}
let bits = unsafe {
jit_finalize_heap_closure(ptr, 3, 1, Arc::as_ptr(&layout))
};
let vw = unsafe { ValueWord::clone_from_bits(bits) };
let is_closure_raw = matches!(
vw.as_heap_ref(),
Some(HeapValue::ClosureRaw(..))
);
assert!(
is_closure_raw,
"finalizer must produce HeapValue::ClosureRaw for H6.5 dispatch"
);
let handle = vw.as_heap_ref().unwrap().as_closure_handle().unwrap();
assert_eq!(handle.function_id() as u16, 3);
assert_eq!(handle.capture_as_value(0).as_i64(), Some(7));
drop(vw);
unsafe { drop_bits_via_raw(bits) };
}
#[test]
fn finalizer_arc_strong_count_after_explicit_release() {
let layout = Arc::new(immutable_layout(&[ConcreteType::String]));
let ptr = unsafe { alloc_typed_closure_for_test(&layout, 71, 0) };
let long_payload = "lifecycle".repeat(32);
let original_vw = ValueWord::from_string(Arc::new(long_payload));
let s_bits = original_vw.into_raw_bits();
let outer_ptr = {
let payload = shape_value::tag_bits::get_payload(s_bits);
let masked = payload & shape_value::tag_bits::HEAP_PTR_MASK;
masked as *const HeapValue
};
unsafe { Arc::increment_strong_count(outer_ptr); }
let observer: Arc<HeapValue> = unsafe { Arc::from_raw(outer_ptr) };
assert_eq!(Arc::strong_count(&observer), 2);
unsafe {
let off = layout.heap_capture_offset(0);
std::ptr::write(ptr.add(off) as *mut u64, s_bits);
let _retained = ValueWord::clone_from_bits(s_bits);
std::mem::forget(_retained);
}
assert_eq!(Arc::strong_count(&observer), 3);
let bits = unsafe {
jit_finalize_heap_closure(ptr, 71, 1, Arc::as_ptr(&layout))
};
assert_eq!(Arc::strong_count(&observer), 3);
unsafe {
let payload = shape_value::tag_bits::get_payload(bits);
let block_ptr = (payload & shape_value::tag_bits::HEAP_PTR_MASK)
as *const HeapValue;
Arc::decrement_strong_count(block_ptr);
}
assert_eq!(Arc::strong_count(&observer), 2);
unsafe { Arc::decrement_strong_count(outer_ptr); }
assert_eq!(Arc::strong_count(&observer), 1);
drop(observer);
}
}
#[cfg(any())]
#[cfg(test)]
mod session_1_shared_local_lifecycle_tests {
use super::*;
use shape_value::v2::closure_layout::{SharedCell, SHARED_CELL_VALUE_OFFSET};
use shape_value::{ValueWord, ValueWordExt};
use std::sync::Arc;
#[test]
fn session1_ffi_alloc_shared_cell_roundtrip() {
let initial = ValueWord::from_i64(1234).into_raw_bits();
let ptr = unsafe { jit_alloc_shared_cell(initial) };
assert_ne!(ptr, 0, "alloc must return a non-null pointer");
assert_eq!(ptr % 8, 0, "SharedCell is 8-byte aligned");
let cell: &SharedCell = unsafe { &*(ptr as *const SharedCell) };
assert_eq!(
cell.state.load(std::sync::atomic::Ordering::Relaxed),
0,
"freshly-allocated cell must be unlocked"
);
let payload = unsafe {
std::ptr::read((ptr as *const u8).add(SHARED_CELL_VALUE_OFFSET as usize)
as *const u64)
};
assert_eq!(payload, initial, "payload must equal initial_bits");
unsafe { jit_arc_shared_release(ptr) };
}
#[test]
fn session1_ffi_alloc_shared_cell_independent_allocations() {
let a = unsafe { jit_alloc_shared_cell(ValueWord::from_i64(10).into_raw_bits()) };
let b = unsafe { jit_alloc_shared_cell(ValueWord::from_i64(20).into_raw_bits()) };
assert_ne!(a, 0);
assert_ne!(b, 0);
assert_ne!(a, b, "independent allocations must yield distinct pointers");
unsafe {
jit_arc_shared_release(a);
jit_arc_shared_release(b);
}
}
#[test]
fn session1_ffi_arc_shared_release_null_is_noop() {
unsafe { jit_arc_shared_release(0) };
}
#[test]
fn session1_ffi_alloc_retain_release_strong_count_balanced() {
let initial = ValueWord::from_i64(7).into_raw_bits();
let ptr = unsafe { jit_alloc_shared_cell(initial) };
let arc_observer: Arc<SharedCell> = unsafe {
Arc::increment_strong_count(ptr as *const SharedCell);
Arc::from_raw(ptr as *const SharedCell)
};
assert_eq!(Arc::strong_count(&arc_observer), 2);
let _retained = unsafe { jit_arc_shared_retain(ptr) };
assert_eq!(Arc::strong_count(&arc_observer), 3);
unsafe { jit_arc_shared_release(ptr) };
assert_eq!(Arc::strong_count(&arc_observer), 2);
unsafe { jit_arc_shared_release(ptr) };
assert_eq!(Arc::strong_count(&arc_observer), 1);
drop(arc_observer);
}
#[test]
fn session1_ffi_shared_cell_value_roundtrip_via_lock_helpers() {
let ptr = unsafe {
jit_alloc_shared_cell(ValueWord::from_i64(100).into_raw_bits())
};
unsafe {
jit_shared_lock_contended(ptr);
std::ptr::write(
(ptr as *mut u8).add(SHARED_CELL_VALUE_OFFSET as usize) as *mut u64,
ValueWord::from_i64(500).into_raw_bits(),
);
jit_shared_unlock_contended(ptr);
jit_shared_lock_contended(ptr);
let v = std::ptr::read(
(ptr as *const u8).add(SHARED_CELL_VALUE_OFFSET as usize) as *const u64,
);
jit_shared_unlock_contended(ptr);
assert_eq!(
v,
ValueWord::from_i64(500).into_raw_bits(),
"locked write must be visible to locked read on the same cell"
);
jit_arc_shared_release(ptr);
}
}
}