///|
priv struct StoreReferences {
resource_budget : ResourceBudget?
closed : Ref[Bool]
tags : Array[@types.FuncType]
heap : MarkSweepHeap
funcref_registry : StoreFuncrefRegistry
native : NativeReferenceState
mut instantiation : RollbackRegistrar?
mut next_exception : Int32
mut next_wrapped_extern : Int32
mut exns : Map[Int32, ExnInstance]
mut wrapped_externs : Map[Int32, @types.Value]
auxiliary_leases : Map[(Int32, Int64), (ResourceLease, ResourceLease)]
} derive(Debug)
///|
fn StoreReferences::new(
resource_budget : ResourceBudget?,
closed : Ref[Bool],
tags : Array[@types.FuncType],
heap : MarkSweepHeap,
funcref_registry : StoreFuncrefRegistry,
native : NativeReferenceState,
) -> StoreReferences {
{
resource_budget,
closed,
tags,
heap,
funcref_registry,
native,
instantiation: None,
next_exception: 0,
next_wrapped_extern: 0,
exns: Map([]),
wrapped_externs: Map([]),
auxiliary_leases: Map([]),
}
}
///|
fn StoreReferences::ensure_open(
self : StoreReferences,
) -> Unit raise RuntimeError {
if self.closed.val {
raise StoreClosed
}
}
///|
fn StoreReferences::on_instantiation_rollback(
self : StoreReferences,
rollback : () -> Unit,
) -> Unit {
if self.instantiation is Some(registrar) {
registrar.on_rollback(rollback)
}
}
///|
fn StoreReferences::wrap_as_extern(
self : StoreReferences,
value : @types.Value,
) -> @types.Value raise RuntimeError {
self.ensure_open()
if self.next_wrapped_extern == 2147483647 {
raise AllocationTooLarge
}
self.reserve_auxiliary_handle(0, self.next_wrapped_extern.to_int64(), 16L)
let idx = self.next_wrapped_extern
self.next_wrapped_extern += 1
self.wrapped_externs.set(idx, value)
self.on_instantiation_rollback(fn() {
self.wrapped_externs.remove(idx)
self.release_auxiliary_handle(0, idx.to_int64())
})
ExternRef(-1 - idx) // -1 for index 0, -2 for index 1, etc.
}
///|
fn StoreReferences::unwrap_extern(
self : StoreReferences,
value : @types.Value,
) -> @types.Value raise RuntimeError {
self.ensure_open()
match value {
ExternRef(idx) =>
if idx < 0 {
// Wrapped internal value
let wrapped_idx = -1 - idx
match self.wrapped_externs.get(wrapped_idx) {
Some(value) => value
None => raise UndefinedElement
}
} else {
// Host externref - return as-is (it's already an internal representation)
value
}
Null => Null
_ => value
}
}
///|
fn StoreReferences::alloc_exn(
self : StoreReferences,
tag_addr : Int32,
values : Array[@types.Value],
) -> Int32 raise RuntimeError {
self.ensure_open()
if self.next_exception == 2147483647 {
raise AllocationTooLarge
}
self.reserve_auxiliary_handle(
1,
self.next_exception.to_int64(),
16L + values.length().to_int64() * 16L,
)
let index = self.next_exception
self.next_exception += 1
self.exns.set(index, { tag_addr, values: values.copy(), })
self.record_exception_allocation(index)
index
}
///|
fn StoreReferences::get_exn(
self : StoreReferences,
idx : Int32,
) -> ExnInstance raise RuntimeError {
self.ensure_open()
match self.exns.get(idx) {
Some(exception) => { ..exception, values: exception.values.copy(), }
None => raise UndefinedElement
}
}
///|
#cfg(target="native")
fn StoreReferences::get_tag_type(
self : StoreReferences,
idx : Int32,
) -> @types.FuncType raise RuntimeError {
self.ensure_open()
if idx < 0 || idx >= self.tags.length() {
raise UndefinedElement
}
self.tags[idx]
}
///|
fn StoreReferences::reserve_auxiliary_handle(
self : StoreReferences,
kind : Int32,
handle : Int64,
size : Int64,
) -> Unit raise RuntimeError {
if self.resource_budget is Some(budget) {
let bytes = budget.reserve(GcBytes, size)
errdefer bytes.release()
let objects = budget.reserve(GcObjects, 1L)
self.auxiliary_leases.set((kind, handle), (bytes, objects))
}
}
///|
#cfg(target="native")
fn StoreReferences::release_auxiliary_handle(
self : StoreReferences,
kind : Int32,
handle : Int64,
) -> Unit {
self.auxiliary_leases.remove((kind, handle))
}
///|
#cfg(target="wasm")
fn StoreReferences::release_auxiliary_handle(
self : StoreReferences,
kind : Int32,
handle : Int64,
) -> Unit {
if self.auxiliary_leases.get((kind, handle)) is Some((bytes, objects)) {
bytes.release()
objects.release()
}
self.auxiliary_leases.remove((kind, handle))
}