diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 807c37d908e..4af237f91bf 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -369,6 +369,8 @@ pub struct Enum { pub struct Variant { /// The name of this variant pub name: Ident, + /// The fields of this variant + pub fields: syn::Fields, /// The backing value of this variant pub value: u32, /// The doc comments on this variant, if any diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 57228711749..ee6264c10a5 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1161,35 +1161,85 @@ impl<'a> ToTokens for DescribeImport<'a> { impl ToTokens for ast::Enum { fn to_tokens(&self, into: &mut TokenStream) { + use quote::format_ident; + let enum_name = &self.rust_name; let hole = &self.hole; let cast_clauses = self.variants.iter().map(|variant| { let variant_name = &variant.name; + let variant_value = &variant.value; + + let fields = &variant.fields.clone(); + let variant_fields_into_array = fields.iter().map(|field| { + quote! { + #field::from_abi(<#field as FromWasmAbi>::Abi::pop_from_u32_vector(&mut vector)) + } + }); + quote! { - if js == #enum_name::#variant_name as u32 { - #enum_name::#variant_name + if vector.pop().unwrap() == #variant_value { + #enum_name::#variant_name(#(#variant_fields_into_array),*) } } }); + let into_clauses = self.variants.iter().map(|variant| { + let variant_name = &variant.name; + let variant_value = &variant.value; + + let fields = &variant.fields.clone(); + let field_vector_into_tuple = fields.iter().enumerate().map(|(index, _)| { + let varname = format_ident!("arg{}", index); + quote! { + #varname + } + }); + + let field_names = field_vector_into_tuple.clone(); + + quote! { + #enum_name::#variant_name(#(#field_vector_into_tuple),*) + => { + let mut vector = vec![(#variant_value).into_abi()]; + #((#field_names).into_abi().push_into_u32_vector(&mut vector));*; + vector + } + } + }); (quote! { + use wasm_bindgen::convert::{WasmAbi, WasmSlice, FromWasmAbi, VectorEncoding}; + #[allow(clippy::all)] impl wasm_bindgen::convert::IntoWasmAbi for #enum_name { - type Abi = u32; + type Abi = WasmSlice; - #[inline] - fn into_abi(self) -> u32 { - self as u32 + fn into_abi(self) -> WasmSlice { + let vector = match self { + #(#into_clauses),* + }; + + let ptr = vector.as_ptr(); + let len = vector.len(); + core::mem::forget(vector); + WasmSlice { + ptr: ptr.into_abi(), + len: len as u32, + } } } #[allow(clippy::all)] impl wasm_bindgen::convert::FromWasmAbi for #enum_name { - type Abi = u32; + type Abi = WasmSlice; #[inline] - unsafe fn from_abi(js: u32) -> Self { + unsafe fn from_abi(js: WasmSlice) -> Self { + let ptr = <*mut u32>::from_abi(js.ptr); + let len = js.len as usize; + let mut vector = Vec::from_raw_parts(ptr, len, len); + let err = format!("{:?}", vector.len()).as_str(); + #(#cast_clauses else)* { - wasm_bindgen::throw_str("invalid enum value passed") + wasm_bindgen::throw_str(err,) } } } @@ -1197,13 +1247,15 @@ impl ToTokens for ast::Enum { #[allow(clippy::all)] impl wasm_bindgen::convert::OptionFromWasmAbi for #enum_name { #[inline] - fn is_none(val: &u32) -> bool { *val == #hole } + fn is_none(val: &WasmSlice) -> bool { + val.ptr == (#hole as u32) + } } #[allow(clippy::all)] impl wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name { #[inline] - fn none() -> Self::Abi { #hole } + fn none() -> Self::Abi { WasmSlice { ptr: 0, len: 0 } } } #[allow(clippy::all)] @@ -1211,7 +1263,6 @@ impl ToTokens for ast::Enum { fn describe() { use wasm_bindgen::describe::*; inform(ENUM); - inform(#hole); } } }) diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index 5950242376a..ad793c0147a 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -64,7 +64,7 @@ pub enum Descriptor { String, Externref, NamedExternref(String), - Enum { hole: u32 }, + Enum, RustStruct(String), Char, Option(Box), @@ -104,6 +104,12 @@ pub enum VectorKind { NamedExternref(String), } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct EnumVariant { + pub name: String, + pub fields: Vec +} + impl Descriptor { pub fn decode(mut data: &[u32]) -> Descriptor { let descriptor = Descriptor::_decode(&mut data, false); @@ -135,7 +141,7 @@ impl Descriptor { CACHED_STRING => Descriptor::CachedString, STRING => Descriptor::String, EXTERNREF => Descriptor::Externref, - ENUM => Descriptor::Enum { hole: get(data) }, + ENUM => Descriptor::Enum, RUST_STRUCT => { let name = get_string(data); Descriptor::RustStruct(name) @@ -212,6 +218,26 @@ fn get_string(data: &mut &[u32]) -> String { .collect() } +// fn get_enum(data: &mut &[u32]) -> Descriptor { +// let name = get_string(data); +// let hole = get(data); +// let variants = (0..get(data)).map(|_| { + +// let variant_name = get_string(data); +// let variant_fields = (0..get(data)).map(|_| { +// Descriptor::_decode(data, false) +// }).collect(); + +// EnumVariant { +// name: variant_name, +// fields: variant_fields +// } + +// }).collect(); + +// Descriptor::Enum {name, variants, hole} +// } + impl Closure { fn decode(data: &mut &[u32]) -> Closure { let shim_idx = get(data); diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index ea79adf9279..1ded13b70d1 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -1234,5 +1234,6 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) { AdapterType::NamedExternref(name) => dst.push_str(name), AdapterType::Struct(name) => dst.push_str(name), AdapterType::Function => dst.push_str("any"), + AdapterType::Enum => dst.push_str("placeholder") } } diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index a911535f485..161432c6520 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -99,7 +99,17 @@ impl InstructionBuilder<'_, '_> { self.get(AdapterType::F64); self.output.push(AdapterType::F64); } - Descriptor::Enum { .. } => self.number(WitVT::U32, WasmVT::I32), + Descriptor::Enum { .. } => { + let kind = crate::descriptor::VectorKind::U32; + + let malloc = self.cx.malloc()?; + let mem = self.cx.memory()?; + self.instruction( + &[AdapterType::Enum.option()], + Instruction::OptionVector { kind, malloc, mem }, + &[AdapterType::I32, AdapterType::I32], + ); + } Descriptor::Ref(d) => self.incoming_ref(false, d)?, Descriptor::RefMut(d) => self.incoming_ref(true, d)?, Descriptor::Option(d) => self.incoming_option(d)?, @@ -280,11 +290,15 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); } - Descriptor::Enum { hole } => { + Descriptor::Enum { .. } => { + let kind = crate::descriptor::VectorKind::U32; + + let malloc = self.cx.malloc()?; + let mem = self.cx.memory()?; self.instruction( - &[AdapterType::U32.option()], - Instruction::I32FromOptionEnum { hole: *hole }, - &[AdapterType::I32], + &[AdapterType::Enum.option()], + Instruction::OptionVector { kind, malloc, mem }, + &[AdapterType::I32, AdapterType::I32], ); } Descriptor::RustStruct(name) => { diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index bba1fe81d8c..5ec9992e9c9 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -60,7 +60,6 @@ impl InstructionBuilder<'_, '_> { self.get(AdapterType::F64); self.output.push(AdapterType::F64); } - Descriptor::Enum { .. } => self.outgoing_i32(AdapterType::U32), Descriptor::Char => { self.instruction( @@ -155,6 +154,22 @@ impl InstructionBuilder<'_, '_> { arg ), + Descriptor::Enum { .. } => { + let kind = crate::descriptor::VectorKind::U32; + + let mem = self.cx.memory()?; + let free = self.cx.free()?; + self.instruction( + &[AdapterType::I32, AdapterType::I32], + Instruction::VectorLoad { + kind, + mem, + free, + }, + &[AdapterType::Enum], + ); + } + // nothing to do Descriptor::Unit => {} @@ -288,13 +303,13 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::String.option()], ); } - Descriptor::Enum { hole } => { - self.instruction( - &[AdapterType::I32], - Instruction::OptionEnumFromI32 { hole: *hole }, - &[AdapterType::U32.option()], - ); - } + // Descriptor::Enum => { + // self.instruction( + // &[AdapterType::I32], + // Instruction::OptionEnumFromI32 { hole: *hole }, + // &[AdapterType::U32.option()], + // ); + // } Descriptor::RustStruct(name) => { self.instruction( &[AdapterType::I32], diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index d9c6807620d..f21e2b65f1e 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -86,6 +86,13 @@ pub enum AdapterType { Struct(String), NamedExternref(String), Function, + Enum, +} + +#[derive(Debug, Clone)] +pub struct EnumVariantAdapter { + pub name: String, + pub fields: Vec } #[derive(Debug, Clone)] @@ -354,7 +361,8 @@ impl AdapterType { | AdapterType::Function | AdapterType::Struct(_) | AdapterType::Bool - | AdapterType::Vector(_) => return None, + | AdapterType::Vector(_) + | AdapterType::Enum => return None, }) } diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index eb14712d6e9..beb31290188 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1159,11 +1159,6 @@ impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum { .iter() .enumerate() .map(|(i, v)| { - match v.fields { - syn::Fields::Unit => (), - _ => bail_span!(v.fields, "only C-Style enums allowed with #[wasm_bindgen]"), - } - // Require that everything either has a discriminant or doesn't. // We don't really want to get in the business of emulating how // rustc assigns values to enums. @@ -1203,6 +1198,7 @@ impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum { name: v.ident.clone(), value, comments, + fields: v.fields.clone(), }) }) .collect::, Diagnostic>>()?; diff --git a/src/convert/mod.rs b/src/convert/mod.rs index ce2c0b2c847..6685f7b77ee 100644 --- a/src/convert/mod.rs +++ b/src/convert/mod.rs @@ -7,5 +7,5 @@ mod slices; mod traits; pub use self::impls::*; -pub use self::slices::WasmSlice; +pub use self::slices::{WasmSlice, VectorEncoding}; pub use self::traits::*; diff --git a/src/convert/slices.rs b/src/convert/slices.rs index 9d0970f4e6a..7b092881ff6 100644 --- a/src/convert/slices.rs +++ b/src/convert/slices.rs @@ -23,7 +23,7 @@ pub struct WasmSlice { unsafe impl WasmAbi for WasmSlice {} #[inline] -fn null_slice() -> WasmSlice { +pub fn null_slice() -> WasmSlice { WasmSlice { ptr: 0, len: 0 } } @@ -309,3 +309,76 @@ if_std! { fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 } } } + +pub trait VectorEncoding { + fn push_into_u32_vector(self, vector: &mut Vec); + + fn pop_from_u32_vector(vector: &mut Vec) -> Self; +} + +impl VectorEncoding for u32 { + fn push_into_u32_vector(self, vector: &mut Vec) { + vector.push(self) + } + + fn pop_from_u32_vector(vector: &mut Vec) -> Self { + vector.pop().unwrap() + } +} + +impl VectorEncoding for () { + fn push_into_u32_vector(self, vector: &mut Vec) { + vector.push(0) + } + + fn pop_from_u32_vector(vector: &mut Vec) -> Self { + vector.pop().unwrap(); + () + } +} + +impl VectorEncoding for f32 { + fn push_into_u32_vector(self, vector: &mut Vec) { + let as_u32 = self.to_bits(); + vector.push(as_u32); + } + + fn pop_from_u32_vector(vector: &mut Vec) -> Self { + f32::from_bits(vector.pop().unwrap()) + } +} + +impl VectorEncoding for f64 { + fn push_into_u32_vector(self, vector: &mut Vec) { + let as_u64 = self.to_bits(); + let hi = (as_u64 >> 32) as u32; + let lo = as_u64 as u32; + vector.push(hi); + vector.push(lo); + } + + fn pop_from_u32_vector(vector: &mut Vec) -> Self { + let hi = vector.pop().unwrap(); + let lo = vector.pop().unwrap(); + let as_u64 = (hi as u64) << 32 | (lo as u64); + f64::from_bits(as_u64) + } +} + +impl VectorEncoding for WasmSlice { + fn push_into_u32_vector(self, vector: &mut Vec) { + let WasmSlice { ptr, len } = self; + + vector.push(ptr); + vector.push(len); + } + + fn pop_from_u32_vector(vector: &mut Vec) -> Self { + let ptr = vector.pop().unwrap(); + let len = vector.pop().unwrap(); + WasmSlice { + ptr, + len + } + } +}