use indexmap::IndexMap;
use std::mem;
use wasm_encoder::{Function, MemArg};
use wit_parser::TypeId;
const VERSION: u32 = 2;
#[derive(Default)]
pub struct Metadata {
pub import_funcs: Vec<ImportFunc>,
pub export_funcs: Vec<ExportFunc>,
pub resources: Vec<Resource>,
pub records: Vec<Record>,
pub flags: Vec<Flags>,
pub tuples: Vec<Tuple>,
pub variants: Vec<Variant>,
pub enums: Vec<Enum>,
pub options: Vec<WitOption>,
pub results: Vec<WitResult>,
pub lists: Vec<List>,
pub fixed_length_lists: Vec<FixedLengthList>,
pub futures: Vec<Future>,
pub streams: Vec<Stream>,
pub aliases: Vec<Alias>,
}
pub struct ImportFunc {
pub interface: Option<String>,
pub name: String,
pub sync_import_elem_index: Option<u32>,
pub async_import_elem_index: Option<u32>,
pub async_import_lift_results_elem_index: Option<u32>,
pub args: Vec<Type>,
pub result: Option<Type>,
pub async_abi_area: Option<(usize, usize)>,
}
pub struct ExportFunc {
pub interface: Option<String>,
pub name: String,
pub async_export_task_return_elem_index: Option<u32>,
pub args: Vec<Type>,
pub result: Option<Type>,
}
pub struct Resource {
pub id: TypeId,
pub interface: Option<String>,
pub name: String,
pub drop_elem_index: u32,
pub new_elem_index: Option<u32>,
pub rep_elem_index: Option<u32>,
}
pub struct Record {
pub id: TypeId,
pub interface: Option<String>,
pub name: String,
pub fields: Vec<(String, Type)>,
}
pub struct Flags {
pub id: TypeId,
pub interface: Option<String>,
pub name: String,
pub names: Vec<String>,
}
pub struct Tuple {
pub id: TypeId,
pub interface: Option<String>,
pub name: Option<String>,
pub types: Vec<Type>,
}
pub struct Variant {
pub id: TypeId,
pub interface: Option<String>,
pub name: String,
pub cases: Vec<(String, Option<Type>)>,
}
pub struct Enum {
pub id: TypeId,
pub interface: Option<String>,
pub name: String,
pub names: Vec<String>,
}
pub struct WitOption {
pub id: TypeId,
pub interface: Option<String>,
pub name: Option<String>,
pub ty: Type,
}
pub struct WitResult {
pub id: TypeId,
pub interface: Option<String>,
pub name: Option<String>,
pub ok: Option<Type>,
pub err: Option<Type>,
}
pub struct List {
pub id: TypeId,
pub interface: Option<String>,
pub name: Option<String>,
pub ty: Type,
}
pub struct FixedLengthList {
pub id: TypeId,
pub interface: Option<String>,
pub name: Option<String>,
pub len: u32,
pub ty: Type,
}
pub struct Future {
pub id: TypeId,
pub interface: Option<String>,
pub name: Option<String>,
pub ty: Option<Type>,
pub new_elem_index: u32,
pub read_elem_index: u32,
pub write_elem_index: u32,
pub cancel_read_elem_index: u32,
pub cancel_write_elem_index: u32,
pub drop_readable_elem_index: u32,
pub drop_writable_elem_index: u32,
pub lift_elem_index: Option<u32>,
pub lower_elem_index: Option<u32>,
pub abi_payload_size: usize,
pub abi_payload_align: usize,
}
pub struct Stream {
pub id: TypeId,
pub interface: Option<String>,
pub name: Option<String>,
pub ty: Option<Type>,
pub new_elem_index: u32,
pub read_elem_index: u32,
pub write_elem_index: u32,
pub cancel_read_elem_index: u32,
pub cancel_write_elem_index: u32,
pub drop_readable_elem_index: u32,
pub drop_writable_elem_index: u32,
pub lift_elem_index: Option<u32>,
pub lower_elem_index: Option<u32>,
pub abi_payload_size: usize,
pub abi_payload_align: usize,
}
pub struct Alias {
pub id: TypeId,
pub interface: Option<String>,
pub name: String,
pub ty: Type,
}
#[derive(Copy, Clone)]
pub enum Type {
U8,
U16,
U32,
U64,
S8,
S16,
S32,
S64,
Bool,
Char,
F32,
F64,
String,
ErrorContext,
Record(usize),
Own(usize),
Borrow(usize),
Flags(usize),
Tuple(usize),
Variant(usize),
Enum(usize),
Option(usize),
Result(usize),
List(usize),
FixedLengthList(usize),
Future(usize),
Stream(usize),
Alias(usize),
}
struct Encoder {
data: Vec<u8>,
table_base: u32,
memory_base: u32,
relocs: Vec<Reloc>,
strings: IndexMap<String, SymbolId>,
symbol_offsets: Vec<usize>,
}
struct Reloc {
sym: SymbolId,
offset: usize,
addend: usize,
kind: RelocKind,
}
enum RelocKind {
Data,
Table,
}
#[derive(Debug, PartialEq, Copy, Clone)]
struct SymbolId(usize);
impl SymbolId {
const TABLE: SymbolId = SymbolId(usize::MAX);
}
impl Metadata {
pub fn encode(&self, table_base: u32, memory_base: u32) -> (u32, Vec<u8>, Option<Function>) {
let mut encoder = Encoder {
data: Vec::new(),
table_base,
memory_base,
relocs: Vec::new(),
strings: IndexMap::new(),
symbol_offsets: Vec::new(),
};
let import_funcs = encoder.encode_list(&self.import_funcs, Encoder::encode_import_funcs);
let export_funcs = encoder.encode_list(&self.export_funcs, Encoder::encode_export_funcs);
let resources = encoder.encode_list(&self.resources, Encoder::encode_resources);
let records = encoder.encode_list(&self.records, Encoder::encode_records);
let flags = encoder.encode_list(&self.flags, Encoder::encode_flags);
let tuples = encoder.encode_list(&self.tuples, Encoder::encode_tuples);
let variants = encoder.encode_list(&self.variants, Encoder::encode_variants);
let enums = encoder.encode_list(&self.enums, Encoder::encode_enums);
let options = encoder.encode_list(&self.options, Encoder::encode_options);
let results = encoder.encode_list(&self.results, Encoder::encode_results);
let lists = encoder.encode_list(&self.lists, Encoder::encode_lists);
let fixed_length_lists =
encoder.encode_list(&self.fixed_length_lists, Encoder::encode_fixed_length_lists);
let futures = encoder.encode_list(&self.futures, Encoder::encode_futures);
let streams = encoder.encode_list(&self.streams, Encoder::encode_streams);
let aliases = encoder.encode_list(&self.aliases, Encoder::encode_aliases);
let sym_metadata = encoder.symbol();
encoder.bind(sym_metadata);
encoder.put_u32(VERSION);
for (sym, len) in [
import_funcs,
export_funcs,
resources,
records,
flags,
tuples,
variants,
enums,
options,
results,
lists,
fixed_length_lists,
futures,
streams,
aliases,
] {
encoder.put_usize(len);
if len > 0 {
encoder.memory_ptr(sym);
} else {
encoder.put_usize(0);
}
}
encoder.encode_strings();
let apply_relocs = encoder.generate_apply_relocs();
(
u32::try_from(encoder.symbol_offsets[sym_metadata.0]).unwrap(),
encoder.finish(),
apply_relocs,
)
}
}
impl Encoder {
fn encode_list<T>(
&mut self,
list: &[T],
encode: impl Fn(&mut Self, &[T]),
) -> (SymbolId, usize) {
let ret = self.symbol();
self.bind(ret);
encode(self, list);
(ret, list.len())
}
fn encode_import_funcs(&mut self, funcs: &[ImportFunc]) {
let mut deferred_args = Vec::new();
for func in funcs {
let ImportFunc {
interface,
name,
sync_import_elem_index,
async_import_elem_index,
async_import_lift_results_elem_index,
args,
result,
async_abi_area,
} = func;
self.opt_string_ptr(interface.as_deref());
self.string_ptr(name);
self.opt_elem_index(*sync_import_elem_index);
self.opt_elem_index(*async_import_elem_index);
self.opt_elem_index(*async_import_lift_results_elem_index);
self.list(args, &mut deferred_args);
self.opt_ty(result.as_ref());
match async_abi_area {
Some((size, align)) => {
self.put_usize(*size);
self.put_usize(*align);
}
None => {
self.put_usize(0);
self.put_usize(0);
}
}
}
for (sym, args) in deferred_args {
self.bind(sym);
for arg in args {
self.ty(arg);
}
}
}
fn encode_export_funcs(&mut self, funcs: &[ExportFunc]) {
let mut deferred_args = Vec::new();
for func in funcs {
let ExportFunc {
interface,
name,
async_export_task_return_elem_index,
args,
result,
} = func;
self.opt_string_ptr(interface.as_deref());
self.string_ptr(name);
self.opt_elem_index(*async_export_task_return_elem_index);
self.list(args, &mut deferred_args);
self.opt_ty(result.as_ref());
}
for (sym, args) in deferred_args {
self.bind(sym);
for arg in args {
self.ty(arg);
}
}
}
fn encode_resources(&mut self, resources: &[Resource]) {
for resource in resources {
let Resource {
id: _,
interface,
name,
drop_elem_index,
new_elem_index,
rep_elem_index,
} = resource;
self.opt_string_ptr(interface.as_deref());
self.string_ptr(name);
self.elem_index(*drop_elem_index);
self.opt_elem_index(*new_elem_index);
self.opt_elem_index(*rep_elem_index);
}
}
fn encode_records(&mut self, records: &[Record]) {
let mut deferred_fields = Vec::new();
for record in records {
let Record {
id: _,
interface,
name,
fields,
} = record;
self.opt_string_ptr(interface.as_deref());
self.string_ptr(name);
self.list(fields, &mut deferred_fields);
}
for (sym, fields) in deferred_fields {
self.bind(sym);
for (name, ty) in fields {
self.string_ptr(name);
self.ty(ty);
}
}
}
fn encode_flags(&mut self, flags: &[Flags]) {
let mut deferred = Vec::new();
for flags in flags {
let Flags {
id: _,
interface,
name,
names,
} = flags;
self.opt_string_ptr(interface.as_deref());
self.string_ptr(name);
self.list(names, &mut deferred);
}
for (sym, names) in deferred {
self.bind(sym);
for name in names {
self.string_ptr(name);
}
}
}
fn encode_tuples(&mut self, tuples: &[Tuple]) {
let mut deferred = Vec::new();
for tuple in tuples {
let Tuple {
id: _,
interface,
name,
types,
} = tuple;
self.opt_string_ptr(interface.as_deref());
self.opt_string_ptr(name.as_deref());
self.list(types, &mut deferred);
}
for (sym, types) in deferred {
self.bind(sym);
for ty in types {
self.ty(ty);
}
}
}
fn encode_variants(&mut self, variants: &[Variant]) {
let mut deferred = Vec::new();
for variant in variants {
let Variant {
id: _,
interface,
name,
cases,
} = variant;
self.opt_string_ptr(interface.as_deref());
self.string_ptr(name);
self.list(cases, &mut deferred);
}
for (sym, cases) in deferred {
self.bind(sym);
for (name, ty) in cases {
self.string_ptr(name);
self.opt_ty(ty.as_ref());
}
}
}
fn encode_enums(&mut self, enums: &[Enum]) {
let mut deferred = Vec::new();
for e in enums {
let Enum {
id: _,
interface,
name,
names,
} = e;
self.opt_string_ptr(interface.as_deref());
self.string_ptr(name);
self.list(names, &mut deferred);
}
for (sym, names) in deferred {
self.bind(sym);
for name in names {
self.string_ptr(name);
}
}
}
fn encode_options(&mut self, options: &[WitOption]) {
for option in options {
let WitOption {
id: _,
interface,
name,
ty,
} = option;
self.opt_string_ptr(interface.as_deref());
self.opt_string_ptr(name.as_deref());
self.ty(ty);
}
}
fn encode_results(&mut self, results: &[WitResult]) {
for result in results {
let WitResult {
id: _,
interface,
name,
ok,
err,
} = result;
self.opt_string_ptr(interface.as_deref());
self.opt_string_ptr(name.as_deref());
self.opt_ty(ok.as_ref());
self.opt_ty(err.as_ref());
}
}
fn encode_lists(&mut self, lists: &[List]) {
for list in lists {
let List {
id: _,
interface,
name,
ty,
} = list;
self.opt_string_ptr(interface.as_deref());
self.opt_string_ptr(name.as_deref());
self.ty(ty);
}
}
fn encode_fixed_length_lists(&mut self, lists: &[FixedLengthList]) {
for list in lists {
let FixedLengthList {
id: _,
interface,
name,
len,
ty,
} = list;
self.opt_string_ptr(interface.as_deref());
self.opt_string_ptr(name.as_deref());
self.put_u32(*len);
self.ty(ty);
}
}
fn encode_futures(&mut self, futures: &[Future]) {
for future in futures {
let Future {
id: _,
interface,
name,
ty,
new_elem_index,
read_elem_index,
write_elem_index,
cancel_read_elem_index,
cancel_write_elem_index,
drop_readable_elem_index,
drop_writable_elem_index,
lift_elem_index,
lower_elem_index,
abi_payload_size,
abi_payload_align,
} = future;
self.opt_string_ptr(interface.as_deref());
self.opt_string_ptr(name.as_deref());
self.opt_ty(ty.as_ref());
self.elem_index(*new_elem_index);
self.elem_index(*read_elem_index);
self.elem_index(*write_elem_index);
self.elem_index(*cancel_read_elem_index);
self.elem_index(*cancel_write_elem_index);
self.elem_index(*drop_readable_elem_index);
self.elem_index(*drop_writable_elem_index);
self.opt_elem_index(*lift_elem_index);
self.opt_elem_index(*lower_elem_index);
self.put_usize(*abi_payload_size);
self.put_usize(*abi_payload_align);
}
}
fn encode_streams(&mut self, streams: &[Stream]) {
for stream in streams {
let Stream {
id: _,
interface,
name,
ty,
new_elem_index,
read_elem_index,
write_elem_index,
cancel_read_elem_index,
cancel_write_elem_index,
drop_readable_elem_index,
drop_writable_elem_index,
lift_elem_index,
lower_elem_index,
abi_payload_size,
abi_payload_align,
} = stream;
self.opt_string_ptr(interface.as_deref());
self.opt_string_ptr(name.as_deref());
self.opt_ty(ty.as_ref());
self.elem_index(*new_elem_index);
self.elem_index(*read_elem_index);
self.elem_index(*write_elem_index);
self.elem_index(*cancel_read_elem_index);
self.elem_index(*cancel_write_elem_index);
self.elem_index(*drop_readable_elem_index);
self.elem_index(*drop_writable_elem_index);
self.opt_elem_index(*lift_elem_index);
self.opt_elem_index(*lower_elem_index);
self.put_usize(*abi_payload_size);
self.put_usize(*abi_payload_align);
}
}
fn encode_aliases(&mut self, aliases: &[Alias]) {
for alias in aliases {
let Alias {
id: _,
interface,
name,
ty,
} = alias;
self.opt_string_ptr(interface.as_deref());
self.string_ptr(name);
self.ty(ty);
}
}
fn encode_strings(&mut self) {
for (string, sym) in mem::take(&mut self.strings) {
self.bind(sym);
self.data.extend_from_slice(string.as_bytes());
self.data.push(0);
}
}
fn symbol(&mut self) -> SymbolId {
let ret = SymbolId(self.symbol_offsets.len());
self.symbol_offsets.push(usize::MAX);
ret
}
fn bind(&mut self, sym: SymbolId) {
assert_eq!(self.symbol_offsets[sym.0], usize::MAX);
self.symbol_offsets[sym.0] = self.data.len();
}
fn put_u32(&mut self, value: u32) {
self.data.extend_from_slice(&value.to_le_bytes());
}
fn put_usize(&mut self, value: usize) {
self.put_u32(value.try_into().unwrap());
}
fn list<'a, T>(&mut self, list: &'a [T], deferred: &mut Vec<(SymbolId, &'a [T])>) {
self.put_usize(list.len());
if list.is_empty() {
self.put_usize(0);
} else {
let sym = self.symbol();
deferred.push((sym, list));
self.memory_ptr(sym);
}
}
fn memory_ptr(&mut self, sym: SymbolId) {
self.add_reloc(sym, 0, RelocKind::Data);
self.put_u32(0);
}
fn opt_elem_index(&mut self, value: Option<u32>) {
match value {
Some(name) => self.elem_index(name),
None => self.put_u32(0),
}
}
fn elem_index(&mut self, value: u32) {
self.add_reloc(SymbolId::TABLE, value.try_into().unwrap(), RelocKind::Table);
self.put_u32(0);
}
fn opt_string_ptr(&mut self, value: Option<&str>) {
match value {
Some(name) => self.string_ptr(name),
None => self.put_u32(0),
}
}
fn string_ptr(&mut self, value: &str) {
let string_sym = if self.strings.contains_key(value) {
self.strings[value]
} else {
let sym = self.symbol();
self.strings.insert(value.to_string(), sym);
sym
};
self.memory_ptr(string_sym);
}
fn ty(&mut self, ty: &Type) {
let index = |discr: u32, index: &usize| {
let index = u32::try_from(*index).unwrap();
assert_eq!(index << 8 >> 8, index);
(index << 8) | discr
};
let val = match ty {
Type::U8 => 0,
Type::U16 => 1,
Type::U32 => 2,
Type::U64 => 3,
Type::S8 => 4,
Type::S16 => 5,
Type::S32 => 6,
Type::S64 => 7,
Type::Bool => 8,
Type::Char => 9,
Type::F32 => 10,
Type::F64 => 11,
Type::String => 12,
Type::ErrorContext => 13,
Type::Record(i) => index(14, i),
Type::Own(i) => index(15, i),
Type::Borrow(i) => index(16, i),
Type::Flags(i) => index(17, i),
Type::Tuple(i) => index(18, i),
Type::Variant(i) => index(19, i),
Type::Enum(i) => index(20, i),
Type::Option(i) => index(21, i),
Type::Result(i) => index(22, i),
Type::List(i) => index(23, i),
Type::FixedLengthList(i) => index(24, i),
Type::Future(i) => index(25, i),
Type::Stream(i) => index(26, i),
Type::Alias(i) => index(27, i),
};
self.put_u32(val);
}
fn opt_ty(&mut self, ty: Option<&Type>) {
match ty {
Some(ty) => self.ty(ty),
None => self.put_u32(u32::MAX),
}
}
fn add_reloc(&mut self, sym: SymbolId, addend: usize, kind: RelocKind) {
self.relocs.push(Reloc {
sym,
offset: self.data.len(),
addend,
kind,
});
}
fn generate_apply_relocs(&mut self) -> Option<Function> {
if self.relocs.is_empty() {
return None;
}
let mut func = Function::new([]);
let mut ins = func.instructions();
for reloc in self.relocs.iter() {
let addend_i32 = u32::try_from(reloc.addend).unwrap() as i32;
ins.global_get(self.memory_base);
match reloc.kind {
RelocKind::Data => {
ins.global_get(self.memory_base);
let offset = self.symbol_offsets[reloc.sym.0];
assert!(
offset != usize::MAX,
"failed to bind symbol {}",
reloc.sym.0,
);
let sym_i32 = u32::try_from(offset).unwrap() as i32;
ins.i32_const(sym_i32);
ins.i32_add();
}
RelocKind::Table => {
ins.global_get(self.table_base);
assert_eq!(reloc.sym, SymbolId::TABLE);
}
}
if addend_i32 != 0 {
ins.i32_const(addend_i32);
ins.i32_add();
}
ins.i32_store(MemArg {
align: 2,
memory_index: 0,
offset: u64::try_from(reloc.offset).unwrap(),
});
}
ins.end();
Some(func)
}
fn finish(self) -> Vec<u8> {
self.data
}
}