use heck::*;
use std::collections::BTreeMap;
use std::io::{Read, Write};
use std::mem;
use std::process::{Command, Stdio};
use wasmer_wit_bindgen_gen_core::wit_parser::abi::{
AbiVariant, Bindgen, Instruction, LiftLower, WasmType,
};
use wasmer_wit_bindgen_gen_core::{wit_parser::*, Direction, Files, Generator, Source, TypeInfo, Types};
use wasmer_wit_bindgen_gen_rust::{
int_repr, wasm_type, FnSig, RustFlagsRepr, RustFunctionGenerator, RustGenerator, TypeMode,
};
#[derive(Default)]
pub struct RustWasm {
src: Source,
opts: Opts,
types: Types,
in_import: bool,
traits: BTreeMap<String, Trait>,
in_trait: bool,
trait_name: String,
return_pointer_area_size: usize,
return_pointer_area_align: usize,
sizes: SizeAlign,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "structopt", derive(structopt::StructOpt))]
pub struct Opts {
#[cfg_attr(feature = "structopt", structopt(long))]
pub rustfmt: bool,
#[cfg_attr(feature = "structopt", structopt(long))]
pub multi_module: bool,
#[cfg_attr(feature = "structopt", structopt(long))]
pub unchecked: bool,
#[cfg_attr(feature = "structopt", structopt(skip))]
pub symbol_namespace: String,
#[cfg_attr(feature = "structopt", structopt(skip))]
pub standalone: bool,
}
#[derive(Default)]
struct Trait {
methods: Vec<String>,
resource_methods: BTreeMap<ResourceId, Vec<String>>,
}
impl Opts {
pub fn build(self) -> RustWasm {
let mut r = RustWasm::new();
r.opts = self;
r
}
}
impl RustWasm {
pub fn new() -> RustWasm {
RustWasm::default()
}
fn abi_variant(dir: Direction) -> AbiVariant {
match dir {
Direction::Export => AbiVariant::GuestExport,
Direction::Import => AbiVariant::GuestImport,
}
}
fn ret_area_name(iface: &Interface) -> String {
format!("{}_RET_AREA", iface.name.to_shouty_snake_case())
}
}
impl RustGenerator for RustWasm {
fn default_param_mode(&self) -> TypeMode {
if self.in_import {
TypeMode::AllBorrowed("'a")
} else {
TypeMode::Owned
}
}
fn handle_projection(&self) -> Option<(&'static str, String)> {
None
}
fn handle_in_super(&self) -> bool {
!self.in_import
}
fn handle_wrapper(&self) -> Option<&'static str> {
if self.in_import {
None
} else {
Some("wit_bindgen_rust::Handle")
}
}
fn push_str(&mut self, s: &str) {
self.src.push_str(s);
}
fn info(&self, ty: TypeId) -> TypeInfo {
self.types.get(ty)
}
fn types_mut(&mut self) -> &mut Types {
&mut self.types
}
fn print_borrowed_slice(
&mut self,
iface: &Interface,
mutbl: bool,
ty: &Type,
lifetime: &'static str,
) {
self.print_rust_slice(iface, mutbl, ty, lifetime);
}
fn print_borrowed_str(&mut self, lifetime: &'static str) {
self.push_str("&");
if lifetime != "'_" {
self.push_str(lifetime);
self.push_str(" ");
}
self.push_str(" str");
}
}
impl Generator for RustWasm {
fn preprocess_one(&mut self, iface: &Interface, dir: Direction) {
let variant = Self::abi_variant(dir);
self.in_import = variant == AbiVariant::GuestImport;
self.types.analyze(iface);
self.trait_name = iface.name.to_camel_case();
if !self.opts.standalone {
self.src.push_str(&format!(
"#[allow(clippy::all)]\nmod {} {{\n",
iface.name.to_snake_case(),
));
}
self.sizes.fill(iface);
}
fn type_record(
&mut self,
iface: &Interface,
id: TypeId,
_name: &str,
record: &Record,
docs: &Docs,
generate_structs: bool,
) {
self.print_typedef_record(iface, id, record, docs, generate_structs);
}
fn type_tuple(
&mut self,
iface: &Interface,
id: TypeId,
_name: &str,
tuple: &Tuple,
docs: &Docs,
generate_structs: bool,
) {
self.print_typedef_tuple(iface, id, tuple, docs, generate_structs);
}
fn type_flags(
&mut self,
_iface: &Interface,
_id: TypeId,
name: &str,
flags: &Flags,
docs: &Docs,
generate_structs: bool,
) {
self.src
.push_str("wit_bindgen_rust::bitflags::bitflags! {\n");
self.rustdoc(docs);
let repr = RustFlagsRepr::new(flags);
self.src
.push_str(&format!("pub struct {}: {repr} {{\n", name.to_camel_case(),));
for (i, flag) in flags.flags.iter().enumerate() {
self.rustdoc(&flag.docs);
self.src.push_str(&format!(
"const {} = 1 << {};\n",
flag.name.to_shouty_snake_case(),
i,
));
}
self.src.push_str("}\n");
self.src.push_str("}\n");
self.src
.push_str(&format!("impl {} {{\n", name.to_camel_case()));
self.src.push_str(&format!(
" /// Convert from a raw integer, preserving any unknown bits. See\n"
));
self.src.push_str(&format!(
" /// <https://github.com/bitflags/bitflags/issues/263#issuecomment-957088321>\n"
));
self.src.push_str(&format!(
" pub fn from_bits_preserve(bits: {repr}) -> Self {{\n",
));
self.src.push_str(&format!(" Self {{ bits }}\n"));
self.src.push_str(&format!(" }}\n"));
self.src.push_str(&format!("}}\n"));
}
fn type_variant(
&mut self,
iface: &Interface,
id: TypeId,
_name: &str,
variant: &Variant,
docs: &Docs,
generate_structs: bool,
) {
self.print_typedef_variant(iface, id, variant, docs, generate_structs);
}
fn type_union(
&mut self,
iface: &Interface,
id: TypeId,
_name: &str,
union: &Union,
docs: &Docs,
generate_structs: bool,
) {
self.print_typedef_union(iface, id, union, docs, generate_structs);
}
fn type_option(
&mut self,
iface: &Interface,
id: TypeId,
_name: &str,
payload: &Type,
docs: &Docs,
generate_structs: bool,
) {
self.print_typedef_option(iface, id, payload, docs, generate_structs);
}
fn type_expected(
&mut self,
iface: &Interface,
id: TypeId,
_name: &str,
expected: &Expected,
docs: &Docs,
generate_structs: bool,
) {
self.print_typedef_expected(iface, id, expected, docs, generate_structs);
}
fn type_enum(
&mut self,
_iface: &Interface,
id: TypeId,
name: &str,
enum_: &Enum,
docs: &Docs,
generate_structs: bool,
) {
self.print_typedef_enum(id, name, enum_, docs, generate_structs);
}
fn type_resource(&mut self, iface: &Interface, ty: ResourceId) {
if !self.in_import {
let panic = "
#[cfg(not(target_arch = \"wasm32\"))]
{
panic!(\"handles can only be used on wasm32\");
}
#[cfg(target_arch = \"wasm32\")]
";
self.src.push_str(&format!(
"
unsafe impl wit_bindgen_rust::HandleType for super::{ty} {{
#[inline]
fn clone(_val: i32) -> i32 {{
{panic_not_wasm}
{{
#[link(wasm_import_module = \"canonical_abi\")]
extern \"C\" {{
#[link_name = \"resource_clone_{name}\"]
fn clone(val: i32) -> i32;
}}
unsafe {{ clone(_val) }}
}}
}}
#[inline]
fn drop(_val: i32) {{
{panic_not_wasm}
{{
#[link(wasm_import_module = \"canonical_abi\")]
extern \"C\" {{
#[link_name = \"resource_drop_{name}\"]
fn drop(val: i32);
}}
unsafe {{ drop(_val) }}
}}
}}
}}
unsafe impl wit_bindgen_rust::LocalHandle for super::{ty} {{
#[inline]
fn new(_val: i32) -> i32 {{
{panic_not_wasm}
{{
#[link(wasm_import_module = \"canonical_abi\")]
extern \"C\" {{
#[link_name = \"resource_new_{name}\"]
fn new(val: i32) -> i32;
}}
unsafe {{ new(_val) }}
}}
}}
#[inline]
fn get(_val: i32) -> i32 {{
{panic_not_wasm}
{{
#[link(wasm_import_module = \"canonical_abi\")]
extern \"C\" {{
#[link_name = \"resource_get_{name}\"]
fn get(val: i32) -> i32;
}}
unsafe {{ get(_val) }}
}}
}}
}}
const _: () = {{
#[export_name = \"{ns}canonical_abi_drop_{name}\"]
extern \"C\" fn drop(ty: Box<super::{ty}>) {{
<super::{iface} as {iface}>::drop_{name_snake}(*ty)
}}
}};
",
ty = iface.resources[ty].name.to_camel_case(),
name = iface.resources[ty].name,
name_snake = iface.resources[ty].name.to_snake_case(),
iface = iface.name.to_camel_case(),
ns = self.opts.symbol_namespace,
panic_not_wasm = panic,
));
let trait_ = self
.traits
.entry(iface.name.to_camel_case())
.or_insert(Trait::default());
trait_.methods.push(format!(
"
/// An optional callback invoked when a handle is finalized
/// and destroyed.
fn drop_{}(val: super::{}) {{
drop(val);
}}
",
iface.resources[ty].name.to_snake_case(),
iface.resources[ty].name.to_camel_case(),
));
return;
}
let resource = &iface.resources[ty];
let name = &resource.name;
self.rustdoc(&resource.docs);
self.src.push_str("#[derive(Debug)]\n");
self.src.push_str("#[repr(transparent)]\n");
self.src
.push_str(&format!("pub struct {}(i32);\n", name.to_camel_case()));
self.src.push_str("impl ");
self.src.push_str(&name.to_camel_case());
self.src.push_str(
" {
pub unsafe fn from_raw(raw: i32) -> Self {
Self(raw)
}
pub fn into_raw(self) -> i32 {
let ret = self.0;
core::mem::forget(self);
return ret;
}
pub fn as_raw(&self) -> i32 {
self.0
}
}\n",
);
self.src.push_str("impl Drop for ");
self.src.push_str(&name.to_camel_case());
self.src.push_str(&format!(
"{{
fn drop(&mut self) {{
#[link(wasm_import_module = \"canonical_abi\")]
extern \"C\" {{
#[link_name = \"resource_drop_{}\"]
fn close(fd: i32);
}}
unsafe {{
close(self.0);
}}
}}
}}\n",
name,
));
self.src.push_str("impl Clone for ");
self.src.push_str(&name.to_camel_case());
self.src.push_str(&format!(
"{{
fn clone(&self) -> Self {{
#[link(wasm_import_module = \"canonical_abi\")]
extern \"C\" {{
#[link_name = \"resource_clone_{}\"]
fn clone(val: i32) -> i32;
}}
unsafe {{
Self(clone(self.0))
}}
}}
}}\n",
name,
));
}
fn type_alias(
&mut self,
iface: &Interface,
id: TypeId,
_name: &str,
ty: &Type,
docs: &Docs,
generate_structs: bool,
) {
self.print_typedef_alias(iface, id, ty, docs, generate_structs);
}
fn type_list(
&mut self,
iface: &Interface,
id: TypeId,
_name: &str,
ty: &Type,
docs: &Docs,
generate_structs: bool,
) {
self.print_type_list(iface, id, ty, docs, generate_structs);
}
fn type_builtin(
&mut self,
iface: &Interface,
_id: TypeId,
name: &str,
ty: &Type,
docs: &Docs,
generate_structs: bool,
) {
self.rustdoc(docs);
self.src
.push_str(&format!("pub type {}", name.to_camel_case()));
self.src.push_str(" = ");
self.print_ty(iface, ty, TypeMode::Owned);
self.src.push_str(";\n");
}
fn preprocess_functions(&mut self, _iface: &Interface, dir: Direction) {
if self.opts.standalone && dir == Direction::Export {
self.src.push_str(
"/// Declares the export of the interface for the given type.\n\
#[macro_export]\n\
macro_rules! export(($t:ident) => {\n",
);
}
}
fn import(&mut self, iface: &Interface, func: &Function, generate_structs: bool) {
let mut sig = FnSig::default();
let param_mode = TypeMode::AllBorrowed("'_");
sig.async_ = func.is_async;
match &func.kind {
FunctionKind::Freestanding => {}
FunctionKind::Static { resource, .. } | FunctionKind::Method { resource, .. } => {
sig.use_item_name = true;
self.src.push_str(&format!(
"impl {} {{\n",
iface.resources[*resource].name.to_camel_case()
));
}
}
if let FunctionKind::Method { .. } = func.kind {
sig.self_arg = Some("&self".to_string());
sig.self_is_first_param = true;
}
let params = self.print_signature(iface, func, param_mode, &sig);
self.src.push_str("{\n");
self.src.push_str("unsafe {\n");
let mut f = FunctionBindgen::new(self, params);
iface.call(
AbiVariant::GuestImport,
LiftLower::LowerArgsLiftResults,
func,
&mut f,
);
let FunctionBindgen {
needs_cleanup_list,
src,
..
} = f;
if needs_cleanup_list {
self.src.push_str("let mut cleanup_list = Vec::new();\n");
}
self.src.push_str(&String::from(src));
self.src.push_str("}\n");
self.src.push_str("}\n");
match &func.kind {
FunctionKind::Freestanding => {}
FunctionKind::Static { .. } | FunctionKind::Method { .. } => {
self.src.push_str("}\n");
}
}
}
fn export(&mut self, iface: &Interface, func: &Function, generate_structs: bool) {
let iface_name = iface.name.to_snake_case();
self.src.push_str("#[export_name = \"");
match &iface.module {
Some(module) => {
self.src.push_str(module);
self.src.push_str("#");
self.src.push_str(&func.name);
}
None => {
self.src.push_str(&self.opts.symbol_namespace);
self.src.push_str(&func.name);
}
}
self.src.push_str("\"]\n");
self.src.push_str("unsafe extern \"C\" fn __wit_bindgen_");
self.src.push_str(&iface_name);
self.src.push_str("_");
self.src.push_str(&func.name.to_snake_case());
self.src.push_str("(");
let sig = iface.wasm_signature(AbiVariant::GuestExport, func);
let mut params = Vec::new();
for (i, param) in sig.params.iter().enumerate() {
let name = format!("arg{}", i);
self.src.push_str(&name);
self.src.push_str(": ");
self.wasm_type(*param);
self.src.push_str(", ");
params.push(name);
}
self.src.push_str(")");
match sig.results.len() {
0 => {}
1 => {
self.src.push_str(" -> ");
self.wasm_type(sig.results[0]);
}
_ => unimplemented!(),
}
self.push_str("{\n");
if self.opts.standalone {
self.src
.push_str("#[allow(unused_imports)]\nuse wasmer_wit_bindgen_rust;\nuse ");
self.src.push_str(&iface_name);
self.src.push_str("::*;\n");
}
if func.is_async {
self.src.push_str("let future = async move {\n");
}
let mut f = FunctionBindgen::new(self, params);
iface.call(
AbiVariant::GuestExport,
LiftLower::LiftArgsLowerResults,
func,
&mut f,
);
let FunctionBindgen {
needs_cleanup_list,
src,
..
} = f;
assert!(!needs_cleanup_list);
self.src.push_str(&String::from(src));
if func.is_async {
self.src.push_str("};\n");
self.src
.push_str("wit_bindgen_rust::rt::execute(Box::pin(future));\n");
}
self.src.push_str("}\n");
let prev = mem::take(&mut self.src);
self.in_trait = true;
let mut sig = FnSig::default();
sig.private = true;
sig.async_ = func.is_async;
match &func.kind {
FunctionKind::Freestanding => {}
FunctionKind::Static { .. } => sig.use_item_name = true,
FunctionKind::Method { .. } => {
sig.use_item_name = true;
sig.self_is_first_param = true;
sig.self_arg = Some("&self".to_string());
}
}
self.print_signature(iface, func, TypeMode::Owned, &sig);
self.src.push_str(";");
self.in_trait = false;
let trait_ = self
.traits
.entry(iface.name.to_camel_case())
.or_insert(Trait::default());
let dst = match &func.kind {
FunctionKind::Freestanding => &mut trait_.methods,
FunctionKind::Static { resource, .. } | FunctionKind::Method { resource, .. } => trait_
.resource_methods
.entry(*resource)
.or_insert(Vec::new()),
};
dst.push(mem::replace(&mut self.src, prev).into());
}
fn finish_functions(&mut self, iface: &Interface, dir: Direction) {
if self.return_pointer_area_align > 0 {
self.src.push_str(&format!(
"
#[repr(align({align}))]
struct RetArea([u8; {size}]);
static mut {name}: RetArea = RetArea([0; {size}]);
",
name = Self::ret_area_name(iface),
align = self.return_pointer_area_align,
size = self.return_pointer_area_size,
));
}
if self.opts.standalone && dir == Direction::Export {
self.src.push_str("});\n");
}
}
fn finish_one(&mut self, iface: &Interface, files: &mut Files) {
let mut src = mem::take(&mut self.src);
let any_async = iface.functions.iter().any(|f| f.is_async);
for (name, trait_) in self.traits.iter() {
if any_async {
src.push_str("#[wit_bindgen_rust::async_trait(?Send)]\n");
}
src.push_str("pub trait ");
src.push_str(&name);
src.push_str(" {\n");
for f in trait_.methods.iter() {
src.push_str(&f);
src.push_str("\n");
}
src.push_str("}\n");
for (id, methods) in trait_.resource_methods.iter() {
if any_async {
src.push_str("#[wit_bindgen_rust::async_trait(?Send)]\n");
}
src.push_str(&format!(
"pub trait {} {{\n",
iface.resources[*id].name.to_camel_case()
));
for f in methods {
src.push_str(&f);
src.push_str("\n");
}
src.push_str("}\n");
}
}
if !self.opts.standalone {
src.push_str("}\n");
}
if self.opts.rustfmt {
let mut child = Command::new("rustfmt")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("failed to spawn `rustfmt`");
child
.stdin
.take()
.unwrap()
.write_all(src.as_bytes())
.unwrap();
src.as_mut_string().truncate(0);
child
.stdout
.take()
.unwrap()
.read_to_string(src.as_mut_string())
.unwrap();
let status = child.wait().unwrap();
assert!(status.success());
}
files.push("bindings.rs", src.as_bytes());
}
}
struct FunctionBindgen<'a> {
gen: &'a mut RustWasm,
params: Vec<String>,
src: Source,
blocks: Vec<String>,
block_storage: Vec<(Source, Vec<(String, String)>)>,
tmp: usize,
needs_cleanup_list: bool,
cleanup: Vec<(String, String)>,
}
impl FunctionBindgen<'_> {
fn new(gen: &mut RustWasm, params: Vec<String>) -> FunctionBindgen<'_> {
FunctionBindgen {
gen,
params,
src: Default::default(),
blocks: Vec::new(),
block_storage: Vec::new(),
tmp: 0,
needs_cleanup_list: false,
cleanup: Vec::new(),
}
}
fn emit_cleanup(&mut self) {
for (ptr, layout) in mem::take(&mut self.cleanup) {
self.push_str(&format!("std::alloc::dealloc({}, {});\n", ptr, layout));
}
if self.needs_cleanup_list {
self.push_str(
"for (ptr, layout) in cleanup_list {
std::alloc::dealloc(ptr, layout);
}\n",
);
}
}
fn declare_import(
&mut self,
iface: &Interface,
name: &str,
params: &[WasmType],
results: &[WasmType],
) -> String {
let module = iface.module.as_deref().unwrap_or(&iface.name);
self.push_str("#[link(wasm_import_module = \"");
self.push_str(module);
self.push_str("\")]\n");
self.push_str("extern \"C\" {\n");
self.push_str("#[cfg_attr(target_arch = \"wasm32\", link_name = \"");
self.push_str(name);
self.push_str("\")]\n");
self.push_str("#[cfg_attr(not(target_arch = \"wasm32\"), link_name = \"");
self.push_str(module);
self.push_str("_");
self.push_str(name);
self.push_str("\")]\n");
self.push_str("fn wit_import(");
for param in params.iter() {
self.push_str("_: ");
self.push_str(wasm_type(*param));
self.push_str(", ");
}
self.push_str(")");
assert!(results.len() < 2);
for result in results.iter() {
self.push_str(" -> ");
self.push_str(wasm_type(*result));
}
self.push_str(";\n}\n");
"wit_import".to_string()
}
}
impl RustFunctionGenerator for FunctionBindgen<'_> {
fn push_str(&mut self, s: &str) {
self.src.push_str(s);
}
fn tmp(&mut self) -> usize {
let ret = self.tmp;
self.tmp += 1;
ret
}
fn rust_gen(&self) -> &dyn RustGenerator {
self.gen
}
fn lift_lower(&self) -> LiftLower {
if self.gen.in_import {
LiftLower::LowerArgsLiftResults
} else {
LiftLower::LiftArgsLowerResults
}
}
}
impl Bindgen for FunctionBindgen<'_> {
type Operand = String;
fn push_block(&mut self) {
let prev_src = mem::take(&mut self.src);
let prev_cleanup = mem::take(&mut self.cleanup);
self.block_storage.push((prev_src, prev_cleanup));
}
fn finish_block(&mut self, operands: &mut Vec<String>) {
if self.cleanup.len() > 0 {
self.needs_cleanup_list = true;
self.push_str("cleanup_list.extend_from_slice(&[");
for (ptr, layout) in mem::take(&mut self.cleanup) {
self.push_str("(");
self.push_str(&ptr);
self.push_str(", ");
self.push_str(&layout);
self.push_str("),");
}
self.push_str("]);\n");
}
let (prev_src, prev_cleanup) = self.block_storage.pop().unwrap();
let src = mem::replace(&mut self.src, prev_src);
self.cleanup = prev_cleanup;
let expr = match operands.len() {
0 => "()".to_string(),
1 => operands[0].clone(),
_ => format!("({})", operands.join(", ")),
};
if src.is_empty() {
self.blocks.push(expr);
} else if operands.is_empty() {
self.blocks.push(format!("{{\n{}\n}}", &src[..]));
} else {
self.blocks.push(format!("{{\n{}\n{}\n}}", &src[..], expr));
}
}
fn return_pointer(&mut self, iface: &Interface, size: usize, align: usize) -> String {
self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size);
self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align);
let tmp = self.tmp();
self.push_str(&format!(
"let ptr{} = {}.0.as_mut_ptr() as i32;\n",
tmp,
RustWasm::ret_area_name(iface),
));
format!("ptr{}", tmp)
}
fn sizes(&self) -> &SizeAlign {
&self.gen.sizes
}
fn is_list_canonical(&self, iface: &Interface, ty: &Type) -> bool {
iface.all_bits_valid(ty)
}
fn emit(
&mut self,
iface: &Interface,
inst: &Instruction<'_>,
operands: &mut Vec<String>,
results: &mut Vec<String>,
) {
let unchecked = self.gen.opts.unchecked;
let mut top_as = |cvt: &str| {
let mut s = operands.pop().unwrap();
s.push_str(" as ");
s.push_str(cvt);
results.push(s);
};
match inst {
Instruction::GetArg { nth } => results.push(self.params[*nth].clone()),
Instruction::I32Const { val } => results.push(format!("{}i32", val)),
Instruction::ConstZero { tys } => {
for ty in tys.iter() {
match ty {
WasmType::I32 => results.push("0i32".to_string()),
WasmType::I64 => results.push("0i64".to_string()),
WasmType::F32 => results.push("0.0f32".to_string()),
WasmType::F64 => results.push("0.0f64".to_string()),
}
}
}
Instruction::I64FromU64 | Instruction::I64FromS64 => {
let s = operands.pop().unwrap();
results.push(format!("wit_bindgen_rust::rt::as_i64({})", s));
}
Instruction::I32FromChar
| Instruction::I32FromU8
| Instruction::I32FromS8
| Instruction::I32FromU16
| Instruction::I32FromS16
| Instruction::I32FromU32
| Instruction::I32FromS32 => {
let s = operands.pop().unwrap();
results.push(format!("wit_bindgen_rust::rt::as_i32({})", s));
}
Instruction::F32FromFloat32 => {
let s = operands.pop().unwrap();
results.push(format!("wit_bindgen_rust::rt::as_f32({})", s));
}
Instruction::F64FromFloat64 => {
let s = operands.pop().unwrap();
results.push(format!("wit_bindgen_rust::rt::as_f64({})", s));
}
Instruction::Float32FromF32
| Instruction::Float64FromF64
| Instruction::S32FromI32
| Instruction::S64FromI64 => {
results.push(operands.pop().unwrap());
}
Instruction::S8FromI32 => top_as("i8"),
Instruction::U8FromI32 => top_as("u8"),
Instruction::S16FromI32 => top_as("i16"),
Instruction::U16FromI32 => top_as("u16"),
Instruction::U32FromI32 => top_as("u32"),
Instruction::U64FromI64 => top_as("u64"),
Instruction::CharFromI32 => {
if unchecked {
results.push(format!(
"core::char::from_u32_unchecked({} as u32)",
operands[0]
));
} else {
results.push(format!(
"core::char::from_u32({} as u32).unwrap()",
operands[0]
));
}
}
Instruction::Bitcasts { casts } => {
wasmer_wit_bindgen_gen_rust::bitcast(casts, operands, results)
}
Instruction::UnitLower => {
self.push_str(&format!("let () = {};\n", operands[0]));
}
Instruction::UnitLift => {
results.push("()".to_string());
}
Instruction::I32FromBool => {
results.push(format!("match {} {{ true => 1, false => 0 }}", operands[0]));
}
Instruction::BoolFromI32 => {
if unchecked {
results.push(format!(
"core::mem::transmute::<u8, bool>({} as u8)",
operands[0],
));
} else {
results.push(format!(
"match {} {{
0 => false,
1 => true,
_ => panic!(\"invalid bool discriminant\"),
}}",
operands[0],
));
}
}
Instruction::I32FromOwnedHandle { .. } => {
results.push(format!(
"wit_bindgen_rust::Handle::into_raw({})",
operands[0]
));
}
Instruction::HandleBorrowedFromI32 { .. } => {
results.push(format!(
"wit_bindgen_rust::Handle::from_raw({})",
operands[0],
));
}
Instruction::I32FromBorrowedHandle { .. } => {
results.push(format!("{}.0", operands[0]));
}
Instruction::HandleOwnedFromI32 { ty } => {
results.push(format!(
"{}({})",
iface.resources[*ty].name.to_camel_case(),
operands[0]
));
}
Instruction::FlagsLower { flags, .. } => {
let tmp = self.tmp();
self.push_str(&format!("let flags{} = {};\n", tmp, operands[0]));
for i in 0..flags.repr().count() {
results.push(format!("(flags{}.bits() >> {}) as i32", tmp, i * 32));
}
}
Instruction::FlagsLift { name, flags, .. } => {
let repr = RustFlagsRepr::new(flags);
let name = name.to_camel_case();
let mut result = format!("{}::empty()", name);
for (i, op) in operands.iter().enumerate() {
result.push_str(&format!(
" | {}::from_bits_preserve((({} as {repr}) << {}) as _)",
name,
op,
i * 32
));
}
results.push(result);
}
Instruction::RecordLower { ty, record, .. } => {
self.record_lower(iface, *ty, record, &operands[0], results);
}
Instruction::RecordLift { ty, record, .. } => {
self.record_lift(iface, *ty, record, operands, results);
}
Instruction::TupleLower { tuple, .. } => {
self.tuple_lower(tuple, &operands[0], results);
}
Instruction::TupleLift { .. } => {
self.tuple_lift(operands, results);
}
Instruction::VariantPayloadName => results.push("e".to_string()),
Instruction::VariantLower {
variant,
results: result_types,
ty,
..
} => {
let blocks = self
.blocks
.drain(self.blocks.len() - variant.cases.len()..)
.collect::<Vec<_>>();
self.let_results(result_types.len(), results);
let op0 = &operands[0];
self.push_str(&format!("match {op0} {{\n"));
let name = self.typename_lower(iface, *ty);
for (case, block) in variant.cases.iter().zip(blocks) {
let case_name = case.name.to_camel_case();
self.push_str(&format!("{name}::{case_name}"));
if case.ty == Type::Unit {
self.push_str(&format!(" => {{\nlet e = ();\n{block}\n}}\n"));
} else {
self.push_str(&format!("(e) => {block},\n"));
}
}
self.push_str("};\n");
}
Instruction::VariantLift { name, variant, .. }
if variant.cases.iter().all(|c| c.ty == Type::Unit) && unchecked =>
{
self.blocks.drain(self.blocks.len() - variant.cases.len()..);
let mut result = format!("core::mem::transmute::<_, ");
result.push_str(&name.to_camel_case());
result.push_str(">(");
result.push_str(&operands[0]);
result.push_str(" as ");
result.push_str(int_repr(variant.tag()));
result.push_str(")");
results.push(result);
}
Instruction::VariantLift { variant, ty, .. } => {
let blocks = self
.blocks
.drain(self.blocks.len() - variant.cases.len()..)
.collect::<Vec<_>>();
let op0 = &operands[0];
let mut result = format!("match {op0} {{\n");
let name = self.typename_lift(iface, *ty);
for (i, (case, block)) in variant.cases.iter().zip(blocks).enumerate() {
let pat = if i == variant.cases.len() - 1 && unchecked {
String::from("_")
} else {
i.to_string()
};
let block = if case.ty != Type::Unit {
format!("({block})")
} else {
String::new()
};
let case = case.name.to_camel_case();
result.push_str(&format!("{pat} => {name}::{case}{block},\n"));
}
if !unchecked {
result.push_str("_ => panic!(\"invalid enum discriminant\"),\n");
}
result.push_str("}");
results.push(result);
}
Instruction::UnionLower {
union,
results: result_types,
ty,
..
} => {
let blocks = self
.blocks
.drain(self.blocks.len() - union.cases.len()..)
.collect::<Vec<_>>();
self.let_results(result_types.len(), results);
let op0 = &operands[0];
self.push_str(&format!("match {op0} {{\n"));
let name = self.typename_lower(iface, *ty);
for (case_name, block) in self
.gen
.union_case_names(iface, union)
.into_iter()
.zip(blocks)
{
self.push_str(&format!("{name}::{case_name}(e) => {block},\n"));
}
self.push_str("};\n");
}
Instruction::UnionLift { union, ty, .. } => {
let blocks = self
.blocks
.drain(self.blocks.len() - union.cases.len()..)
.collect::<Vec<_>>();
let op0 = &operands[0];
let mut result = format!("match {op0} {{\n");
for (i, (case_name, block)) in self
.gen
.union_case_names(iface, union)
.into_iter()
.zip(blocks)
.enumerate()
{
let pat = if i == union.cases.len() - 1 && unchecked {
String::from("_")
} else {
i.to_string()
};
let name = self.typename_lift(iface, *ty);
result.push_str(&format!("{pat} => {name}::{case_name}({block}),\n"));
}
if !unchecked {
result.push_str("_ => panic!(\"invalid union discriminant\"),\n");
}
result.push_str("}");
results.push(result);
}
Instruction::OptionLower {
results: result_types,
..
} => {
let some = self.blocks.pop().unwrap();
let none = self.blocks.pop().unwrap();
self.let_results(result_types.len(), results);
let operand = &operands[0];
self.push_str(&format!(
"match {operand} {{
Some(e) => {some},
None => {{\nlet e = ();\n{none}\n}},
}};"
));
}
Instruction::OptionLift { .. } => {
let some = self.blocks.pop().unwrap();
let none = self.blocks.pop().unwrap();
assert_eq!(none, "()");
let operand = &operands[0];
let invalid = if unchecked {
"std::hint::unreachable_unchecked()"
} else {
"panic!(\"invalid enum discriminant\")"
};
results.push(format!(
"match {operand} {{
0 => None,
1 => Some({some}),
_ => {invalid},
}}"
));
}
Instruction::ExpectedLower {
results: result_types,
..
} => {
let err = self.blocks.pop().unwrap();
let ok = self.blocks.pop().unwrap();
self.let_results(result_types.len(), results);
let operand = &operands[0];
self.push_str(&format!(
"match {operand} {{
Ok(e) => {{ {ok} }},
Err(e) => {{ {err} }},
}};"
));
}
Instruction::ExpectedLift { .. } => {
let err = self.blocks.pop().unwrap();
let ok = self.blocks.pop().unwrap();
let operand = &operands[0];
let invalid = if unchecked {
"std::hint::unreachable_unchecked()"
} else {
"panic!(\"invalid enum discriminant\")"
};
results.push(format!(
"match {operand} {{
0 => Ok({ok}),
1 => Err({err}),
_ => {invalid},
}}"
));
}
Instruction::EnumLower { enum_, name, .. } => {
let mut result = format!("match {} {{\n", operands[0]);
let name = name.to_camel_case();
for (i, case) in enum_.cases.iter().enumerate() {
let case = case.name.to_camel_case();
result.push_str(&format!("{name}::{case} => {i},\n"));
}
result.push_str("}");
results.push(result);
}
Instruction::EnumLift { enum_, name, .. } if unchecked => {
let mut result = format!("core::mem::transmute::<_, ");
result.push_str(&name.to_camel_case());
result.push_str(">(");
result.push_str(&operands[0]);
result.push_str(" as ");
result.push_str(int_repr(enum_.tag()));
result.push_str(")");
results.push(result);
}
Instruction::EnumLift { enum_, name, .. } => {
let mut result = format!("match ");
result.push_str(&operands[0]);
result.push_str(" {\n");
let name = name.to_camel_case();
for (i, case) in enum_.cases.iter().enumerate() {
let case = case.name.to_camel_case();
result.push_str(&format!("{i} => {name}::{case},\n"));
}
result.push_str("_ => panic!(\"invalid enum discriminant\"),\n");
result.push_str("}");
results.push(result);
}
Instruction::ListCanonLower { realloc, .. } => {
let tmp = self.tmp();
let val = format!("vec{}", tmp);
let ptr = format!("ptr{}", tmp);
let len = format!("len{}", tmp);
if realloc.is_none() {
self.push_str(&format!("let {} = {};\n", val, operands[0]));
} else {
let op0 = operands.pop().unwrap();
self.push_str(&format!("let {} = ({}).into_boxed_slice();\n", val, op0));
}
self.push_str(&format!("let {} = {}.as_ptr() as i32;\n", ptr, val));
self.push_str(&format!("let {} = {}.len() as i32;\n", len, val));
if realloc.is_some() {
self.push_str(&format!("core::mem::forget({});\n", val));
}
results.push(ptr);
results.push(len);
}
Instruction::ListCanonLift { free, .. } => {
assert!(free.is_some());
let tmp = self.tmp();
let len = format!("len{}", tmp);
self.push_str(&format!("let {} = {} as usize;\n", len, operands[1]));
let result = format!(
"Vec::from_raw_parts({} as *mut _, {1}, {1})",
operands[0], len
);
results.push(result);
}
Instruction::StringLower { realloc } => {
let tmp = self.tmp();
let val = format!("vec{}", tmp);
let ptr = format!("ptr{}", tmp);
let len = format!("len{}", tmp);
if realloc.is_none() {
self.push_str(&format!("let {} = {};\n", val, operands[0]));
} else {
let op0 = format!("{}.into_bytes()", operands[0]);
self.push_str(&format!("let {} = ({}).into_boxed_slice();\n", val, op0));
}
self.push_str(&format!("let {} = {}.as_ptr() as i32;\n", ptr, val));
self.push_str(&format!("let {} = {}.len() as i32;\n", len, val));
if realloc.is_some() {
self.push_str(&format!("core::mem::forget({});\n", val));
}
results.push(ptr);
results.push(len);
}
Instruction::StringLift { free, .. } => {
assert!(free.is_some());
let tmp = self.tmp();
let len = format!("len{}", tmp);
self.push_str(&format!("let {} = {} as usize;\n", len, operands[1]));
let result = format!(
"Vec::from_raw_parts({} as *mut _, {1}, {1})",
operands[0], len
);
if unchecked {
results.push(format!("String::from_utf8_unchecked({})", result));
} else {
results.push(format!("String::from_utf8({}).unwrap()", result));
}
}
Instruction::ListLower { element, realloc } => {
let body = self.blocks.pop().unwrap();
let tmp = self.tmp();
let vec = format!("vec{}", tmp);
let result = format!("result{}", tmp);
let layout = format!("layout{}", tmp);
let len = format!("len{}", tmp);
self.push_str(&format!("let {} = {};\n", vec, operands[0]));
self.push_str(&format!("let {} = {}.len() as i32;\n", len, vec));
let size = self.gen.sizes.size(element);
let align = self.gen.sizes.align(element);
self.push_str(&format!(
"let {} = core::alloc::Layout::from_size_align_unchecked({}.len() * {}, {});\n",
layout, vec, size, align,
));
self.push_str(&format!(
"let {} = std::alloc::alloc({});\n",
result, layout,
));
self.push_str(&format!(
"if {}.is_null() {{ std::alloc::handle_alloc_error({}); }}\n",
result, layout,
));
self.push_str(&format!(
"for (i, e) in {}.into_iter().enumerate() {{\n",
vec
));
self.push_str(&format!(
"let base = {} as i32 + (i as i32) * {};\n",
result, size,
));
self.push_str(&body);
self.push_str("}\n");
results.push(format!("{} as i32", result));
results.push(len);
if realloc.is_none() {
self.cleanup.push((result, layout));
}
}
Instruction::ListLift { element, free, .. } => {
assert!(free.is_some());
let body = self.blocks.pop().unwrap();
let tmp = self.tmp();
let size = self.gen.sizes.size(element);
let align = self.gen.sizes.align(element);
let len = format!("len{}", tmp);
let base = format!("base{}", tmp);
let result = format!("result{}", tmp);
self.push_str(&format!("let {} = {};\n", base, operands[0]));
self.push_str(&format!("let {} = {};\n", len, operands[1],));
self.push_str(&format!(
"let mut {} = Vec::with_capacity({} as usize);\n",
result, len,
));
self.push_str("for i in 0..");
self.push_str(&len);
self.push_str(" {\n");
self.push_str("let base = ");
self.push_str(&base);
self.push_str(" + i *");
self.push_str(&size.to_string());
self.push_str(";\n");
self.push_str(&result);
self.push_str(".push(");
self.push_str(&body);
self.push_str(");\n");
self.push_str("}\n");
results.push(result);
self.push_str(&format!(
"std::alloc::dealloc(
{} as *mut _,
std::alloc::Layout::from_size_align_unchecked(
({} as usize) * {},
{},
),
);\n",
base, len, size, align
));
}
Instruction::IterElem { .. } => results.push("e".to_string()),
Instruction::IterBasePointer => results.push("base".to_string()),
Instruction::CallWasm { iface, name, sig } => {
let func = self.declare_import(iface, name, &sig.params, &sig.results);
if sig.results.len() > 0 {
self.push_str("let ret = ");
results.push("ret".to_string());
}
self.push_str(&func);
self.push_str("(");
self.push_str(&operands.join(", "));
self.push_str(");\n");
}
Instruction::CallWasmAsyncImport {
iface,
name,
params: wasm_params,
results: wasm_results,
} => {
self.push_str("unsafe extern \"C\" fn completion_callback(sender: usize");
for (i, result) in wasm_results.iter().enumerate() {
self.push_str(", ");
self.push_str(&format!("ret{}: ", i));
self.push_str(wasm_type(*result));
}
self.push_str(") {\n");
self.push_str("wit_bindgen_rust::rt::Sender::from_usize(sender).send((");
for i in 0..wasm_results.len() {
self.push_str(&format!("ret{},", i));
}
self.push_str("));\n");
self.push_str("}\n");
self.push_str("let (rx, tx) = wit_bindgen_rust::rt::Oneshot::<(");
for ty in *wasm_results {
self.push_str(wasm_type(*ty));
self.push_str(", ");
}
self.push_str(")>::new();\n");
let func = self.declare_import(iface, name, wasm_params, &[]);
self.push_str(&func);
self.push_str("(");
for op in operands {
self.push_str(op);
self.push_str(", ");
}
self.push_str("completion_callback as i32, ");
self.push_str("tx.into_usize() as i32");
self.push_str(");\n");
let tmp = self.tmp();
self.push_str("let (");
for i in 0..wasm_results.len() {
let name = format!("ret{}_{}", tmp, i);
self.push_str(&name);
self.push_str(",");
results.push(name);
}
self.push_str(") = rx.await;\n");
}
Instruction::CallWasmAsyncExport { .. } => unreachable!(),
Instruction::CallInterface { module, func } => {
self.push_str("let result = ");
results.push("result".to_string());
match &func.kind {
FunctionKind::Freestanding => {
if self.gen.opts.standalone {
self.push_str(&format!(
"<$t as {t}>::{}",
func.name.to_snake_case(),
t = module.to_camel_case(),
));
} else {
self.push_str(&format!(
"<super::{m} as {m}>::{}",
func.name.to_snake_case(),
m = module.to_camel_case()
));
}
}
FunctionKind::Static { resource, name }
| FunctionKind::Method { resource, name } => {
self.push_str(&format!(
"<super::{r} as {r}>::{}",
name.to_snake_case(),
r = iface.resources[*resource].name.to_camel_case(),
));
}
}
self.push_str("(");
if let FunctionKind::Method { .. } = func.kind {
self.push_str("&");
}
self.push_str(&operands.join(", "));
self.push_str(")");
if func.is_async {
self.push_str(".await");
}
self.push_str(";\n");
}
Instruction::Return { amt, .. } => {
self.emit_cleanup();
match amt {
0 => {}
1 => {
self.push_str(&operands[0]);
self.push_str("\n");
}
_ => {
self.push_str("(");
self.push_str(&operands.join(", "));
self.push_str(")\n");
}
}
}
Instruction::ReturnAsyncExport { .. } => {
self.emit_cleanup();
self.push_str(&format!(
"wit_bindgen_rust::rt::async_export_done({}, {});\n",
operands[0], operands[1]
));
}
Instruction::ReturnAsyncImport { .. } => unreachable!(),
Instruction::I32Load { offset } => {
results.push(format!("*(({} + {}) as *const i32)", operands[0], offset));
}
Instruction::I32Load8U { offset } => {
results.push(format!(
"i32::from(*(({} + {}) as *const u8))",
operands[0], offset
));
}
Instruction::I32Load8S { offset } => {
results.push(format!(
"i32::from(*(({} + {}) as *const i8))",
operands[0], offset
));
}
Instruction::I32Load16U { offset } => {
results.push(format!(
"i32::from(*(({} + {}) as *const u16))",
operands[0], offset
));
}
Instruction::I32Load16S { offset } => {
results.push(format!(
"i32::from(*(({} + {}) as *const i16))",
operands[0], offset
));
}
Instruction::I64Load { offset } => {
results.push(format!("*(({} + {}) as *const i64)", operands[0], offset));
}
Instruction::F32Load { offset } => {
results.push(format!("*(({} + {}) as *const f32)", operands[0], offset));
}
Instruction::F64Load { offset } => {
results.push(format!("*(({} + {}) as *const f64)", operands[0], offset));
}
Instruction::I32Store { offset } => {
self.push_str(&format!(
"*(({} + {}) as *mut i32) = {};\n",
operands[1], offset, operands[0]
));
}
Instruction::I32Store8 { offset } => {
self.push_str(&format!(
"*(({} + {}) as *mut u8) = ({}) as u8;\n",
operands[1], offset, operands[0]
));
}
Instruction::I32Store16 { offset } => {
self.push_str(&format!(
"*(({} + {}) as *mut u16) = ({}) as u16;\n",
operands[1], offset, operands[0]
));
}
Instruction::I64Store { offset } => {
self.push_str(&format!(
"*(({} + {}) as *mut i64) = {};\n",
operands[1], offset, operands[0]
));
}
Instruction::F32Store { offset } => {
self.push_str(&format!(
"*(({} + {}) as *mut f32) = {};\n",
operands[1], offset, operands[0]
));
}
Instruction::F64Store { offset } => {
self.push_str(&format!(
"*(({} + {}) as *mut f64) = {};\n",
operands[1], offset, operands[0]
));
}
Instruction::Malloc { .. } => unimplemented!(),
Instruction::Free {
free: _,
size,
align,
} => {
self.push_str(&format!(
"wit_bindgen_rust::rt::canonical_abi_free({} as *mut u8, {}, {});\n",
operands[0], size, align
));
}
}
}
}