use crate::extern_::{val_matches_in_store, Tag};
use crate::store::{AsContext, AsContextMut, ExnEntity};
use crate::value::gc_ref::{ExnRef, Rooted};
use crate::value::{ExnType, HeapType, Val, ValType};
use crate::{Error, Result};
#[derive(Debug)]
pub struct ExnRefPre {
store: u64,
ty: ExnType,
}
impl ExnRefPre {
pub fn new(mut store: impl AsContextMut, ty: ExnType) -> Self {
let ctx = store.as_context_mut();
assert!(
ty.engine().same(ctx.engine()),
"exception type used with a store from a different engine"
);
ExnRefPre {
store: ctx.inner().store_id(),
ty,
}
}
}
impl ExnRef {
pub fn new(
mut store: impl AsContextMut,
allocator: &ExnRefPre,
tag: &Tag,
fields: &[Val],
) -> Result<Rooted<ExnRef>> {
let mut ctx = store.as_context_mut();
ctx.inner().check_handle(allocator.store);
let tag_ty = &ctx.inner().tag(*tag).ty;
if allocator.ty.func() != tag_ty.ty() {
return Err(Error::msg("exception allocator and tag types do not match"));
}
let params: Vec<ValType> = allocator.ty.func().params().collect();
if fields.len() != params.len() {
return Err(Error::msg("wrong number of exception fields"));
}
for (v, ty) in fields.iter().zip(¶ms) {
if !val_matches_in_store(ctx.inner(), v, ty)? {
return Err(Error::msg("exception field value has the wrong type"));
}
}
let exn = ctx.store_mut().gc_alloc_exn(ExnEntity {
tag: *tag,
args: fields.to_vec(),
backtrace: None,
})?;
let inner = ctx.inner_mut();
inner.push_gc_root(exn.raw(), crate::canon::RefKind::Exn);
Ok(Rooted::from_raw_gen(
exn.raw(),
inner.exn_generation(exn.raw()).unwrap_or(0),
inner.store_id(),
crate::canon::RefKind::Exn,
))
}
}
impl Rooted<ExnRef> {
pub fn field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val> {
let mut ctx = store.as_context_mut();
let value = ctx
.inner()
.exn_checked(*self)?
.args
.get(index)
.copied()
.ok_or_else(|| Error::msg("exception field index out of bounds"))?;
ctx.inner_mut().root_host_result(value)
}
pub fn tag(&self, mut store: impl AsContextMut) -> Result<Tag> {
Ok(store.as_context_mut().inner().exn_checked(*self)?.tag)
}
pub fn ty(&self, store: impl AsContext) -> Result<ExnType> {
let ctx = store.as_context();
let tag = ctx.inner().exn_checked(*self)?.tag;
ExnType::from_tag_type(&ctx.inner().tag(tag).ty)
}
pub fn matches_ty(&self, store: impl AsContext, ty: &HeapType) -> Result<bool> {
let ctx = store.as_context();
let _ = ctx.inner().exn_checked(*self)?;
Ok(HeapType::Exn.matches(ty))
}
}