use wasmtime::{ArrayRef, ArrayRefPre, Caller, Func, Global, GlobalType, Mutability, StructRef};
use wasmtime::{StructRefPre, Val, ValType};
use crate::runtime::StoreData;
use crate::runtime::fuel::{host_func, host_func_async};
use crate::runtime::host::{
abi_result, host_closure_vtable, host_object_vtable, host_value_async, throw_host_error,
type_error, write_submilli_string_struct,
};
use crate::runtime::intrinsic_types::{IntrinsicTypes, intrinsic_types};
use crate::runtime::prelude::iterator::{iterator_result_struct, value_closure_type};
use crate::runtime::prelude::vtable::read_string_units;
use crate::{Type, ValueKind};
pub(super) fn static_value(caller: &mut Caller<'_, StoreData>, tag: &Val) -> wasmtime::Result<Val> {
let key = read_string_units(caller, tag, "static value tag")?;
if let Some(global) = caller.data().static_values.get(&key).copied() {
return Ok(global.get(&mut *caller));
}
let value = build_static_value_object(caller, *tag)?;
let ty = GlobalType::new(
ValType::Ref(wasmtime::RefType::new(true, wasmtime::HeapType::Any)),
Mutability::Const,
);
let global = Global::new(&mut *caller, ty, value)?;
caller.data_mut().static_values.insert(key, global);
Ok(value)
}
fn build_static_value_object(
caller: &mut Caller<'_, StoreData>,
tag: Val,
) -> wasmtime::Result<Val> {
let intr = intrinsic_types(&mut *caller)?;
let text = String::from_utf16_lossy(&read_string_units(caller, &tag, "static value tag")?);
if let Some(signature) = text.strip_prefix("function ") {
let name = signature
.split_once('(')
.map_or(signature, |(name, _)| name);
if let Some((key, function)) = call_signature_function(caller, name) {
return build_converting_closure(caller, &intr, tag, text, key, function);
}
let message = format!("{name} used as a value can't be called");
return build_throwing_closure(caller, &intr, tag, text, message);
}
build_namespace_object(caller, &intr, text)
}
fn call_signature_function(caller: &Caller<'_, StoreData>, name: &str) -> Option<(String, Func)> {
let (prelude, _, _) = crate::runtime::prelude::cached_runtime_package_declarations();
let symbol = prelude.first()?.values.get(name)?;
let ValueKind::Const {
ty: Type::InterfaceRef { mangled, .. },
..
} = &symbol.kind
else {
return None;
};
let key = crate::mangle::extend(mangled, "@call").as_str().to_owned();
let function = caller
.data()
.host_abi
.as_ref()?
.member_functions
.get(&key)
.copied()?;
Some((key, function))
}
fn build_converting_closure(
caller: &mut Caller<'_, StoreData>,
intr: &IntrinsicTypes,
tag: Val,
text: String,
key: String,
function: Func,
) -> wasmtime::Result<Val> {
let closure_vtable = host_closure_vtable(caller)?;
let vtable = vtable_printing(caller, intr, &closure_vtable, text)?;
let arity = function.ty(&*caller).params().len();
let env = match super::super::member::builtin_parameters(&key) {
Some(parameters) => super::super::arguments::with_metadata(caller, tag, parameters)?,
None => tag,
};
let (function_ty, closure_ty) = value_closure_type(caller.engine(), intr, arity)?;
let convert = host_value_async(&mut *caller, function_ty, move |caller, params| {
let key = key.clone();
Box::new(async move {
let args = params.get(1..).unwrap_or_default();
super::super::member::call_builtin(caller, function, None, args, &key).await
})
});
let closure_pre = StructRefPre::new(&mut *caller, closure_ty);
let closure = StructRef::new(
&mut *caller,
&closure_pre,
&[vtable, Val::FuncRef(Some(convert)), env, Val::I64(0)],
)?;
Ok(Val::AnyRef(Some(closure.to_anyref())))
}
fn build_throwing_closure(
caller: &mut Caller<'_, StoreData>,
intr: &IntrinsicTypes,
tag: Val,
text: String,
message: String,
) -> wasmtime::Result<Val> {
let closure_vtable = host_closure_vtable(caller)?;
let vtable = vtable_printing(caller, intr, &closure_vtable, text)?;
let (function_ty, closure_ty) = value_closure_type(caller.engine(), intr, 0)?;
let function = host_func(&mut *caller, function_ty, move |caller, _, _| {
Err(throw_host_error(caller, type_error(message.clone())))
});
let closure_pre = StructRefPre::new(&mut *caller, closure_ty);
let closure = StructRef::new(
&mut *caller,
&closure_pre,
&[vtable, Val::FuncRef(Some(function)), tag, Val::I64(0)],
)?;
Ok(Val::AnyRef(Some(closure.to_anyref())))
}
fn build_namespace_object(
caller: &mut Caller<'_, StoreData>,
intr: &IntrinsicTypes,
text: String,
) -> wasmtime::Result<Val> {
let object_vtable = host_object_vtable(caller)?;
let vtable = vtable_printing(caller, intr, &object_vtable, text)?;
let names_pre = ArrayRefPre::new(&mut *caller, intr.field_names.clone());
let names = ArrayRef::new_fixed(&mut *caller, &names_pre, &[])?;
let fields_pre = ArrayRefPre::new(&mut *caller, intr.object_fields.clone());
let fields = ArrayRef::new_fixed(&mut *caller, &fields_pre, &[])?;
let object_ty = iterator_result_struct(caller.engine(), intr)?;
let object_pre = StructRefPre::new(&mut *caller, object_ty);
let object = StructRef::new(
&mut *caller,
&object_pre,
&[
vtable,
Val::AnyRef(Some(names.to_anyref())),
Val::AnyRef(Some(fields.to_anyref())),
Val::AnyRef(None),
],
)?;
Ok(Val::AnyRef(Some(object.to_anyref())))
}
fn vtable_printing(
caller: &mut Caller<'_, StoreData>,
intr: &IntrinsicTypes,
base: &Val,
text: String,
) -> wasmtime::Result<Val> {
let Val::AnyRef(Some(base)) = base else {
return Err(wasmtime::Error::msg("static value base vtable is null"));
};
let base = base
.as_struct(&mut *caller)?
.ok_or_else(|| wasmtime::Error::msg("static value base vtable is not a struct"))?;
let to_string = host_func_async(
&mut *caller,
intr.to_string_fn.clone(),
move |mut caller, _, results| {
let text = text.clone();
Box::new(async move {
let value = write_submilli_string_struct(&mut caller, &text)?;
*abi_result(results, 0)? = Val::AnyRef(Some(value.to_anyref()));
Ok(())
})
},
);
let slots = [
Val::FuncRef(Some(to_string)),
base.field(&mut *caller, 1)?,
base.field(&mut *caller, 2)?,
base.field(&mut *caller, 3)?,
];
let pre = StructRefPre::new(&mut *caller, intr.vtable.clone());
let vtable = StructRef::new(&mut *caller, &pre, &slots)?;
Ok(Val::AnyRef(Some(vtable.to_anyref())))
}