Files
simple-rust-tests/__wasm/wit-bindgen-sample/wit-bindgen/crates/gen-c/src/lib.rs

2261 lines
83 KiB
Rust

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<String, Vec<Func>>,
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<TypeId>,
// 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<TypeId>,
// Type definitions for the given `TypeId`. This is printed topologically
// at the end.
types: HashMap<TypeId, wit_bindgen_gen_core::Source>,
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<Scalar>,
retptrs: Vec<Type>,
}
struct CSig {
name: String,
sig: String,
params: Vec<(bool, String)>,
ret: Return,
retptrs: Vec<String>,
}
#[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<T, E>` 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 <stdint.h>
#include <stdbool.h>
",
iface.name.to_shouty_snake_case(),
);
uwrite!(
self.src.c,
"\
#include <stdlib.h>
#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 <string.h>\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<wit_bindgen_gen_core::Source>,
blocks: Vec<(String, Vec<String>)>,
payloads: Vec<String>,
params: Vec<String>,
wasm_return: Option<String>,
}
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<String>) {
results.push(format!("*(({}*) ({} + {}))", ty, operands[0], offset));
}
fn load_ext(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec<String>) {
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<String>) {
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<String>,
results: &mut Vec<String>,
) {
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::<Vec<_>>();
let payloads = self
.payloads
.drain(self.payloads.len() - variant.cases.len()..)
.collect::<Vec<_>>();
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::<Vec<_>>();
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::<Vec<_>>();
let payloads = self
.payloads
.drain(self.payloads.len() - union.cases.len()..)
.collect::<Vec<_>>();
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::<Vec<_>>();
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),
}
}