diff --git a/.gitignore b/.gitignore index 8cdf73b9e4f..319ffaa37af 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ yarn.lock /publish.exe .vscode webdriver.json +crates/foo \ No newline at end of file diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 4671bf4551a..26f52b5cacb 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -374,6 +374,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 348d10295ff..e936abc242d 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1202,35 +1202,98 @@ 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 wasm_bindgen::convert::FromWasmAbi>::Abi as wasm_bindgen::convert::VectorEncoding>::pop_from_u32_vector(&mut vector)) + } + }); + + let variant_field_names = if let syn::Fields::Unit = fields { + quote! {} + } else { + quote! {(#(#variant_fields_into_array),*)} + }; + quote! { - if js == #enum_name::#variant_name as u32 { - #enum_name::#variant_name + if vector.pop().unwrap() == #variant_value { + #enum_name::#variant_name #variant_field_names } } }); + + 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(); + + let variant_field_names = if let syn::Fields::Unit = fields { + quote! {} + } else { + quote! {(#(#field_vector_into_tuple),*)} + }; + + quote! { + #enum_name::#variant_name #variant_field_names + => { + #[allow(unused_mut)] + let mut vector = vec![(#variant_value).into_abi()]; + #[allow(unused_imports)] + use wasm_bindgen::convert::VectorEncoding; + #((#field_names).into_abi().push_into_u32_vector(&mut vector));*; + vector + } + } + }); (quote! { #[automatically_derived] impl wasm_bindgen::convert::IntoWasmAbi for #enum_name { - type Abi = u32; + type Abi = wasm_bindgen::convert::WasmSlice; - #[inline] - fn into_abi(self) -> u32 { - self as u32 + fn into_abi(self) -> wasm_bindgen::convert::WasmSlice { + let vector = match self { + #(#into_clauses),* + }; + + let ptr = vector.as_ptr(); + let len = vector.len(); + core::mem::forget(vector); + wasm_bindgen::convert::WasmSlice { + ptr: ptr.into_abi(), + len: len as u32, + } } } #[automatically_derived] impl wasm_bindgen::convert::FromWasmAbi for #enum_name { - type Abi = u32; + type Abi = wasm_bindgen::convert::WasmSlice; #[inline] - unsafe fn from_abi(js: u32) -> Self { + unsafe fn from_abi(js: wasm_bindgen::convert::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); + #(#cast_clauses else)* { - wasm_bindgen::throw_str("invalid enum value passed") + wasm_bindgen::throw_str(format!("{:?}", vector.len()).as_str()) } } } @@ -1238,13 +1301,15 @@ impl ToTokens for ast::Enum { #[automatically_derived] impl wasm_bindgen::convert::OptionFromWasmAbi for #enum_name { #[inline] - fn is_none(val: &u32) -> bool { *val == #hole } + fn is_none(val: &wasm_bindgen::convert::WasmSlice) -> bool { + val.ptr == (#hole as u32) + } } #[automatically_derived] impl wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name { #[inline] - fn none() -> Self::Abi { #hole } + fn none() -> Self::Abi { wasm_bindgen::convert::WasmSlice { ptr: 0, len: 0 } } } #[automatically_derived] @@ -1252,7 +1317,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 a3b73a2221a..214d72048f1 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -65,7 +65,7 @@ pub enum Descriptor { String, Externref, NamedExternref(String), - Enum { hole: u32 }, + Enum, RustStruct(String), Char, Option(Box), @@ -107,6 +107,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); @@ -139,7 +145,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) @@ -216,6 +222,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 c29e808587e..30cf31e14be 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -1353,5 +1353,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 1778516caad..eb5285c54df 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)?, @@ -281,11 +291,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 e2598f7e920..55746a257ce 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -71,7 +71,6 @@ impl InstructionBuilder<'_, '_> { self.get(AdapterType::F64); self.output.push(AdapterType::F64); } - Descriptor::Enum { .. } => self.outgoing_i32(AdapterType::U32), Descriptor::Char => { self.instruction( @@ -167,6 +166,18 @@ 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 => {} @@ -304,13 +315,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 78df620d765..a1b99f84b49 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -87,6 +87,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)] @@ -372,7 +379,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 ac0e835dfda..4b183ba47b2 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1211,11 +1211,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. @@ -1255,6 +1250,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..3891d61a1f5 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::{VectorEncoding, WasmSlice}; pub use self::traits::*; diff --git a/src/convert/slices.rs b/src/convert/slices.rs index 9d0970f4e6a..5fbd0fcb118 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,73 @@ 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 } + } +}