///|
/// Resource counts defining an instance's native storage. Legacy contexts allow
/// resources to be configured after allocation and retain the fixed public ABI.
pub(all) enum ContextShape {
Legacy
Module(Int32, Int32, Int32)
} derive(Eq, Debug)
///|
pub fn ContextShape::from_module(module_ : @types.Module) -> ContextShape {
let mut memories = module_.memories.length()
let mut tables = module_.tables.length()
let mut globals = module_.globals.length()
for entry in module_.imports {
match entry.desc {
Memory(_) => memories += 1
Table(_) => tables += 1
Global(_) => globals += 1
_ => ()
}
}
Module(memories, tables, globals)
}
///|
/// Includes the trailing pointer arrays, with checked arithmetic before native
/// allocation. The descriptor itself is shared immutable module metadata.
pub fn ContextShape::descriptor(self : ContextShape) -> ReadOnlyArray[Int32]? {
guard self is Module(memories, tables, globals) else { return None }
// Bound byte offsets to signed 32-bit displacement/allocation arithmetic.
if memories < 0 ||
tables < 0 ||
globals < 0 ||
memories.to_int64() * 8L + tables.to_int64() * 24L + 128L > 2147483647L {
return None
}
let offsets = FixedArray::make(30, -1)
let mut next = 0
// Pointer-width fields first; pack scalar counts together at the end.
for index in [0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 15, 16, 17, 8, 9, 13, 14] {
let present = match index {
0 | 1 | 2 | 12 | 13 => memories > 0
4 | 5 | 7 | 8 | 10 | 11 => tables > 0
6 => globals > 0
_ => true
}
if present {
offsets[index] = next
next += if index == 8 || index == 9 || index == 13 || index == 14 {
4
} else {
8
}
}
}
next = (next + 7) / 8 * 8
offsets[18] = 8
offsets[19] = 8
offsets[20] = 0
offsets[21] = 16
offsets[22] = 8
offsets[24] = next
next += memories * 8
offsets[25] = next
next += tables * 8
offsets[26] = next
next += tables * 8
offsets[27] = next
next += tables * 8
offsets[23] = next
offsets[28] = memories
offsets[29] = tables
Some(ReadOnlyArray::from_array(offsets[:]))
}
///|
pub extend ContextShape with Eq::{equal, not_equal}
///|
pub extend ContextShape with Debug::{to_repr}
///|
/// Immutable layout retained by a loaded module and shared by its instances.
pub struct ContextLayout {
priv fields : Bytes
}
///|
pub fn ContextLayout::new(shape : ContextShape) -> ContextLayout? {
guard shape.descriptor() is Some(words) else { return None }
let fields = Bytes::makei(48, i => {
if i < 18 {
(if words[i] < 0 { 255 } else { words[i] }).to_byte()
} else if i < 20 {
0
} else {
(words[i / 4 + 18] >> (i % 4 * 8)).to_byte()
}
})
Some({ fields, })
}
///|
pub fn ContextLayout::field(self : ContextLayout, index : Int32) -> Int32 {
if index < 18 {
let offset = self.fields[index].to_int()
if offset == 255 {
-1
} else {
offset
}
} else if index < 23 {
match index {
20 => 0
21 => 16
_ => 8
}
} else {
let start = (index - 18) * 4
self.fields[start].to_int() |
(self.fields[start + 1].to_int() << 8) |
(self.fields[start + 2].to_int() << 16) |
(self.fields[start + 3].to_int() << 24)
}
}
///|
/// Immutable packed descriptor at the native FFI boundary; retaining it keeps the
/// descriptor valid independently of the loaded artifact's lifetime.
pub fn ContextLayout::words(self : ContextLayout) -> Bytes {
self.fields
}