use wasm_encoder::{TypeSection, ValType};
#[derive(Debug, Clone, Copy)]
pub struct RuntimeFuncIndices {
pub alloc: u32,
pub truncate: u32, pub collect_begin: u32, pub collect_end: u32, pub rebase_i32: u32, pub retain_i32: u32, pub wrap: u32, pub wrap_f64: u32, pub wrap_i32: u32, pub unwrap: u32, pub unwrap_f64: u32, pub unwrap_i32: u32, pub obj_kind: u32, pub obj_tag: u32, pub obj_meta: u32, pub obj_field: u32, pub obj_field_f64: u32, pub obj_field_i32: u32, pub list_cons: u32, pub list_cons_f64: u32, pub int_to_str: u32, pub float_to_str: u32, pub fd_write_buf: u32, pub str_eq: u32, pub str_concat: u32, pub i64_to_str_obj: u32, pub f64_to_str_obj: u32, pub list_take: u32, pub list_drop: u32, pub list_concat: u32, pub list_reverse: u32, pub list_contains: u32, pub list_zip: u32, pub map_get: u32, pub map_set: u32, pub map_has: u32, pub map_keys: u32, pub map_entries: u32, pub vec_from_list: u32, pub vec_get: u32, pub vec_len: u32, pub vec_set: u32, pub vec_new: u32, pub vec_to_list: u32, pub str_len: u32, pub str_byte_len: u32, pub str_find: u32, pub str_starts_with: u32, pub str_ends_with: u32, pub str_contains: u32, pub str_char_at: u32, pub char_from_code: u32, pub char_to_code: u32, pub byte_to_hex: u32, pub byte_from_hex: u32, pub str_trim: u32, pub str_slice: u32, pub str_chars: u32, pub str_split: u32, pub str_join: u32, pub str_replace: u32, pub str_to_lower: u32, pub str_to_upper: u32, pub int_from_str: u32, pub float_from_str: u32, pub count: u32,
pub fd_write_import: u32,
pub adapter: super::super::WasmAdapter,
}
impl RuntimeFuncIndices {
pub fn new(base: u32) -> Self {
let mut i = base;
let mut next = || {
let idx = i;
i += 1;
idx
};
RuntimeFuncIndices {
alloc: next(),
truncate: next(),
collect_begin: next(),
collect_end: next(),
rebase_i32: next(),
retain_i32: next(),
wrap: next(),
wrap_f64: next(),
wrap_i32: next(),
unwrap: next(),
unwrap_f64: next(),
unwrap_i32: next(),
obj_kind: next(),
obj_tag: next(),
obj_meta: next(),
obj_field: next(),
obj_field_f64: next(),
obj_field_i32: next(),
list_cons: next(),
list_cons_f64: next(),
int_to_str: next(),
float_to_str: next(),
fd_write_buf: next(),
str_eq: next(),
str_concat: next(),
i64_to_str_obj: next(),
f64_to_str_obj: next(),
list_take: next(),
list_drop: next(),
list_concat: next(),
list_reverse: next(),
list_contains: next(),
list_zip: next(),
map_get: next(),
map_set: next(),
map_has: next(),
map_keys: next(),
map_entries: next(),
vec_from_list: next(),
vec_get: next(),
vec_len: next(),
vec_set: next(),
vec_new: next(),
vec_to_list: next(),
str_len: next(),
str_byte_len: next(),
str_find: next(),
str_starts_with: next(),
str_ends_with: next(),
str_contains: next(),
str_char_at: next(),
char_from_code: next(),
char_to_code: next(),
byte_to_hex: next(),
byte_from_hex: next(),
str_trim: next(),
str_slice: next(),
str_chars: next(),
str_split: next(),
str_join: next(),
str_replace: next(),
str_to_lower: next(),
str_to_upper: next(),
int_from_str: next(),
float_from_str: next(),
count: i - base,
fd_write_import: 0,
adapter: super::super::WasmAdapter::Aver,
}
}
}
#[derive(Default)]
struct TypeRegistry {
entries: Vec<(Vec<ValType>, Vec<ValType>)>,
}
impl TypeRegistry {
fn intern(
&mut self,
type_section: &mut TypeSection,
params: &[ValType],
results: &[ValType],
) -> u32 {
if let Some((idx, _)) = self
.entries
.iter()
.enumerate()
.find(|(_, (ps, rs))| ps.as_slice() == params && rs.as_slice() == results)
{
return idx as u32;
}
let idx = self.entries.len() as u32;
type_section
.ty()
.function(params.to_vec(), results.to_vec());
self.entries.push((params.to_vec(), results.to_vec()));
idx
}
fn count(&self) -> u32 {
self.entries.len() as u32
}
}
#[derive(Debug, Clone, Copy)]
pub struct RtTypeIndices {
pub alloc: u32, pub i32_to_empty: u32, pub wrap_i64: u32, pub wrap_f64: u32, pub wrap_i32: u32, pub unwrap_i64: u32, pub unwrap_f64: u32, pub unwrap_i32: u32, pub obj_kind: u32, pub obj_tag: u32, pub obj_meta: u32, pub obj_field_i64: u32, pub obj_field_f64: u32, pub obj_field_i32: u32, pub list_cons_i64: u32, pub list_cons_f64: u32, pub print_i64: u32, pub print_f64: u32, pub print_i32: u32, pub int_to_str: u32, pub float_to_str: u32, pub fd_write_buf: u32, pub wasi_fd_write: u32, pub i32_i32_to_i32: u32, pub i64_i32_to_i32: u32, pub i64_to_i32: u32, pub f64_to_i32: u32, pub i32_i64_to_i32: u32, pub i32_i64_i32_to_i32: u32, pub i32_i32_i64_to_i32: u32, pub i32_i32_i64_i32_to_i32: u32, pub i32_i64_i64_to_i32: u32, pub i64_i64_to_i32: u32, pub i64_i64_i32_to_i32: u32, pub f64_to_f64: u32, pub f64_f64_to_f64: u32, pub i32_i32_i32_to_i32: u32, pub empty_to_i32: u32, pub empty_to_i32_i32: u32, pub i32_to_i32_i32: u32, pub i32_i64_to_empty: u32, pub i32_i64_to_i32_i32: u32, pub i64_i64_to_i64: u32, pub empty_to_i64: u32, pub empty_to_empty: u32, pub count: u32, }
pub fn emit_base_type_section(type_section: &mut TypeSection) -> RtTypeIndices {
let mut registry = TypeRegistry::default();
let alloc = registry.intern(type_section, &[ValType::I32], &[ValType::I32]);
let i32_to_empty = registry.intern(type_section, &[ValType::I32], &[]);
let wrap_i64 = registry.intern(
type_section,
&[ValType::I32, ValType::I64, ValType::I32],
&[ValType::I32],
);
let wrap_f64 = registry.intern(type_section, &[ValType::I32, ValType::F64], &[ValType::I32]);
let wrap_i32 = registry.intern(
type_section,
&[ValType::I32, ValType::I32, ValType::I32],
&[ValType::I32],
);
let unwrap_i64 = registry.intern(type_section, &[ValType::I32], &[ValType::I64]);
let unwrap_f64 = registry.intern(type_section, &[ValType::I32], &[ValType::F64]);
let unwrap_i32 = registry.intern(type_section, &[ValType::I32], &[ValType::I32]);
let obj_field_i64 =
registry.intern(type_section, &[ValType::I32, ValType::I32], &[ValType::I64]);
let obj_field_f64 =
registry.intern(type_section, &[ValType::I32, ValType::I32], &[ValType::F64]);
let list_cons_i64 = registry.intern(
type_section,
&[ValType::I64, ValType::I32, ValType::I32],
&[ValType::I32],
);
let list_cons_f64 =
registry.intern(type_section, &[ValType::F64, ValType::I32], &[ValType::I32]);
let print_i64 = registry.intern(type_section, &[ValType::I64], &[]);
let print_f64 = registry.intern(type_section, &[ValType::F64], &[]);
let print_i32 = registry.intern(type_section, &[ValType::I32], &[]);
let fd_write_buf = registry.intern(type_section, &[ValType::I32, ValType::I32], &[]);
let wasi_fd_write = registry.intern(
type_section,
&[ValType::I32, ValType::I32, ValType::I32, ValType::I32],
&[ValType::I32],
);
let i32_i32_to_i32 =
registry.intern(type_section, &[ValType::I32, ValType::I32], &[ValType::I32]);
let i64_i32_to_i32 =
registry.intern(type_section, &[ValType::I64, ValType::I32], &[ValType::I32]);
let i64_to_i32 = registry.intern(type_section, &[ValType::I64], &[ValType::I32]);
let f64_to_i32 = registry.intern(type_section, &[ValType::F64], &[ValType::I32]);
let i32_i64_to_i32 =
registry.intern(type_section, &[ValType::I32, ValType::I64], &[ValType::I32]);
let i32_i64_i32_to_i32 = registry.intern(
type_section,
&[ValType::I32, ValType::I64, ValType::I32],
&[ValType::I32],
);
let i32_i32_i64_to_i32 = registry.intern(
type_section,
&[ValType::I32, ValType::I32, ValType::I64],
&[ValType::I32],
);
let i32_i32_i64_i32_to_i32 = registry.intern(
type_section,
&[ValType::I32, ValType::I32, ValType::I64, ValType::I32],
&[ValType::I32],
);
let i32_i64_i64_to_i32 = registry.intern(
type_section,
&[ValType::I32, ValType::I64, ValType::I64],
&[ValType::I32],
);
let i64_i64_to_i32 =
registry.intern(type_section, &[ValType::I64, ValType::I64], &[ValType::I32]);
let i64_i64_i32_to_i32 = registry.intern(
type_section,
&[ValType::I64, ValType::I64, ValType::I32],
&[ValType::I32],
);
let f64_to_f64 = registry.intern(type_section, &[ValType::F64], &[ValType::F64]);
let f64_f64_to_f64 =
registry.intern(type_section, &[ValType::F64, ValType::F64], &[ValType::F64]);
let i32_i32_i32_to_i32 = registry.intern(
type_section,
&[ValType::I32, ValType::I32, ValType::I32],
&[ValType::I32],
);
let empty_to_i32 = registry.intern(type_section, &[], &[ValType::I32]);
let empty_to_i32_i32 = registry.intern(type_section, &[], &[ValType::I32, ValType::I32]);
let i32_to_i32_i32 =
registry.intern(type_section, &[ValType::I32], &[ValType::I32, ValType::I32]);
let i32_i64_to_empty = registry.intern(type_section, &[ValType::I32, ValType::I64], &[]);
let i32_i64_to_i32_i32 = registry.intern(
type_section,
&[ValType::I32, ValType::I64],
&[ValType::I32, ValType::I32],
);
let i64_i64_to_i64 =
registry.intern(type_section, &[ValType::I64, ValType::I64], &[ValType::I64]);
let empty_to_i64 = registry.intern(type_section, &[], &[ValType::I64]);
let empty_to_empty = registry.intern(type_section, &[], &[]);
RtTypeIndices {
alloc,
i32_to_empty,
wrap_i64,
wrap_f64,
wrap_i32,
unwrap_i64,
unwrap_f64,
unwrap_i32,
obj_kind: unwrap_i32,
obj_tag: unwrap_i32,
obj_meta: unwrap_i32,
obj_field_i64,
obj_field_f64,
obj_field_i32: i32_i32_to_i32,
list_cons_i64,
list_cons_f64,
print_i64,
print_f64,
print_i32,
int_to_str: i64_i32_to_i32,
float_to_str: list_cons_f64,
fd_write_buf,
wasi_fd_write,
i32_i32_to_i32,
i64_i32_to_i32,
i64_to_i32,
f64_to_i32,
i32_i64_to_i32,
i32_i64_i32_to_i32,
i32_i32_i64_to_i32,
i32_i32_i64_i32_to_i32,
i32_i64_i64_to_i32,
i64_i64_to_i32,
i64_i64_i32_to_i32,
f64_to_f64,
f64_f64_to_f64,
i32_i32_i32_to_i32,
empty_to_i32,
empty_to_i32_i32,
i32_to_i32_i32,
i32_i64_to_empty,
i32_i64_to_i32_i32,
i64_i64_to_i64,
empty_to_i64,
empty_to_empty,
count: registry.count(),
}
}
pub fn lookup_type_index(
rti: &RtTypeIndices,
params: &[ValType],
results: &[ValType],
) -> Option<u32> {
if params == [ValType::I32] && results.is_empty() {
return Some(rti.i32_to_empty);
}
if params == [ValType::I32] && results == [ValType::I32] {
return Some(rti.unwrap_i32);
}
if params == [ValType::I32, ValType::I64, ValType::I32] && results == [ValType::I32] {
return Some(rti.wrap_i64);
}
if params == [ValType::I32, ValType::F64] && results == [ValType::I32] {
return Some(rti.wrap_f64);
}
if params == [ValType::I32, ValType::I32, ValType::I32] && results == [ValType::I32] {
return Some(rti.wrap_i32);
}
if params == [ValType::I32] && results == [ValType::I64] {
return Some(rti.unwrap_i64);
}
if params == [ValType::I32] && results == [ValType::F64] {
return Some(rti.unwrap_f64);
}
if params == [ValType::I32, ValType::I32] && results == [ValType::I64] {
return Some(rti.obj_field_i64);
}
if params == [ValType::I32, ValType::I32] && results == [ValType::F64] {
return Some(rti.obj_field_f64);
}
if params == [ValType::I64, ValType::I32, ValType::I32] && results == [ValType::I32] {
return Some(rti.list_cons_i64);
}
if params == [ValType::F64, ValType::I32] && results == [ValType::I32] {
return Some(rti.list_cons_f64);
}
if params == [ValType::I64] && results.is_empty() {
return Some(rti.print_i64);
}
if params == [ValType::F64] && results.is_empty() {
return Some(rti.print_f64);
}
if params == [ValType::I32] && results.is_empty() {
return Some(rti.print_i32);
}
if params == [ValType::I32, ValType::I32] && results.is_empty() {
return Some(rti.fd_write_buf);
}
if params == [ValType::I32, ValType::I32, ValType::I32, ValType::I32]
&& results == [ValType::I32]
{
return Some(rti.wasi_fd_write);
}
if params == [ValType::I64, ValType::I32] && results == [ValType::I32] {
return Some(rti.i64_i32_to_i32);
}
if params == [ValType::I64] && results == [ValType::I32] {
return Some(rti.i64_to_i32);
}
if params == [ValType::F64] && results == [ValType::I32] {
return Some(rti.f64_to_i32);
}
if params == [ValType::I32, ValType::I64] && results == [ValType::I32] {
return Some(rti.i32_i64_to_i32);
}
if params == [ValType::I32, ValType::I64, ValType::I32] && results == [ValType::I32] {
return Some(rti.i32_i64_i32_to_i32);
}
if params == [ValType::I32, ValType::I32, ValType::I64] && results == [ValType::I32] {
return Some(rti.i32_i32_i64_to_i32);
}
if params == [ValType::I32, ValType::I32, ValType::I64, ValType::I32]
&& results == [ValType::I32]
{
return Some(rti.i32_i32_i64_i32_to_i32);
}
if params == [ValType::I32, ValType::I64, ValType::I64] && results == [ValType::I32] {
return Some(rti.i32_i64_i64_to_i32);
}
if params == [ValType::I64, ValType::I64] && results == [ValType::I32] {
return Some(rti.i64_i64_to_i32);
}
if params == [ValType::I64, ValType::I64, ValType::I32] && results == [ValType::I32] {
return Some(rti.i64_i64_i32_to_i32);
}
if params == [ValType::F64] && results == [ValType::F64] {
return Some(rti.f64_to_f64);
}
if params == [ValType::F64, ValType::F64] && results == [ValType::F64] {
return Some(rti.f64_f64_to_f64);
}
if params == [ValType::I32, ValType::I32, ValType::I32] && results == [ValType::I32] {
return Some(rti.i32_i32_i32_to_i32);
}
if params.is_empty() && results == [ValType::I32] {
return Some(rti.empty_to_i32);
}
if params.is_empty() && results == [ValType::I32, ValType::I32] {
return Some(rti.empty_to_i32_i32);
}
if params == [ValType::I32] && results == [ValType::I32, ValType::I32] {
return Some(rti.i32_to_i32_i32);
}
if params == [ValType::I32, ValType::I64] && results.is_empty() {
return Some(rti.i32_i64_to_empty);
}
if params == [ValType::I32, ValType::I64] && results == [ValType::I32, ValType::I32] {
return Some(rti.i32_i64_to_i32_i32);
}
if params == [ValType::I64, ValType::I64] && results == [ValType::I64] {
return Some(rti.i64_i64_to_i64);
}
if params.is_empty() && results == [ValType::I64] {
return Some(rti.empty_to_i64);
}
if params.is_empty() && results.is_empty() {
return Some(rti.empty_to_empty);
}
None
}
pub fn rt_type_index(
rt: &RuntimeFuncIndices,
rti: &RtTypeIndices,
func_idx: u32,
import_func_count: u32,
) -> u32 {
let local_idx = func_idx - import_func_count;
let alloc_local = rt.alloc - import_func_count;
if local_idx == alloc_local {
return rti.alloc;
}
if local_idx == rt.truncate - import_func_count {
return rti.i32_to_empty;
}
if local_idx == rt.collect_begin - import_func_count {
return rti.i32_to_empty;
}
if local_idx == rt.collect_end - import_func_count {
return rti.empty_to_empty;
}
if local_idx == rt.rebase_i32 - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.retain_i32 - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.wrap - import_func_count {
return rti.wrap_i64;
}
if local_idx == rt.wrap_f64 - import_func_count {
return rti.wrap_f64;
}
if local_idx == rt.wrap_i32 - import_func_count {
return rti.wrap_i32;
}
if local_idx == rt.unwrap - import_func_count {
return rti.unwrap_i64;
}
if local_idx == rt.unwrap_f64 - import_func_count {
return rti.unwrap_f64;
}
if local_idx == rt.unwrap_i32 - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.obj_kind - import_func_count {
return rti.obj_kind;
}
if local_idx == rt.obj_tag - import_func_count {
return rti.obj_tag;
}
if local_idx == rt.obj_meta - import_func_count {
return rti.obj_meta;
}
if local_idx == rt.obj_field - import_func_count {
return rti.obj_field_i64;
}
if local_idx == rt.obj_field_f64 - import_func_count {
return rti.obj_field_f64;
}
if local_idx == rt.obj_field_i32 - import_func_count {
return rti.obj_field_i32;
}
if local_idx == rt.list_cons - import_func_count {
return rti.list_cons_i64;
}
if local_idx == rt.list_cons_f64 - import_func_count {
return rti.list_cons_f64;
}
if local_idx == rt.int_to_str - import_func_count {
return rti.int_to_str;
}
if local_idx == rt.float_to_str - import_func_count {
return rti.float_to_str;
}
if local_idx == rt.fd_write_buf - import_func_count {
return rti.fd_write_buf;
}
if local_idx == rt.str_eq - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.str_concat - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.i64_to_str_obj - import_func_count {
return rti.i64_to_i32;
}
if local_idx == rt.f64_to_str_obj - import_func_count {
return rti.f64_to_i32;
}
if local_idx == rt.list_take - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.list_drop - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.list_concat - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.list_reverse - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.list_contains - import_func_count {
return rti.i32_i64_to_i32;
}
if local_idx == rt.list_zip - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.map_get - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.map_set - import_func_count {
return rti.i32_i32_i64_i32_to_i32;
}
if local_idx == rt.map_has - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.map_keys - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.map_entries - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.vec_from_list - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.vec_get - import_func_count {
return rti.i32_i64_to_i32;
}
if local_idx == rt.vec_len - import_func_count {
return rti.unwrap_i64;
}
if local_idx == rt.vec_set - import_func_count {
return rti.i32_i64_i64_to_i32;
}
if local_idx == rt.vec_new - import_func_count {
return rti.i64_i64_i32_to_i32;
}
if local_idx == rt.vec_to_list - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.str_len - import_func_count {
return rti.unwrap_i64;
}
if local_idx == rt.str_byte_len - import_func_count {
return rti.unwrap_i64;
}
if local_idx == rt.str_find - import_func_count {
return rti.i32_i32_i32_to_i32;
}
if local_idx == rt.str_starts_with - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.str_ends_with - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.str_contains - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.str_char_at - import_func_count {
return rti.i32_i64_to_i32;
}
if local_idx == rt.char_from_code - import_func_count {
return rti.i64_to_i32;
}
if local_idx == rt.char_to_code - import_func_count {
return rti.unwrap_i64;
}
if local_idx == rt.byte_to_hex - import_func_count {
return rti.i64_to_i32;
}
if local_idx == rt.byte_from_hex - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.str_trim - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.str_slice - import_func_count {
return rti.i32_i32_i32_to_i32;
}
if local_idx == rt.str_chars - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.str_split - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.str_join - import_func_count {
return rti.i32_i32_to_i32;
}
if local_idx == rt.str_replace - import_func_count {
return rti.i32_i32_i32_to_i32;
}
if local_idx == rt.str_to_lower - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.str_to_upper - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.int_from_str - import_func_count {
return rti.unwrap_i32;
}
if local_idx == rt.float_from_str - import_func_count {
return rti.unwrap_i32;
}
panic!(
"Unknown runtime function index: {} (base={})",
func_idx, import_func_count
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn base_type_section_interns_shared_signatures() {
let mut type_section = TypeSection::new();
let rti = emit_base_type_section(&mut type_section);
assert_eq!(rti.alloc, rti.unwrap_i32);
assert_eq!(rti.obj_kind, rti.unwrap_i32);
assert_eq!(rti.obj_tag, rti.unwrap_i32);
assert_eq!(rti.obj_meta, rti.unwrap_i32);
assert_eq!(rti.list_cons_f64, rti.float_to_str);
}
#[test]
fn abi_lookup_covers_effect_signatures() {
let mut type_section = TypeSection::new();
let rti = emit_base_type_section(&mut type_section);
assert_eq!(
lookup_type_index(&rti, &[ValType::I64, ValType::I64], &[ValType::I64]),
Some(rti.i64_i64_to_i64)
);
assert_eq!(
lookup_type_index(&rti, &[], &[ValType::I64]),
Some(rti.empty_to_i64)
);
assert_eq!(
lookup_type_index(
&rti,
&[ValType::I32, ValType::I64],
&[ValType::I32, ValType::I32]
),
Some(rti.i32_i64_to_i32_i32)
);
}
}