use heck::*; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Write; use std::mem; use wit_bindgen_gen_core::wit_parser::abi::{ AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmType, }; use wit_bindgen_gen_core::{uwrite, uwriteln, wit_parser::*, Direction, Files, Generator, Ns}; #[derive(Default)] pub struct C { src: Source, in_import: bool, opts: Opts, funcs: HashMap>, return_pointer_area_size: usize, return_pointer_area_align: usize, sizes: SizeAlign, names: Ns, // The set of types that are considered public (aka need to be in the // header file) which are anonymous and we're effectively monomorphizing. // This is discovered lazily when printing type names. public_anonymous_types: BTreeSet, // This is similar to `public_anonymous_types` where it's discovered // lazily, but the set here are for private types only used in the // implementation of functions. These types go in the implementation file, // not the header file. private_anonymous_types: BTreeSet, // Type definitions for the given `TypeId`. This is printed topologically // at the end. types: HashMap, needs_string: bool, } struct Func { src: Source, } #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "structopt", derive(structopt::StructOpt))] pub struct Opts { // ... } impl Opts { pub fn build(&self) -> C { let mut r = C::new(); r.opts = self.clone(); r } } #[derive(Debug)] struct Return { splat_tuple: bool, scalar: Option, retptrs: Vec, } struct CSig { name: String, sig: String, params: Vec<(bool, String)>, ret: Return, retptrs: Vec, } #[derive(Debug)] enum Scalar { Void, OptionBool(Type), ExpectedEnum { err: TypeId, max_err: usize }, Type(Type), } impl C { pub fn new() -> C { C::default() } fn abi_variant(dir: Direction) -> AbiVariant { // This generator uses the obvious direction to ABI variant mapping. match dir { Direction::Export => AbiVariant::GuestExport, Direction::Import => AbiVariant::GuestImport, } } fn classify_ret(&mut self, iface: &Interface, func: &Function) -> Return { let mut ret = Return { splat_tuple: false, scalar: None, retptrs: Vec::new(), }; ret.return_single(iface, &func.result, &func.result); return ret; } fn print_sig(&mut self, iface: &Interface, func: &Function) -> CSig { let name = format!( "{}_{}", iface.name.to_snake_case(), func.name.to_snake_case() ); self.names.insert(&name).expect("duplicate symbols"); let start = self.src.h.len(); let ret = self.classify_ret(iface, func); match &ret.scalar { None | Some(Scalar::Void) => self.src.h("void"), Some(Scalar::OptionBool(_id)) => self.src.h("bool"), Some(Scalar::ExpectedEnum { err, .. }) => self.print_ty(iface, &Type::Id(*err)), Some(Scalar::Type(ty)) => self.print_ty(iface, ty), } self.src.h(" "); self.src.h(&name); self.src.h("("); let mut params = Vec::new(); for (i, (name, ty)) in func.params.iter().enumerate() { if i > 0 { self.src.h(", "); } self.print_ty(iface, ty); self.src.h(" "); let pointer = self.is_arg_by_pointer(iface, ty); if pointer { self.src.h("*"); } let name = name.to_snake_case(); self.src.h(&name); params.push((pointer, name)); } let mut retptrs = Vec::new(); for (i, ty) in ret.retptrs.iter().enumerate() { if i > 0 || func.params.len() > 0 { self.src.h(", "); } self.print_ty(iface, ty); self.src.h(" *"); let name = format!("ret{}", i); self.src.h(&name); retptrs.push(name); } if func.params.len() == 0 && ret.retptrs.len() == 0 { self.src.h("void"); } self.src.h(")"); let sig = self.src.h[start..].to_string(); self.src.h(";\n"); CSig { sig, name, params, ret, retptrs, } } fn is_arg_by_pointer(&self, iface: &Interface, ty: &Type) -> bool { match ty { Type::Id(id) => match &iface.types[*id].kind { TypeDefKind::Type(t) => self.is_arg_by_pointer(iface, t), TypeDefKind::Variant(_) => true, TypeDefKind::Union(_) => true, TypeDefKind::Option(_) => true, TypeDefKind::Expected(_) => true, TypeDefKind::Enum(_) => false, TypeDefKind::Flags(_) => false, TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, TypeDefKind::Future(_) => todo!("is_arg_by_pointer for future"), TypeDefKind::Stream(_) => todo!("is_arg_by_pointer for stream"), }, Type::String => true, _ => false, } } fn type_string(&mut self, iface: &Interface, ty: &Type) -> String { // Getting a type string happens during codegen, and by default means // that this is a private type that's being generated. This means we // want to keep track of new anonymous types that are *only* mentioned // in methods like this, so we can place those types in the C file // instead of the header interface file. let prev = mem::take(&mut self.src.h); let prev_public = mem::take(&mut self.public_anonymous_types); let prev_private = mem::take(&mut self.private_anonymous_types); // Print the type, which will collect into the fields that we replaced // above. self.print_ty(iface, ty); // Reset our public/private sets back to what they were beforehand. // Note that `print_ty` always adds to the public set, so we're // inverting the meaning here by interpreting those as new private // types. let new_private = mem::replace(&mut self.public_anonymous_types, prev_public); assert!(self.private_anonymous_types.is_empty()); self.private_anonymous_types = prev_private; // For all new private types found while we printed this type, if the // type isn't already public then it's a new private type. for id in new_private { if !self.public_anonymous_types.contains(&id) { self.private_anonymous_types.insert(id); } } mem::replace(&mut self.src.h, prev).into() } fn print_ty(&mut self, iface: &Interface, ty: &Type) { match ty { Type::Unit => self.src.h("void"), Type::Bool => self.src.h("bool"), Type::Char => self.src.h("uint32_t"), // TODO: better type? Type::U8 => self.src.h("uint8_t"), Type::S8 => self.src.h("int8_t"), Type::U16 => self.src.h("uint16_t"), Type::S16 => self.src.h("int16_t"), Type::U32 => self.src.h("uint32_t"), Type::S32 => self.src.h("int32_t"), Type::U64 => self.src.h("uint64_t"), Type::S64 => self.src.h("int64_t"), Type::Float32 => self.src.h("float"), Type::Float64 => self.src.h("double"), Type::Handle(id) => { self.print_namespace(iface); self.src.h(&iface.resources[*id].name.to_snake_case()); self.src.h("_t"); } Type::String => { self.print_namespace(iface); self.src.h("string_t"); self.needs_string = true; } Type::Id(id) => { let ty = &iface.types[*id]; match &ty.name { Some(name) => { self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t"); } None => match &ty.kind { TypeDefKind::Type(t) => self.print_ty(iface, t), _ => { self.public_anonymous_types.insert(*id); self.private_anonymous_types.remove(id); self.print_namespace(iface); self.print_ty_name(iface, &Type::Id(*id)); self.src.h("_t"); } }, } } } } fn print_ty_name(&mut self, iface: &Interface, ty: &Type) { match ty { Type::Unit => self.src.h("unit"), Type::Bool => self.src.h("bool"), Type::Char => self.src.h("char32"), Type::U8 => self.src.h("u8"), Type::S8 => self.src.h("s8"), Type::U16 => self.src.h("u16"), Type::S16 => self.src.h("s16"), Type::U32 => self.src.h("u32"), Type::S32 => self.src.h("s32"), Type::U64 => self.src.h("u64"), Type::S64 => self.src.h("s64"), Type::Float32 => self.src.h("float32"), Type::Float64 => self.src.h("float64"), Type::Handle(id) => self.src.h(&iface.resources[*id].name.to_snake_case()), Type::String => self.src.h("string"), Type::Id(id) => { let ty = &iface.types[*id]; if let Some(name) = &ty.name { return self.src.h(&name.to_snake_case()); } match &ty.kind { TypeDefKind::Type(t) => self.print_ty_name(iface, t), TypeDefKind::Record(_) | TypeDefKind::Flags(_) | TypeDefKind::Enum(_) | TypeDefKind::Variant(_) | TypeDefKind::Union(_) => { unimplemented!() } TypeDefKind::Tuple(t) => { self.src.h("tuple"); self.src.h(&t.types.len().to_string()); for ty in t.types.iter() { self.src.h("_"); self.print_ty_name(iface, ty); } } TypeDefKind::Option(ty) => { self.src.h("option_"); self.print_ty_name(iface, ty); } TypeDefKind::Expected(e) => { self.src.h("expected_"); self.print_ty_name(iface, &e.ok); self.src.h("_"); self.print_ty_name(iface, &e.err); } TypeDefKind::List(t) => { self.src.h("list_"); self.print_ty_name(iface, t); } TypeDefKind::Future(t) => { self.src.h("future_"); self.print_ty_name(iface, t); } TypeDefKind::Stream(s) => { self.src.h("stream_"); self.print_ty_name(iface, &s.element); self.src.h("_"); self.print_ty_name(iface, &s.end); } } } } } fn print_anonymous_type(&mut self, iface: &Interface, ty: TypeId) { let prev = mem::take(&mut self.src.h); self.src.h("typedef "); let kind = &iface.types[ty].kind; match kind { TypeDefKind::Type(_) | TypeDefKind::Flags(_) | TypeDefKind::Record(_) | TypeDefKind::Enum(_) | TypeDefKind::Variant(_) | TypeDefKind::Union(_) => { unreachable!() } TypeDefKind::Tuple(t) => { self.src.h("struct {\n"); for (i, t) in t.types.iter().enumerate() { self.print_ty(iface, t); uwriteln!(self.src.h, " f{i};"); } self.src.h("}"); } TypeDefKind::Option(t) => { self.src.h("struct {\n"); self.src.h("bool is_some;\n"); if !self.is_empty_type(iface, t) { self.print_ty(iface, t); self.src.h(" val;\n"); } self.src.h("}"); } TypeDefKind::Expected(e) => { self.src.h("struct { bool is_err; union { "); if !self.is_empty_type(iface, &e.ok) { self.print_ty(iface, &e.ok); self.src.h(" ok;\n"); } if !self.is_empty_type(iface, &e.err) { self.print_ty(iface, &e.err); self.src.h(" err;\n"); } self.src.h("} val;\n"); self.src.h("}"); } TypeDefKind::List(t) => { self.src.h("struct {\n"); self.print_ty(iface, t); self.src.h(" *ptr;\n"); self.src.h("size_t len;\n"); self.src.h("}"); } TypeDefKind::Future(_) => todo!("print_anonymous_type for future"), TypeDefKind::Stream(_) => todo!("print_anonymous_type for stream"), } self.src.h(" "); self.print_namespace(iface); self.print_ty_name(iface, &Type::Id(ty)); self.src.h("_t;\n"); self.types.insert(ty, mem::replace(&mut self.src.h, prev)); } fn is_empty_type(&self, iface: &Interface, ty: &Type) -> bool { let id = match ty { Type::Id(id) => *id, Type::Unit => return true, _ => return false, }; match &iface.types[id].kind { TypeDefKind::Type(t) => self.is_empty_type(iface, t), TypeDefKind::Record(r) => r.fields.is_empty(), TypeDefKind::Tuple(t) => t.types.is_empty(), _ => false, } } fn print_intrinsics(&mut self) { // Note that these intrinsics are declared as `weak` so they can be // overridden from some other symbol. self.src.c(" __attribute__((weak, export_name(\"canonical_abi_realloc\"))) void *canonical_abi_realloc( void *ptr, size_t orig_size, size_t org_align, size_t new_size ) { void *ret = realloc(ptr, new_size); if (!ret) abort(); return ret; } __attribute__((weak, export_name(\"canonical_abi_free\"))) void canonical_abi_free( void *ptr, size_t size, size_t align ) { free(ptr); } "); } fn print_namespace(&mut self, iface: &Interface) { self.src.h(&iface.name.to_snake_case()); self.src.h("_"); } fn print_dtor(&mut self, iface: &Interface, id: TypeId) { let ty = Type::Id(id); if !self.owns_anything(iface, &ty) { return; } let pos = self.src.h.len(); self.src.h("void "); self.print_namespace(iface); self.print_ty_name(iface, &ty); self.src.h("_free("); self.print_namespace(iface); self.print_ty_name(iface, &ty); self.src.h("_t *ptr)"); self.src.c(&self.src.h[pos..].to_string()); self.src.h(";\n"); self.src.c(" {\n"); match &iface.types[id].kind { TypeDefKind::Type(t) => self.free(iface, t, "ptr"), TypeDefKind::Flags(_) => {} TypeDefKind::Enum(_) => {} TypeDefKind::Record(r) => { for field in r.fields.iter() { if !self.owns_anything(iface, &field.ty) { continue; } self.free( iface, &field.ty, &format!("&ptr->{}", field.name.to_snake_case()), ); } } TypeDefKind::Tuple(t) => { for (i, ty) in t.types.iter().enumerate() { if !self.owns_anything(iface, ty) { continue; } self.free(iface, ty, &format!("&ptr->f{i}")); } } TypeDefKind::List(t) => { if self.owns_anything(iface, t) { self.src.c("for (size_t i = 0; i < ptr->len; i++) {\n"); self.free(iface, t, "&ptr->ptr[i]"); self.src.c("}\n"); } uwriteln!( self.src.c, "canonical_abi_free(ptr->ptr, ptr->len * {}, {});", self.sizes.size(t), self.sizes.align(t), ); } TypeDefKind::Variant(v) => { self.src.c("switch ((int32_t) ptr->tag) {\n"); for (i, case) in v.cases.iter().enumerate() { if !self.owns_anything(iface, &case.ty) { continue; } uwriteln!(self.src.c, "case {}: {{", i); let expr = format!("&ptr->val.{}", case.name.to_snake_case()); self.free(iface, &case.ty, &expr); self.src.c("break;\n"); self.src.c("}\n"); } self.src.c("}\n"); } TypeDefKind::Union(u) => { self.src.c("switch ((int32_t) ptr->tag) {\n"); for (i, case) in u.cases.iter().enumerate() { if !self.owns_anything(iface, &case.ty) { continue; } uwriteln!(self.src.c, "case {i}: {{"); let expr = format!("&ptr->val.f{i}"); self.free(iface, &case.ty, &expr); self.src.c("break;\n"); self.src.c("}\n"); } self.src.c("}\n"); } TypeDefKind::Option(t) => { self.src.c("if (ptr->is_some) {\n"); self.free(iface, t, "&ptr->val"); self.src.c("}\n"); } TypeDefKind::Expected(e) => { self.src.c("if (!ptr->is_err) {\n"); if self.owns_anything(iface, &e.ok) { self.free(iface, &e.ok, "&ptr->val.ok"); } if self.owns_anything(iface, &e.err) { self.src.c("} else {\n"); self.free(iface, &e.err, "&ptr->val.err"); } self.src.c("}\n"); } TypeDefKind::Future(_) => todo!("print_dtor for future"), TypeDefKind::Stream(_) => todo!("print_dtor for stream"), } self.src.c("}\n"); } fn owns_anything(&self, iface: &Interface, ty: &Type) -> bool { let id = match ty { Type::Id(id) => *id, Type::String => return true, Type::Handle(_) => return true, _ => return false, }; match &iface.types[id].kind { TypeDefKind::Type(t) => self.owns_anything(iface, t), TypeDefKind::Record(r) => r.fields.iter().any(|t| self.owns_anything(iface, &t.ty)), TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.owns_anything(iface, t)), TypeDefKind::Flags(_) => false, TypeDefKind::Enum(_) => false, TypeDefKind::List(_) => true, TypeDefKind::Variant(v) => v.cases.iter().any(|c| self.owns_anything(iface, &c.ty)), TypeDefKind::Union(v) => v .cases .iter() .any(|case| self.owns_anything(iface, &case.ty)), TypeDefKind::Option(t) => self.owns_anything(iface, t), TypeDefKind::Expected(e) => { self.owns_anything(iface, &e.ok) || self.owns_anything(iface, &e.err) } TypeDefKind::Future(_) => todo!("owns_anything for future"), TypeDefKind::Stream(_) => todo!("owns_anything for stream"), } } fn free(&mut self, iface: &Interface, ty: &Type, expr: &str) { let prev = mem::take(&mut self.src.h); self.print_namespace(iface); self.print_ty_name(iface, ty); let name = mem::replace(&mut self.src.h, prev); self.src.c(&name); self.src.c("_free("); self.src.c(expr); self.src.c(");\n"); } fn docs(&mut self, docs: &Docs) { let docs = match &docs.contents { Some(docs) => docs, None => return, }; for line in docs.trim().lines() { self.src.h("// "); self.src.h(line); self.src.h("\n"); } } } impl Return { fn return_single(&mut self, iface: &Interface, ty: &Type, orig_ty: &Type) { let id = match ty { Type::Id(id) => *id, Type::String => { self.retptrs.push(*orig_ty); return; } Type::Unit => { self.scalar = Some(Scalar::Void); return; } _ => { self.scalar = Some(Scalar::Type(*orig_ty)); return; } }; match &iface.types[id].kind { TypeDefKind::Type(t) => self.return_single(iface, t, orig_ty), // Flags are returned as their bare values TypeDefKind::Flags(_) => { self.scalar = Some(Scalar::Type(*orig_ty)); } // Tuples get splatted to multiple return pointers TypeDefKind::Tuple(_) => self.splat_tuples(iface, ty, orig_ty), // Record returns are always through a pointer TypeDefKind::Record(_) => { self.retptrs.push(*orig_ty); } // other records/lists/buffers always go to return pointers TypeDefKind::List(_) => self.retptrs.push(*orig_ty), // Enums are scalars TypeDefKind::Enum(_) => { self.scalar = Some(Scalar::Type(*orig_ty)); } // Unpack optional returns where a boolean discriminant is // returned and then the actual type returned is returned // through a return pointer. TypeDefKind::Option(ty) => { self.scalar = Some(Scalar::OptionBool(*ty)); self.retptrs.push(*ty); } // Unpack `expected` returns where `E` looks like an enum // so we can return that in the scalar return and have `T` get // returned through the normal returns. TypeDefKind::Expected(e) => { if let Type::Id(err) = e.err { if let TypeDefKind::Enum(enum_) = &iface.types[err].kind { self.scalar = Some(Scalar::ExpectedEnum { err, max_err: enum_.cases.len(), }); self.splat_tuples(iface, &e.ok, &e.ok); return; } } // otherwise just return the variant via a normal // return pointer self.retptrs.push(*orig_ty); } TypeDefKind::Variant(_) | TypeDefKind::Union(_) => { self.retptrs.push(*orig_ty); } TypeDefKind::Future(_) => todo!("return_single for future"), TypeDefKind::Stream(_) => todo!("return_single for stream"), } } fn splat_tuples(&mut self, iface: &Interface, ty: &Type, orig_ty: &Type) { let id = match ty { Type::Id(id) => *id, Type::Unit => return, _ => { self.retptrs.push(*orig_ty); return; } }; match &iface.types[id].kind { TypeDefKind::Tuple(t) => { self.splat_tuple = true; self.retptrs.extend(t.types.iter()); } _ => self.retptrs.push(*orig_ty), } } } impl Generator for C { fn preprocess_one(&mut self, iface: &Interface, dir: Direction) { let variant = Self::abi_variant(dir); self.sizes.fill(iface); self.in_import = variant == AbiVariant::GuestImport; } fn type_record( &mut self, iface: &Interface, id: TypeId, name: &str, record: &Record, docs: &Docs, ) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.names.insert(&name.to_snake_case()).unwrap(); self.src.h("typedef struct {\n"); for field in record.fields.iter() { self.print_ty(iface, &field.ty); self.src.h(" "); self.src.h(&field.name.to_snake_case()); self.src.h(";\n"); } self.src.h("} "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_tuple( &mut self, iface: &Interface, id: TypeId, name: &str, tuple: &Tuple, docs: &Docs, ) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.names.insert(&name.to_snake_case()).unwrap(); self.src.h("typedef struct {\n"); for (i, ty) in tuple.types.iter().enumerate() { self.print_ty(iface, ty); uwriteln!(self.src.h, " f{i};"); } self.src.h("} "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_flags( &mut self, iface: &Interface, id: TypeId, name: &str, flags: &Flags, docs: &Docs, ) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.names.insert(&name.to_snake_case()).unwrap(); self.src.h("typedef "); let repr = flags_repr(flags); self.src.h(int_repr(repr)); self.src.h(" "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); for (i, flag) in flags.flags.iter().enumerate() { uwriteln!( self.src.h, "#define {}_{}_{} (1 << {})", iface.name.to_shouty_snake_case(), name.to_shouty_snake_case(), flag.name.to_shouty_snake_case(), i, ); } self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_variant( &mut self, iface: &Interface, id: TypeId, name: &str, variant: &Variant, docs: &Docs, ) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.names.insert(&name.to_snake_case()).unwrap(); self.src.h("typedef struct {\n"); self.src.h(int_repr(variant.tag())); self.src.h(" tag;\n"); self.src.h("union {\n"); for case in variant.cases.iter() { if self.is_empty_type(iface, &case.ty) { continue; } self.print_ty(iface, &case.ty); self.src.h(" "); self.src.h(&case.name.to_snake_case()); self.src.h(";\n"); } self.src.h("} val;\n"); self.src.h("} "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); for (i, case) in variant.cases.iter().enumerate() { uwriteln!( self.src.h, "#define {}_{}_{} {}", iface.name.to_shouty_snake_case(), name.to_shouty_snake_case(), case.name.to_shouty_snake_case(), i, ); } self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_union( &mut self, iface: &Interface, id: TypeId, name: &str, union: &Union, docs: &Docs, ) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.names.insert(&name.to_snake_case()).unwrap(); self.src.h("typedef struct {\n"); self.src.h(int_repr(union.tag())); self.src.h(" tag;\n"); self.src.h("union {\n"); for (i, case) in union.cases.iter().enumerate() { self.print_ty(iface, &case.ty); uwriteln!(self.src.h, " f{i};"); } self.src.h("} val;\n"); self.src.h("} "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_option( &mut self, iface: &Interface, id: TypeId, name: &str, payload: &Type, docs: &Docs, ) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.names.insert(&name.to_snake_case()).unwrap(); self.src.h("typedef struct {\n"); self.src.h("bool is_some;\n"); if !self.is_empty_type(iface, payload) { self.print_ty(iface, payload); self.src.h(" val;\n"); } self.src.h("} "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_expected( &mut self, iface: &Interface, id: TypeId, name: &str, expected: &Expected, docs: &Docs, ) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.names.insert(&name.to_snake_case()).unwrap(); self.src.h("typedef struct {\n"); self.src.h("bool is_err;\n"); self.src.h("union {\n"); if !self.is_empty_type(iface, &expected.ok) { self.print_ty(iface, &expected.ok); self.src.h(" ok;\n"); } if !self.is_empty_type(iface, &expected.err) { self.print_ty(iface, &expected.err); self.src.h(" err;\n"); } self.src.h("} val;\n"); self.src.h("} "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_enum(&mut self, iface: &Interface, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.names.insert(&name.to_snake_case()).unwrap(); self.src.h("typedef "); self.src.h(int_repr(enum_.tag())); self.src.h(" "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); for (i, case) in enum_.cases.iter().enumerate() { uwriteln!( self.src.h, "#define {}_{}_{} {}", iface.name.to_shouty_snake_case(), name.to_shouty_snake_case(), case.name.to_shouty_snake_case(), i, ); } self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_resource(&mut self, iface: &Interface, ty: ResourceId) { drop((iface, ty)); } fn type_alias(&mut self, iface: &Interface, id: TypeId, name: &str, ty: &Type, docs: &Docs) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.src.h("typedef "); self.print_ty(iface, ty); self.src.h(" "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_list(&mut self, iface: &Interface, id: TypeId, name: &str, ty: &Type, docs: &Docs) { let prev = mem::take(&mut self.src.h); self.docs(docs); self.src.h("typedef struct {\n"); self.print_ty(iface, ty); self.src.h(" *ptr;\n"); self.src.h("size_t len;\n"); self.src.h("} "); self.print_namespace(iface); self.src.h(&name.to_snake_case()); self.src.h("_t;\n"); self.types.insert(id, mem::replace(&mut self.src.h, prev)); } fn type_builtin(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) { drop((iface, _id, name, ty, docs)); } fn import(&mut self, iface: &Interface, func: &Function) { assert!(!func.is_async, "async not supported yet"); let prev = mem::take(&mut self.src); let sig = iface.wasm_signature(AbiVariant::GuestImport, func); // In the private C file, print a function declaration which is the // actual wasm import that we'll be calling, and this has the raw wasm // signature. uwriteln!( self.src.c, "__attribute__((import_module(\"{}\"), import_name(\"{}\")))", iface.name, func.name ); let import_name = self.names.tmp(&format!( "__wasm_import_{}_{}", iface.name.to_snake_case(), func.name.to_snake_case() )); match sig.results.len() { 0 => self.src.c("void"), 1 => self.src.c(wasm_type(sig.results[0])), _ => unimplemented!("multi-value return not supported"), } self.src.c(" "); self.src.c(&import_name); self.src.c("("); for (i, param) in sig.params.iter().enumerate() { if i > 0 { self.src.c(", "); } self.src.c(wasm_type(*param)); } if sig.params.len() == 0 { self.src.c("void"); } self.src.c(");\n"); // Print the public facing signature into the header, and since that's // what we are defining also print it into the C file. let c_sig = self.print_sig(iface, func); self.src.c(&c_sig.sig); self.src.c(" {\n"); let mut f = FunctionBindgen::new(self, c_sig, &import_name); for (pointer, param) in f.sig.params.iter() { f.locals.insert(param).unwrap(); if *pointer { f.params.push(format!("*{}", param)); } else { f.params.push(param.clone()); } } for ptr in f.sig.retptrs.iter() { f.locals.insert(ptr).unwrap(); } iface.call( AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, &mut f, ); let FunctionBindgen { src, .. } = f; self.src.c(&String::from(src)); self.src.c("}\n"); let src = mem::replace(&mut self.src, prev); self.funcs .entry(iface.name.to_string()) .or_insert(Vec::new()) .push(Func { src }); } fn export(&mut self, iface: &Interface, func: &Function) { assert!(!func.is_async, "async not supported yet"); let prev = mem::take(&mut self.src); let sig = iface.wasm_signature(AbiVariant::GuestExport, func); // Print the actual header for this function into the header file, and // it's what we'll be calling. let c_sig = self.print_sig(iface, func); // Generate, in the C source file, the raw wasm signature that has the // canonical ABI. uwriteln!( self.src.c, "__attribute__((export_name(\"{}\")))", func.name ); let import_name = self.names.tmp(&format!( "__wasm_export_{}_{}", iface.name.to_snake_case(), func.name.to_snake_case() )); let mut f = FunctionBindgen::new(self, c_sig, &import_name); match sig.results.len() { 0 => f.gen.src.c("void"), 1 => f.gen.src.c(wasm_type(sig.results[0])), _ => unimplemented!("multi-value return not supported"), } f.gen.src.c(" "); f.gen.src.c(&import_name); f.gen.src.c("("); for (i, param) in sig.params.iter().enumerate() { if i > 0 { f.gen.src.c(", "); } let name = f.locals.tmp("arg"); uwrite!(f.gen.src.c, "{} {}", wasm_type(*param), name); f.params.push(name); } if sig.params.len() == 0 { f.gen.src.c("void"); } f.gen.src.c(") {\n"); // Perform all lifting/lowering and append it to our src. iface.call( AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, &mut f, ); let FunctionBindgen { src, .. } = f; self.src.c(&src); self.src.c("}\n"); let src = mem::replace(&mut self.src, prev); self.funcs .entry(iface.name.to_string()) .or_insert(Vec::new()) .push(Func { src }); } fn finish_one(&mut self, iface: &Interface, files: &mut Files) { uwrite!( self.src.h, "\ #ifndef __BINDINGS_{0}_H #define __BINDINGS_{0}_H #ifdef __cplusplus extern \"C\" {{ #endif #include #include ", iface.name.to_shouty_snake_case(), ); uwrite!( self.src.c, "\ #include #include <{}.h> ", iface.name.to_kebab_case(), ); self.print_intrinsics(); for (_, resource) in iface.resources.iter() { let ns = iface.name.to_snake_case(); let name = resource.name.to_snake_case(); uwrite!( self.src.h, " typedef struct {{ uint32_t idx; }} {ns}_{name}_t; void {ns}_{name}_free({ns}_{name}_t *ptr); {ns}_{name}_t {ns}_{name}_clone({ns}_{name}_t *ptr); ", ns = ns, name = name, ); uwrite!( self.src.c, " __attribute__((import_module(\"canonical_abi\"), import_name(\"resource_drop_{name_orig}\"))) void __resource_{name}_drop(uint32_t idx); void {ns}_{name}_free({ns}_{name}_t *ptr) {{ __resource_{name}_drop(ptr->idx); }} __attribute__((import_module(\"canonical_abi\"), import_name(\"resource_clone_{name_orig}\"))) uint32_t __resource_{name}_clone(uint32_t idx); {ns}_{name}_t {ns}_{name}_clone({ns}_{name}_t *ptr) {{ return ({ns}_{name}_t){{__resource_{name}_clone(ptr->idx)}}; }} ", ns = ns, name = name, name_orig = resource.name, ); // Exported resources have more capabilities, they can create new // resources and get the private value that it was created with. // Furthermore we also define the destructor which delegates to the // actual user-defined destructor, if any. if !self.in_import { uwrite!( self.src.h, "\ {ns}_{name}_t {ns}_{name}_new(void *data); void* {ns}_{name}_get({ns}_{name}_t *ptr); __attribute__((weak)) void {ns}_{name}_dtor(void *data); ", ns = ns, name = name, ); uwrite!( self.src.c, " __attribute__((import_module(\"canonical_abi\"), import_name(\"resource_new_{name_orig}\"))) uint32_t __resource_{name}_new(uint32_t val); {ns}_{name}_t {ns}_{name}_new(void *data) {{ return ({ns}_{name}_t){{__resource_{name}_new((uint32_t) data)}}; }} __attribute__((import_module(\"canonical_abi\"), import_name(\"resource_get_{name_orig}\"))) uint32_t __resource_{name}_get(uint32_t idx); void* {ns}_{name}_get({ns}_{name}_t *ptr) {{ return (void*) __resource_{name}_get(ptr->idx); }} __attribute__((export_name(\"canonical_abi_drop_{name_orig}\"))) void __resource_{name}_dtor(uint32_t val) {{ if ({ns}_{name}_dtor) {ns}_{name}_dtor((void*) val); }} ", ns = ns, name = name, name_orig = resource.name, ); } } // Continuously generate anonymous types while we continue to find more // // First we take care of the public set of anonymous types. This will // iteratively print them and also remove any references from the // private set if we happen to also reference them. while !self.public_anonymous_types.is_empty() { for ty in mem::take(&mut self.public_anonymous_types) { self.print_anonymous_type(iface, ty); } } // Next we take care of private types. To do this we have basically the // same loop as above, after we switch the sets. We record, however, // all private types in a local set here to later determine if the type // needs to be in the C file or the H file. // // Note though that we don't re-print a type (and consider it private) // if we already printed it above as part of the public set. let mut private_types = HashSet::new(); self.public_anonymous_types = mem::take(&mut self.private_anonymous_types); while !self.public_anonymous_types.is_empty() { for ty in mem::take(&mut self.public_anonymous_types) { if self.types.contains_key(&ty) { continue; } private_types.insert(ty); self.print_anonymous_type(iface, ty); } } if self.needs_string { uwrite!( self.src.h, " typedef struct {{ char *ptr; size_t len; }} {0}_string_t; void {0}_string_set({0}_string_t *ret, const char *s); void {0}_string_dup({0}_string_t *ret, const char *s); void {0}_string_free({0}_string_t *ret); ", iface.name.to_snake_case(), ); self.src.c("#include \n"); uwrite!( self.src.c, " void {0}_string_set({0}_string_t *ret, const char *s) {{ ret->ptr = (char*) s; ret->len = strlen(s); }} void {0}_string_dup({0}_string_t *ret, const char *s) {{ ret->len = strlen(s); ret->ptr = canonical_abi_realloc(NULL, 0, 1, ret->len); memcpy(ret->ptr, s, ret->len); }} void {0}_string_free({0}_string_t *ret) {{ canonical_abi_free(ret->ptr, ret->len, 1); ret->ptr = NULL; ret->len = 0; }} ", iface.name.to_snake_case(), ); } // Afterwards print all types. Note that this print must be in a // topological order, so we for id in iface.topological_types() { if let Some(ty) = self.types.get(&id) { if private_types.contains(&id) { self.src.c(ty); } else { self.src.h(ty); self.print_dtor(iface, id); } } } if self.return_pointer_area_size > 0 { uwrite!( self.src.c, " __attribute__((aligned({}))) static uint8_t RET_AREA[{}]; ", self.return_pointer_area_align, self.return_pointer_area_size, ); } for (_module, funcs) in mem::take(&mut self.funcs) { for func in funcs { self.src.h(&func.src.h); self.src.c(&func.src.c); } } self.src.h("\ #ifdef __cplusplus } #endif "); self.src.h("#endif\n"); files.push( &format!("{}.c", iface.name.to_kebab_case()), self.src.c.as_bytes(), ); files.push( &format!("{}.h", iface.name.to_kebab_case()), self.src.h.as_bytes(), ); } } struct FunctionBindgen<'a> { gen: &'a mut C, locals: Ns, // tmp: usize, src: wit_bindgen_gen_core::Source, sig: CSig, func_to_call: &'a str, block_storage: Vec, blocks: Vec<(String, Vec)>, payloads: Vec, params: Vec, wasm_return: Option, } impl<'a> FunctionBindgen<'a> { fn new(gen: &'a mut C, sig: CSig, func_to_call: &'a str) -> FunctionBindgen<'a> { FunctionBindgen { gen, sig, locals: Default::default(), src: Default::default(), func_to_call, block_storage: Vec::new(), blocks: Vec::new(), payloads: Vec::new(), params: Vec::new(), wasm_return: None, } } fn store_op(&mut self, op: &str, loc: &str) { self.src.push_str(loc); self.src.push_str(" = "); self.src.push_str(op); self.src.push_str(";\n"); } fn load(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { results.push(format!("*(({}*) ({} + {}))", ty, operands[0], offset)); } fn load_ext(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { self.load(ty, offset, operands, results); let result = results.pop().unwrap(); results.push(format!("(int32_t) ({})", result)); } fn store(&mut self, ty: &str, offset: i32, operands: &[String]) { uwriteln!( self.src, "*(({}*)({} + {})) = {};", ty, operands[1], offset, operands[0] ); } fn store_in_retptrs(&mut self, operands: &[String]) { if self.sig.ret.splat_tuple { assert_eq!(operands.len(), 1); let op = &operands[0]; for (i, ptr) in self.sig.retptrs.clone().into_iter().enumerate() { self.store_op(&format!("{}.f{}", op, i), &format!("*{}", ptr)); } // ... } else { assert_eq!(operands.len(), self.sig.retptrs.len()); for (op, ptr) in operands.iter().zip(self.sig.retptrs.clone()) { self.store_op(op, &format!("*{}", ptr)); } } } } impl Bindgen for FunctionBindgen<'_> { type Operand = String; fn sizes(&self) -> &SizeAlign { &self.gen.sizes } fn push_block(&mut self) { let prev = mem::take(&mut self.src); self.block_storage.push(prev); } fn finish_block(&mut self, operands: &mut Vec) { let to_restore = self.block_storage.pop().unwrap(); let src = mem::replace(&mut self.src, to_restore); self.blocks.push((src.into(), mem::take(operands))); } 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 ptr = self.locals.tmp("ptr"); uwriteln!(self.src, "int32_t {} = (int32_t) &RET_AREA;", ptr); ptr } 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, results: &mut Vec, ) { match inst { Instruction::GetArg { nth } => results.push(self.params[*nth].clone()), Instruction::I32Const { val } => results.push(val.to_string()), Instruction::ConstZero { tys } => { for _ in tys.iter() { results.push("0".to_string()); } } // TODO: checked? Instruction::U8FromI32 => results.push(format!("(uint8_t) ({})", operands[0])), Instruction::S8FromI32 => results.push(format!("(int8_t) ({})", operands[0])), Instruction::U16FromI32 => results.push(format!("(uint16_t) ({})", operands[0])), Instruction::S16FromI32 => results.push(format!("(int16_t) ({})", operands[0])), Instruction::U32FromI32 => results.push(format!("(uint32_t) ({})", operands[0])), Instruction::S32FromI32 | Instruction::S64FromI64 => results.push(operands[0].clone()), Instruction::U64FromI64 => results.push(format!("(uint64_t) ({})", operands[0])), Instruction::I32FromU8 | Instruction::I32FromS8 | Instruction::I32FromU16 | Instruction::I32FromS16 | Instruction::I32FromU32 => { results.push(format!("(int32_t) ({})", operands[0])); } Instruction::I32FromS32 | Instruction::I64FromS64 => results.push(operands[0].clone()), Instruction::I64FromU64 => { results.push(format!("(int64_t) ({})", operands[0])); } // f32/f64 have the same representation in the import type and in C, // so no conversions necessary. Instruction::F32FromFloat32 | Instruction::F64FromFloat64 | Instruction::Float32FromF32 | Instruction::Float64FromF64 => { results.push(operands[0].clone()); } // TODO: checked Instruction::CharFromI32 => { results.push(format!("(uint32_t) ({})", operands[0])); } Instruction::I32FromChar => { results.push(format!("(int32_t) ({})", operands[0])); } Instruction::Bitcasts { casts } => { for (cast, op) in casts.iter().zip(operands) { let op = op; match cast { Bitcast::I32ToF32 | Bitcast::I64ToF32 => { results .push(format!("((union {{ int32_t a; float b; }}){{ {} }}).b", op)); } Bitcast::F32ToI32 | Bitcast::F32ToI64 => { results .push(format!("((union {{ float a; int32_t b; }}){{ {} }}).b", op)); } Bitcast::I64ToF64 => { results.push(format!( "((union {{ int64_t a; double b; }}){{ {} }}).b", op )); } Bitcast::F64ToI64 => { results.push(format!( "((union {{ double a; int64_t b; }}){{ {} }}).b", op )); } Bitcast::I32ToI64 => { results.push(format!("(int64_t) {}", op)); } Bitcast::I64ToI32 => { results.push(format!("(int32_t) {}", op)); } Bitcast::None => results.push(op.to_string()), } } } Instruction::UnitLower => {} Instruction::UnitLift => { results.push("INVALID".to_string()); } Instruction::BoolFromI32 | Instruction::I32FromBool => { results.push(operands[0].clone()); } Instruction::I32FromOwnedHandle { .. } | Instruction::I32FromBorrowedHandle { .. } => { results.push(format!("({}).idx", operands[0])); } Instruction::HandleBorrowedFromI32 { ty, .. } | Instruction::HandleOwnedFromI32 { ty, .. } => { results.push(format!( "({}_{}_t){{ {} }}", iface.name.to_snake_case(), iface.resources[*ty].name.to_snake_case(), operands[0], )); } Instruction::RecordLower { record, .. } => { let op = &operands[0]; for f in record.fields.iter() { results.push(format!("({}).{}", op, f.name.to_snake_case())); } } Instruction::RecordLift { ty, .. } => { let name = self.gen.type_string(iface, &Type::Id(*ty)); let mut result = format!("({}) {{\n", name); for op in operands { uwriteln!(result, "{},", op); } result.push_str("}"); results.push(result); } Instruction::TupleLower { tuple, .. } => { let op = &operands[0]; for i in 0..tuple.types.len() { results.push(format!("({}).f{}", op, i)); } } Instruction::TupleLift { ty, .. } => { let name = self.gen.type_string(iface, &Type::Id(*ty)); let mut result = format!("({}) {{\n", name); for op in operands { uwriteln!(result, "{},", op); } result.push_str("}"); results.push(result); } // TODO: checked Instruction::FlagsLower { flags, ty, .. } => match flags_repr(flags) { Int::U8 | Int::U16 | Int::U32 => { results.push(operands.pop().unwrap()); } Int::U64 => { let name = self.gen.type_string(iface, &Type::Id(*ty)); let tmp = self.locals.tmp("flags"); uwriteln!(self.src, "{name} {tmp} = {};", operands[0]); results.push(format!("{tmp} & 0xffffffff")); results.push(format!("({tmp} >> 32) & 0xffffffff")); } }, Instruction::FlagsLift { flags, ty, .. } => match flags_repr(flags) { Int::U8 | Int::U16 | Int::U32 => { results.push(operands.pop().unwrap()); } Int::U64 => { let name = self.gen.type_string(iface, &Type::Id(*ty)); let op0 = &operands[0]; let op1 = &operands[1]; results.push(format!("(({name}) ({op0})) | ((({name}) ({op1})) << 32)")); } }, Instruction::VariantPayloadName => { let name = self.locals.tmp("payload"); results.push(format!("*{}", name)); self.payloads.push(name); } Instruction::VariantLower { variant, results: result_types, .. } => { let blocks = self .blocks .drain(self.blocks.len() - variant.cases.len()..) .collect::>(); let payloads = self .payloads .drain(self.payloads.len() - variant.cases.len()..) .collect::>(); let mut variant_results = Vec::with_capacity(result_types.len()); for ty in result_types.iter() { let name = self.locals.tmp("variant"); results.push(name.clone()); self.src.push_str(wasm_type(*ty)); self.src.push_str(" "); self.src.push_str(&name); self.src.push_str(";\n"); variant_results.push(name); } let expr_to_match = format!("({}).tag", operands[0]); uwriteln!(self.src, "switch ((int32_t) {}) {{", expr_to_match); for (i, ((case, (block, block_results)), payload)) in variant.cases.iter().zip(blocks).zip(payloads).enumerate() { uwriteln!(self.src, "case {}: {{", i); if !self.gen.is_empty_type(iface, &case.ty) { let ty = self.gen.type_string(iface, &case.ty); uwrite!( self.src, "const {} *{} = &({}).val", ty, payload, operands[0], ); self.src.push_str("."); self.src.push_str(&case.name.to_snake_case()); self.src.push_str(";\n"); } self.src.push_str(&block); for (name, result) in variant_results.iter().zip(&block_results) { uwriteln!(self.src, "{} = {};", name, result); } self.src.push_str("break;\n}\n"); } self.src.push_str("}\n"); } Instruction::VariantLift { variant, ty, .. } => { let blocks = self .blocks .drain(self.blocks.len() - variant.cases.len()..) .collect::>(); let ty = self.gen.type_string(iface, &Type::Id(*ty)); let result = self.locals.tmp("variant"); uwriteln!(self.src, "{} {};", ty, result); uwriteln!(self.src, "{}.tag = {};", result, operands[0]); uwriteln!(self.src, "switch ((int32_t) {}.tag) {{", result); for (i, (case, (block, block_results))) in variant.cases.iter().zip(blocks).enumerate() { uwriteln!(self.src, "case {}: {{", i); self.src.push_str(&block); assert!(block_results.len() == 1); if !self.gen.is_empty_type(iface, &case.ty) { let mut dst = format!("{}.val", result); dst.push_str("."); dst.push_str(&case.name.to_snake_case()); self.store_op(&block_results[0], &dst); } self.src.push_str("break;\n}\n"); } self.src.push_str("}\n"); results.push(result); } Instruction::UnionLower { union, results: result_types, .. } => { let blocks = self .blocks .drain(self.blocks.len() - union.cases.len()..) .collect::>(); let payloads = self .payloads .drain(self.payloads.len() - union.cases.len()..) .collect::>(); let mut union_results = Vec::with_capacity(result_types.len()); for ty in result_types.iter() { let name = self.locals.tmp("unionres"); results.push(name.clone()); let ty = wasm_type(*ty); uwriteln!(self.src, "{ty} {name};"); union_results.push(name); } let op0 = &operands[0]; uwriteln!(self.src, "switch (({op0}).tag) {{"); for (i, ((case, (block, block_results)), payload)) in union.cases.iter().zip(blocks).zip(payloads).enumerate() { uwriteln!(self.src, "case {i}: {{"); if !self.gen.is_empty_type(iface, &case.ty) { let ty = self.gen.type_string(iface, &case.ty); uwriteln!(self.src, "const {ty} *{payload} = &({op0}).val.f{i};"); } self.src.push_str(&block); for (name, result) in union_results.iter().zip(&block_results) { uwriteln!(self.src, "{name} = {result};"); } self.src.push_str("break;\n}\n"); } self.src.push_str("}\n"); } Instruction::UnionLift { union, ty, .. } => { let blocks = self .blocks .drain(self.blocks.len() - union.cases.len()..) .collect::>(); let ty = self.gen.type_string(iface, &Type::Id(*ty)); let result = self.locals.tmp("unionres"); uwriteln!(self.src, "{} {};", ty, result); uwriteln!(self.src, "{}.tag = {};", result, operands[0]); uwriteln!(self.src, "switch ((int32_t) {}.tag) {{", result); for (i, (_case, (block, block_results))) in union.cases.iter().zip(blocks).enumerate() { uwriteln!(self.src, "case {i}: {{"); self.src.push_str(&block); assert!(block_results.len() == 1); let dst = format!("{result}.val.f{i}"); self.store_op(&block_results[0], &dst); self.src.push_str("break;\n}\n"); } self.src.push_str("}\n"); results.push(result); } Instruction::OptionLower { results: result_types, payload, .. } => { let (mut some, some_results) = self.blocks.pop().unwrap(); let (mut none, none_results) = self.blocks.pop().unwrap(); let some_payload = self.payloads.pop().unwrap(); let _none_payload = self.payloads.pop().unwrap(); for (i, ty) in result_types.iter().enumerate() { let name = self.locals.tmp("option"); results.push(name.clone()); self.src.push_str(wasm_type(*ty)); self.src.push_str(" "); self.src.push_str(&name); self.src.push_str(";\n"); let some_result = &some_results[i]; uwriteln!(some, "{name} = {some_result};"); let none_result = &none_results[i]; uwriteln!(none, "{name} = {none_result};"); } let op0 = &operands[0]; let ty = self.gen.type_string(iface, payload); let bind_some = if self.gen.is_empty_type(iface, payload) { String::new() } else { format!("const {ty} *{some_payload} = &({op0}).val;") }; uwrite!( self.src, " if (({op0}).is_some) {{ {bind_some} {some} }} else {{ {none} }} " ); } Instruction::OptionLift { payload, ty, .. } => { let (some, some_results) = self.blocks.pop().unwrap(); let (none, none_results) = self.blocks.pop().unwrap(); assert!(none_results.len() == 1); assert!(some_results.len() == 1); let some_result = &some_results[0]; assert_eq!(none_results[0], "INVALID"); let ty = self.gen.type_string(iface, &Type::Id(*ty)); let result = self.locals.tmp("option"); uwriteln!(self.src, "{ty} {result};"); let op0 = &operands[0]; let set_some = if self.gen.is_empty_type(iface, payload) { String::new() } else { format!("{result}.val = {some_result};") }; uwrite!( self.src, "switch ({op0}) {{ case 0: {{ {result}.is_some = false; {none} break; }} case 1: {{ {result}.is_some = true; {some} {set_some} break; }} }}" ); results.push(result); } Instruction::ExpectedLower { results: result_types, expected, .. } => { let (mut err, err_results) = self.blocks.pop().unwrap(); let (mut ok, ok_results) = self.blocks.pop().unwrap(); let err_payload = self.payloads.pop().unwrap(); let ok_payload = self.payloads.pop().unwrap(); for (i, ty) in result_types.iter().enumerate() { let name = self.locals.tmp("expected"); results.push(name.clone()); self.src.push_str(wasm_type(*ty)); self.src.push_str(" "); self.src.push_str(&name); self.src.push_str(";\n"); let ok_result = &ok_results[i]; uwriteln!(ok, "{name} = {ok_result};"); let err_result = &err_results[i]; uwriteln!(err, "{name} = {err_result};"); } let op0 = &operands[0]; let ok_ty = self.gen.type_string(iface, &expected.ok); let err_ty = self.gen.type_string(iface, &expected.err); let bind_ok = if self.gen.is_empty_type(iface, &expected.ok) { String::new() } else { format!("const {ok_ty} *{ok_payload} = &({op0}).val.ok;") }; let bind_err = if self.gen.is_empty_type(iface, &expected.err) { String::new() } else { format!("const {err_ty} *{err_payload} = &({op0}).val.err;") }; uwrite!( self.src, " if (({op0}).is_err) {{ {bind_err} {err} }} else {{ {bind_ok} {ok} }} " ); } Instruction::ExpectedLift { expected, ty, .. } => { let (err, err_results) = self.blocks.pop().unwrap(); assert!(err_results.len() == 1); let err_result = &err_results[0]; let (ok, ok_results) = self.blocks.pop().unwrap(); assert!(ok_results.len() == 1); let ok_result = &ok_results[0]; let result = self.locals.tmp("expected"); let set_ok = if self.gen.is_empty_type(iface, &expected.ok) { String::new() } else { format!("{result}.val.ok = {ok_result};") }; let set_err = if self.gen.is_empty_type(iface, &expected.err) { String::new() } else { format!("{result}.val.err = {err_result};") }; let ty = self.gen.type_string(iface, &Type::Id(*ty)); uwriteln!(self.src, "{ty} {result};"); let op0 = &operands[0]; uwrite!( self.src, "switch ({op0}) {{ case 0: {{ {result}.is_err = false; {ok} {set_ok} break; }} case 1: {{ {result}.is_err = true; {err} {set_err} break; }} }}" ); results.push(result); } Instruction::EnumLower { .. } => results.push(format!("(int32_t) {}", operands[0])), Instruction::EnumLift { .. } => results.push(operands.pop().unwrap()), Instruction::ListCanonLower { .. } | Instruction::StringLower { .. } => { results.push(format!("(int32_t) ({}).ptr", operands[0])); results.push(format!("(int32_t) ({}).len", operands[0])); } Instruction::ListCanonLift { element, ty, .. } => { let list_name = self.gen.type_string(iface, &Type::Id(*ty)); let elem_name = self.gen.type_string(iface, element); results.push(format!( "({}) {{ ({}*)({}), (size_t)({}) }}", list_name, elem_name, operands[0], operands[1] )); } Instruction::StringLift { .. } => { let list_name = self.gen.type_string(iface, &Type::String); results.push(format!( "({}) {{ (char*)({}), (size_t)({}) }}", list_name, operands[0], operands[1] )); } Instruction::ListLower { .. } => { let _body = self.blocks.pop().unwrap(); results.push(format!("(int32_t) ({}).ptr", operands[0])); results.push(format!("(int32_t) ({}).len", operands[0])); } Instruction::ListLift { element, ty, .. } => { let _body = self.blocks.pop().unwrap(); let list_name = self.gen.type_string(iface, &Type::Id(*ty)); let elem_name = self.gen.type_string(iface, element); results.push(format!( "({}) {{ ({}*)({}), (size_t)({}) }}", list_name, elem_name, operands[0], operands[1] )); } Instruction::IterElem { .. } => results.push("e".to_string()), Instruction::IterBasePointer => results.push("base".to_string()), Instruction::CallWasm { sig, .. } => { match sig.results.len() { 0 => {} 1 => { self.src.push_str(wasm_type(sig.results[0])); let ret = self.locals.tmp("ret"); self.wasm_return = Some(ret.clone()); uwrite!(self.src, " {} = ", ret); results.push(ret); } _ => unimplemented!(), } self.src.push_str(self.func_to_call); self.src.push_str("("); for (i, op) in operands.iter().enumerate() { if i > 0 { self.src.push_str(", "); } self.src.push_str(op); } self.src.push_str(");\n"); } Instruction::CallInterface { module: _, func } => { let mut args = String::new(); for (i, (op, (byref, _))) in operands.iter().zip(&self.sig.params).enumerate() { if i > 0 { args.push_str(", "); } if *byref { let name = self.locals.tmp("arg"); let ty = self.gen.type_string(iface, &func.params[i].1); uwriteln!(self.src, "{} {} = {};", ty, name, op); args.push_str("&"); args.push_str(&name); } else { args.push_str(op); } } match &self.sig.ret.scalar { None => { let mut retptrs = Vec::new(); for ty in self.sig.ret.retptrs.iter() { let name = self.locals.tmp("ret"); let ty = self.gen.type_string(iface, ty); uwriteln!(self.src, "{} {};", ty, name); if args.len() > 0 { args.push_str(", "); } args.push_str("&"); args.push_str(&name); retptrs.push(name); } uwriteln!(self.src, "{}({});", self.sig.name, args); if self.sig.ret.splat_tuple { let ty = self.gen.type_string(iface, &func.result); results.push(format!("({}){{ {} }}", ty, retptrs.join(", "))); } else if self.sig.retptrs.len() > 0 { results.extend(retptrs); } } Some(Scalar::Void) => { uwriteln!(self.src, "{}({});", self.sig.name, args); results.push("INVALID".to_string()); } Some(Scalar::Type(_)) => { let ret = self.locals.tmp("ret"); let ty = self.gen.type_string(iface, &func.result); uwriteln!(self.src, "{} {} = {}({});", ty, ret, self.sig.name, args); results.push(ret); } Some(Scalar::OptionBool(ty)) => { let ret = self.locals.tmp("ret"); let val = self.locals.tmp("val"); if args.len() > 0 { args.push_str(", "); } args.push_str("&"); args.push_str(&val); let payload_ty = self.gen.type_string(iface, ty); uwriteln!(self.src, "{} {};", payload_ty, val); uwriteln!(self.src, "bool {} = {}({});", ret, self.sig.name, args); let option_ty = self.gen.type_string(iface, &func.result); let option_ret = self.locals.tmp("ret"); uwrite!( self.src, " {ty} {ret}; {ret}.is_some = {tag}; {ret}.val = {val}; ", ty = option_ty, ret = option_ret, tag = ret, val = val, ); results.push(option_ret); } Some(Scalar::ExpectedEnum { err, max_err }) => { let ret = self.locals.tmp("ret"); let mut ok_names = Vec::new(); for ty in self.sig.ret.retptrs.iter() { let val = self.locals.tmp("ok"); if args.len() > 0 { args.push_str(", "); } args.push_str("&"); args.push_str(&val); let ty = self.gen.type_string(iface, ty); uwriteln!(self.src, "{} {};", ty, val); ok_names.push(val); } let err_ty = self.gen.type_string(iface, &Type::Id(*err)); uwriteln!( self.src, "{} {} = {}({});", err_ty, ret, self.sig.name, args, ); let expected_ty = self.gen.type_string(iface, &func.result); let expected_ret = self.locals.tmp("ret"); uwrite!( self.src, " {ty} {ret}; if ({tag} <= {max}) {{ {ret}.is_err = true; {ret}.val.err = {tag}; }} else {{ {ret}.is_err = false; {set_ok} }} ", ty = expected_ty, ret = expected_ret, tag = ret, max = max_err, set_ok = if self.sig.ret.retptrs.len() == 0 { String::new() } else if self.sig.ret.splat_tuple { let mut s = String::new(); for (i, name) in ok_names.iter().enumerate() { uwriteln!(s, "{}.val.ok.f{} = {};", expected_ret, i, name,); } s } else { let name = ok_names.pop().unwrap(); format!("{}.val.ok = {};", expected_ret, name) }, ); results.push(expected_ret); } } } Instruction::Return { .. } if self.gen.in_import => match self.sig.ret.scalar { None => self.store_in_retptrs(operands), Some(Scalar::Void) => { assert_eq!(operands, &["INVALID"]); } Some(Scalar::Type(_)) => { assert_eq!(operands.len(), 1); self.src.push_str("return "); self.src.push_str(&operands[0]); self.src.push_str(";\n"); } Some(Scalar::OptionBool(_)) => { assert_eq!(operands.len(), 1); let variant = &operands[0]; self.store_in_retptrs(&[format!("{}.val", variant)]); self.src.push_str("return "); self.src.push_str(&variant); self.src.push_str(".is_some;\n"); } Some(Scalar::ExpectedEnum { .. }) => { assert_eq!(operands.len(), 1); let variant = &operands[0]; if self.sig.retptrs.len() > 0 { self.store_in_retptrs(&[format!("{}.val.ok", variant)]); } uwriteln!(self.src, "return {}.is_err ? {0}.val.err : -1;", variant); } }, Instruction::Return { amt, .. } => { assert!(*amt <= 1); if *amt == 1 { uwriteln!(self.src, "return {};", operands[0]); } } Instruction::I32Load { offset } => self.load("int32_t", *offset, operands, results), Instruction::I64Load { offset } => self.load("int64_t", *offset, operands, results), Instruction::F32Load { offset } => self.load("float", *offset, operands, results), Instruction::F64Load { offset } => self.load("double", *offset, operands, results), Instruction::I32Store { offset } => self.store("int32_t", *offset, operands), Instruction::I64Store { offset } => self.store("int64_t", *offset, operands), Instruction::F32Store { offset } => self.store("float", *offset, operands), Instruction::F64Store { offset } => self.store("double", *offset, operands), Instruction::I32Store8 { offset } => self.store("int8_t", *offset, operands), Instruction::I32Store16 { offset } => self.store("int16_t", *offset, operands), Instruction::I32Load8U { offset } => { self.load_ext("uint8_t", *offset, operands, results) } Instruction::I32Load8S { offset } => { self.load_ext("int8_t", *offset, operands, results) } Instruction::I32Load16U { offset } => { self.load_ext("uint16_t", *offset, operands, results) } Instruction::I32Load16S { offset } => { self.load_ext("int16_t", *offset, operands, results) } Instruction::Free { .. } => { uwriteln!(self.src, "free((void*) ({}));", operands[0]); } i => unimplemented!("{:?}", i), } } } #[derive(Default)] struct Source { h: wit_bindgen_gen_core::Source, c: wit_bindgen_gen_core::Source, } impl Source { fn c(&mut self, s: &str) { self.c.push_str(s); } fn h(&mut self, s: &str) { self.h.push_str(s); } } fn wasm_type(ty: WasmType) -> &'static str { match ty { WasmType::I32 => "int32_t", WasmType::I64 => "int64_t", WasmType::F32 => "float", WasmType::F64 => "double", } } fn int_repr(ty: Int) -> &'static str { match ty { Int::U8 => "uint8_t", Int::U16 => "uint16_t", Int::U32 => "uint32_t", Int::U64 => "uint64_t", } } fn flags_repr(f: &Flags) -> Int { match f.repr() { FlagsRepr::U8 => Int::U8, FlagsRepr::U16 => Int::U16, FlagsRepr::U32(1) => Int::U32, FlagsRepr::U32(2) => Int::U64, repr => panic!("unimplemented flags {:?}", repr), } }