diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 86a584becc85..dc15cb6d2ea3 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -365,6 +365,12 @@ impl ToTokens for ast::Struct { #wasm_bindgen::convert::js_value_vector_from_abi(js) } } + + impl #wasm_bindgen::__rt::VectorIntoJsValue for #name { + fn vector_into_jsvalue(vector: #wasm_bindgen::__rt::std::boxed::Box<[#name]>) -> #wasm_bindgen::JsValue { + #wasm_bindgen::__rt::js_value_vector_into_jsvalue(vector) + } + } }) .to_tokens(tokens); @@ -1495,6 +1501,12 @@ impl ToTokens for ast::Enum { #wasm_bindgen::convert::js_value_vector_from_abi(js) } } + + impl #wasm_bindgen::__rt::VectorIntoJsValue for #enum_name { + fn vector_into_jsvalue(vector: #wasm_bindgen::__rt::std::boxed::Box<[#enum_name]>) -> #wasm_bindgen::JsValue { + #wasm_bindgen::__rt::js_value_vector_into_jsvalue(vector) + } + } }) .to_tokens(into); } diff --git a/crates/cli-support/src/intrinsic.rs b/crates/cli-support/src/intrinsic.rs index ea209a47fa4e..44a5079f4ed3 100644 --- a/crates/cli-support/src/intrinsic.rs +++ b/crates/cli-support/src/intrinsic.rs @@ -264,6 +264,45 @@ intrinsics! { #[symbol = "__wbindgen_copy_to_typed_array"] #[signature = fn(slice(U8), ref_externref()) -> Unit] CopyToTypedArray, + #[symbol = "__wbindgen_uint8_array_new"] + #[signature = fn(slice(U8)) -> Externref] + Uint8ArrayNew, + #[symbol = "__wbindgen_uint8_clamped_array_new"] + #[signature = fn(slice(ClampedU8)) -> Externref] + Uint8ClampedArrayNew, + #[symbol = "__wbindgen_uint16_array_new"] + #[signature = fn(slice(U16)) -> Externref] + Uint16ArrayNew, + #[symbol = "__wbindgen_uint32_array_new"] + #[signature = fn(slice(U32)) -> Externref] + Uint32ArrayNew, + #[symbol = "__wbindgen_biguint64_array_new"] + #[signature = fn(slice(U64)) -> Externref] + BigUint64ArrayNew, + #[symbol = "__wbindgen_int8_array_new"] + #[signature = fn(slice(I8)) -> Externref] + Int8ArrayNew, + #[symbol = "__wbindgen_int16_array_new"] + #[signature = fn(slice(I16)) -> Externref] + Int16ArrayNew, + #[symbol = "__wbindgen_int32_array_new"] + #[signature = fn(slice(I32)) -> Externref] + Int32ArrayNew, + #[symbol = "__wbindgen_bigint64_array_new"] + #[signature = fn(slice(I64)) -> Externref] + BigInt64ArrayNew, + #[symbol = "__wbindgen_float32_array_new"] + #[signature = fn(slice(F32)) -> Externref] + Float32ArrayNew, + #[symbol = "__wbindgen_float64_array_new"] + #[signature = fn(slice(F64)) -> Externref] + Float64ArrayNew, + #[symbol = "__wbindgen_array_new"] + #[signature = fn() -> Externref] + ArrayNew, + #[symbol = "__wbindgen_array_push"] + #[signature = fn(ref_externref(), Externref) -> Unit] + ArrayPush, #[symbol = "__wbindgen_externref_heap_live_count"] #[signature = fn() -> I32] ExternrefHeapLiveCount, diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index f81d981398b5..422cfab3992f 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -3607,6 +3607,31 @@ impl<'a> Context<'a> { ) } + Intrinsic::Uint8ArrayNew + | Intrinsic::Uint8ClampedArrayNew + | Intrinsic::Uint16ArrayNew + | Intrinsic::Uint32ArrayNew + | Intrinsic::BigUint64ArrayNew + | Intrinsic::Int8ArrayNew + | Intrinsic::Int16ArrayNew + | Intrinsic::Int32ArrayNew + | Intrinsic::BigInt64ArrayNew + | Intrinsic::Float32ArrayNew + | Intrinsic::Float64ArrayNew => { + assert_eq!(args.len(), 1); + args[0].clone() + } + + Intrinsic::ArrayNew => { + assert_eq!(args.len(), 0); + "[]".to_string() + } + + Intrinsic::ArrayPush => { + assert_eq!(args.len(), 2); + format!("{}.push({})", args[0], args[1]) + } + Intrinsic::ExternrefHeapLiveCount => { assert_eq!(args.len(), 0); self.expose_global_heap(); diff --git a/src/lib.rs b/src/lib.rs index f188e023ddff..d2dd2f2b226f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1079,6 +1079,21 @@ externs! { fn __wbindgen_copy_to_typed_array(ptr: *const u8, len: usize, idx: u32) -> (); + fn __wbindgen_uint8_array_new(ptr: *mut u8, len: usize) -> u32; + fn __wbindgen_uint8_clamped_array_new(ptr: *mut u8, len: usize) -> u32; + fn __wbindgen_uint16_array_new(ptr: *mut u16, len: usize) -> u32; + fn __wbindgen_uint32_array_new(ptr: *mut u32, len: usize) -> u32; + fn __wbindgen_biguint64_array_new(ptr: *mut u64, len: usize) -> u32; + fn __wbindgen_int8_array_new(ptr: *mut i8, len: usize) -> u32; + fn __wbindgen_int16_array_new(ptr: *mut i16, len: usize) -> u32; + fn __wbindgen_int32_array_new(ptr: *mut i32, len: usize) -> u32; + fn __wbindgen_bigint64_array_new(ptr: *mut i64, len: usize) -> u32; + fn __wbindgen_float32_array_new(ptr: *mut f32, len: usize) -> u32; + fn __wbindgen_float64_array_new(ptr: *mut f64, len: usize) -> u32; + + fn __wbindgen_array_new() -> u32; + fn __wbindgen_array_push(array: u32, value: u32) -> (); + fn __wbindgen_not(idx: u32) -> u32; fn __wbindgen_exports() -> u32; @@ -1787,6 +1802,35 @@ pub mod __rt { } } } + + if_std! { + use core::mem; + use std::boxed::Box; + + /// Trait for element types to implement `Into` for vectors of + /// themselves, which isn't possible directly thanks to the orphan rule. + pub trait VectorIntoJsValue: Sized { + fn vector_into_jsvalue(vector: Box<[Self]>) -> JsValue; + } + + impl From> for JsValue { + fn from(vector: Box<[T]>) -> Self { + T::vector_into_jsvalue(vector) + } + } + + pub fn js_value_vector_into_jsvalue>(vector: Box<[T]>) -> JsValue { + let result = unsafe { JsValue::_new(super::__wbindgen_array_new()) }; + for value in vector.into_vec() { + let js: JsValue = value.into(); + unsafe { super::__wbindgen_array_push(result.idx, js.idx) } + // `__wbindgen_array_push` takes ownership over `js` and has already dropped it, + // so don't drop it again. + mem::forget(js); + } + result + } + } } /// A wrapper type around slices and vectors for binding the `Uint8ClampedArray` @@ -1905,3 +1949,65 @@ impl From for JsValue { error.value } } + +macro_rules! typed_arrays { + ($($ty:ident $ctor:ident $clamped_ctor:ident,)*) => { + $( + impl From> for JsValue { + fn from(mut vec: Vec<$ty>) -> Self { + let result = unsafe { JsValue::_new($ctor(vec.as_mut_ptr(), vec.len())) }; + mem::forget(vec); + result + } + } + + impl From>> for JsValue { + fn from(mut vec: Clamped>) -> Self { + let result = unsafe { JsValue::_new($clamped_ctor(vec.as_mut_ptr(), vec.len())) }; + mem::forget(vec); + result + } + } + )* + }; +} + +typed_arrays! { + u8 __wbindgen_uint8_array_new __wbindgen_uint8_clamped_array_new, + u16 __wbindgen_uint16_array_new __wbindgen_uint16_array_new, + u32 __wbindgen_uint32_array_new __wbindgen_uint32_array_new, + u64 __wbindgen_biguint64_array_new __wbindgen_biguint64_array_new, + i8 __wbindgen_int8_array_new __wbindgen_int8_array_new, + i16 __wbindgen_int16_array_new __wbindgen_int16_array_new, + i32 __wbindgen_int32_array_new __wbindgen_int32_array_new, + i64 __wbindgen_bigint64_array_new __wbindgen_bigint64_array_new, + f32 __wbindgen_float32_array_new __wbindgen_float32_array_new, + f64 __wbindgen_float64_array_new __wbindgen_float64_array_new, +} + +impl __rt::VectorIntoJsValue for JsValue { + fn vector_into_jsvalue(vector: Box<[JsValue]>) -> JsValue { + __rt::js_value_vector_into_jsvalue::(vector) + } +} + +impl __rt::VectorIntoJsValue for T { + fn vector_into_jsvalue(vector: Box<[T]>) -> JsValue { + __rt::js_value_vector_into_jsvalue::(vector) + } +} + +impl __rt::VectorIntoJsValue for String { + fn vector_into_jsvalue(vector: Box<[String]>) -> JsValue { + __rt::js_value_vector_into_jsvalue::(vector) + } +} + +impl From> for JsValue +where + JsValue: From>, +{ + fn from(vector: Vec) -> Self { + JsValue::from(vector.into_boxed_slice()) + } +} diff --git a/tests/wasm/async_vecs.js b/tests/wasm/async_vecs.js new file mode 100644 index 000000000000..5e10a24edc07 --- /dev/null +++ b/tests/wasm/async_vecs.js @@ -0,0 +1,11 @@ +const wasm = require('wasm-bindgen-test.js'); +const assert = require('assert'); + +exports.js_works = async () => { + assert.deepStrictEqual(await wasm.async_number_vec(), new Int32Array([1, -3, 7, 12])); + assert.deepStrictEqual(await wasm.async_jsvalue_vec(), [1, "hi", new Float64Array(), null]); + assert.deepStrictEqual(await wasm.async_import_vec(), [/hi|bye/, /hello w[a-z]rld/]); + assert.deepStrictEqual(await wasm.async_string_vec(), ["a", "b", "c"]); + assert.strictEqual((await wasm.async_struct_vec()).length, 2); + assert.deepStrictEqual(await wasm.async_enum_vec(), [wasm.AnotherEnum.C, wasm.AnotherEnum.A, wasm.AnotherEnum.B]); +}; diff --git a/tests/wasm/async_vecs.rs b/tests/wasm/async_vecs.rs new file mode 100644 index 000000000000..d1705eb1695e --- /dev/null +++ b/tests/wasm/async_vecs.rs @@ -0,0 +1,66 @@ +use wasm_bindgen::prelude::*; +use wasm_bindgen_test::*; + +#[wasm_bindgen(module = "tests/wasm/async_vecs.js")] +extern "C" { + async fn js_works(); +} + +#[wasm_bindgen] +extern "C" { + pub type RegExp; + #[wasm_bindgen(constructor)] + fn new(re: &str) -> RegExp; +} + +#[wasm_bindgen] +pub async fn async_number_vec() -> Vec { + vec![1, -3, 7, 12] +} + +#[wasm_bindgen] +pub async fn async_jsvalue_vec() -> Vec { + vec![ + 1u32.into(), + "hi".into(), + Vec::::new().into(), + JsValue::NULL, + ] +} + +#[wasm_bindgen] +pub async fn async_import_vec() -> Vec { + vec![RegExp::new("hi|bye"), RegExp::new("hello w[a-z]rld")] +} + +#[wasm_bindgen] +pub async fn async_string_vec() -> Vec { + vec!["a".to_owned(), "b".to_owned(), "c".to_owned()] +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct AnotherStruct; + +#[wasm_bindgen] +pub async fn async_struct_vec() -> Vec { + vec![AnotherStruct; 2] +} + +#[wasm_bindgen] +#[derive(Clone)] +pub enum AnotherEnum { + A, + B, + C, +} + +#[wasm_bindgen] +pub async fn async_enum_vec() -> Vec { + vec![AnotherEnum::C, AnotherEnum::A, AnotherEnum::B] +} + +#[wasm_bindgen_test] +async fn works() { + js_works().await; +} diff --git a/tests/wasm/main.rs b/tests/wasm/main.rs index 72bb5c414f4d..00e58de56a62 100644 --- a/tests/wasm/main.rs +++ b/tests/wasm/main.rs @@ -16,6 +16,7 @@ use wasm_bindgen::prelude::*; pub mod api; pub mod arg_names; +pub mod async_vecs; pub mod bigint; pub mod char; pub mod classes;