mod builders;
mod lower;
mod validate;
pub use builders::{
ArrayTypeBuilder, ForwardRefElementBuilder, ForwardRefFieldBuilder, ForwardRefFuncValBuilder,
FuncTypeBuilder, StructTypeBuilder,
};
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::canon::{AggKind, CanonicalTypeId, GroupId, ModuleType};
use crate::engine::Engine;
use crate::value::{
ArrayType, FieldType, Finality, FuncType, HeapType, StorageType, StructType, ValType,
};
use crate::{Error, Result};
static NEXT_BUILDER_ID: AtomicUsize = AtomicUsize::new(0);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct PendingType {
builder_id: usize,
index: u32,
kind: AggKind,
}
enum FieldDef {
Registered(FieldType),
Forward {
target: PendingType,
mutable: bool,
nullable: bool,
},
}
enum ValDef {
Registered(ValType),
Forward { target: PendingType, nullable: bool },
}
enum SuperDef {
Forward(u32),
Struct(StructType),
Array(ArrayType),
Func(FuncType),
}
enum MemberDef {
Struct {
finality: Finality,
supertype: Option<SuperDef>,
fields: Vec<FieldDef>,
},
Array {
finality: Finality,
supertype: Option<SuperDef>,
element: FieldDef,
},
Func {
finality: Finality,
supertype: Option<SuperDef>,
params: Vec<ValDef>,
results: Vec<ValDef>,
},
}
impl MemberDef {
fn supertype(&self) -> Option<&SuperDef> {
match self {
MemberDef::Struct { supertype, .. }
| MemberDef::Array { supertype, .. }
| MemberDef::Func { supertype, .. } => supertype.as_ref(),
}
}
}
pub struct RecGroupBuilder {
engine: Engine,
builder_id: usize,
error: Option<Error>,
members: Vec<Option<MemberDef>>,
}
impl core::fmt::Debug for RecGroupBuilder {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RecGroupBuilder")
.field("members", &self.members.len())
.finish_non_exhaustive()
}
}
impl RecGroupBuilder {
pub fn new(engine: &Engine) -> Self {
RecGroupBuilder {
engine: engine.clone(),
builder_id: NEXT_BUILDER_ID.fetch_add(1, Ordering::Relaxed),
error: None,
members: Vec::new(),
}
}
fn declare(&mut self, kind: AggKind) -> PendingType {
let index = u32::try_from(self.members.len()).expect("too many types in a rec group");
self.members.push(None);
PendingType {
builder_id: self.builder_id,
index,
kind,
}
}
pub fn declare_struct(&mut self) -> PendingType {
self.declare(AggKind::Struct)
}
pub fn declare_array(&mut self) -> PendingType {
self.declare(AggKind::Array)
}
pub fn declare_func(&mut self) -> PendingType {
self.declare(AggKind::Func)
}
#[track_caller]
fn check_owns(&self, ty: PendingType) {
assert_eq!(
ty.builder_id, self.builder_id,
"`PendingType` used with a different `RecGroupBuilder` than it came from"
);
}
fn record_error(&mut self, error: Error) {
if self.error.is_none() {
self.error = Some(error);
}
}
fn check_engine(&mut self, same_engine: bool, what: &str) {
if !same_engine {
self.record_error(crate::format_err!(
"{what} is associated with a different engine"
));
}
}
fn check_field_engine(&mut self, ty: &FieldType, what: &str) {
if let StorageType::ValType(v) = ty.element_type() {
self.check_val_engine(v, what);
}
}
fn check_val_engine(&mut self, ty: &ValType, what: &str) {
let same = match ty {
ValType::Ref(rt) => match rt.heap_type() {
HeapType::ConcreteStruct(t) => t.engine().same(&self.engine),
HeapType::ConcreteArray(t) => t.engine().same(&self.engine),
HeapType::ConcreteFunc(t) => t.engine().same(&self.engine),
_ => true,
},
_ => true,
};
self.check_engine(same, what);
}
#[track_caller]
pub fn define_struct(&mut self, ty: PendingType) -> StructTypeBuilder<'_> {
self.check_owns(ty);
assert_eq!(
ty.kind,
AggKind::Struct,
"handle was not declared as a struct type"
);
StructTypeBuilder::new(self, ty.index)
}
#[track_caller]
pub fn define_array(&mut self, ty: PendingType) -> ArrayTypeBuilder<'_> {
self.check_owns(ty);
assert_eq!(
ty.kind,
AggKind::Array,
"handle was not declared as an array type"
);
ArrayTypeBuilder::new(self, ty.index)
}
#[track_caller]
pub fn define_func(&mut self, ty: PendingType) -> FuncTypeBuilder<'_> {
self.check_owns(ty);
assert_eq!(
ty.kind,
AggKind::Func,
"handle was not declared as a function type"
);
FuncTypeBuilder::new(self, ty.index)
}
pub fn build(self) -> Result<RecGroup> {
let RecGroupBuilder {
engine,
builder_id,
error,
members,
} = self;
if let Some(error) = error {
return Err(error);
}
let n = members.len();
let mut defs = Vec::with_capacity(n);
for (i, m) in members.into_iter().enumerate() {
defs.push(
m.ok_or_else(|| Error::msg(format!("type {i} was declared but never defined")))?,
);
}
let mut externals = Vec::new();
let mut lowered = Vec::with_capacity(n);
let mut kinds = Vec::with_capacity(n);
for def in &defs {
let (finality, supertype, body, kind) = lower::lower(def, n, &mut externals);
lowered.push(ModuleType {
group: 0,
finality,
supertype,
body,
});
kinds.push(kind);
}
let (ids, group) = engine.intern_host_group(&lowered, &externals);
let group = RecGroup {
engine: engine.clone(),
builder_id,
group,
ids,
kinds,
};
for (i, def) in defs.iter().enumerate() {
validate::supertype(&engine, &group, i, def.supertype())?;
}
Ok(group)
}
}
#[derive(Debug)]
pub struct RecGroup {
engine: Engine,
builder_id: usize,
group: GroupId,
ids: Vec<CanonicalTypeId>,
kinds: Vec<AggKind>,
}
impl Drop for RecGroup {
fn drop(&mut self) {
self.engine.release_group(self.group);
}
}
impl RecGroup {
pub fn len(&self) -> usize {
self.ids.len()
}
pub fn is_empty(&self) -> bool {
self.ids.is_empty()
}
#[track_caller]
fn index_of(&self, ty: PendingType) -> usize {
assert_eq!(
ty.builder_id, self.builder_id,
"`PendingType` used with a different `RecGroup` than it came from"
);
ty.index as usize
}
pub fn get_struct(&self, ty: PendingType) -> Option<StructType> {
let i = self.index_of(ty);
(self.kinds[i] == AggKind::Struct).then(|| StructType::from_id(&self.engine, self.ids[i]))
}
pub fn get_array(&self, ty: PendingType) -> Option<ArrayType> {
let i = self.index_of(ty);
(self.kinds[i] == AggKind::Array).then(|| ArrayType::from_id(&self.engine, self.ids[i]))
}
pub fn get_func(&self, ty: PendingType) -> Option<FuncType> {
let i = self.index_of(ty);
(self.kinds[i] == AggKind::Func).then(|| FuncType::from_id(&self.engine, self.ids[i]))
}
pub fn types(&self) -> impl ExactSizeIterator<Item = HeapType> + '_ {
self.ids
.iter()
.zip(&self.kinds)
.map(|(&id, &kind)| match kind {
AggKind::Struct => HeapType::ConcreteStruct(StructType::from_id(&self.engine, id)),
AggKind::Array => HeapType::ConcreteArray(ArrayType::from_id(&self.engine, id)),
AggKind::Func => HeapType::ConcreteFunc(FuncType::from_id(&self.engine, id)),
})
}
}