Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Into<JsValue> for Vec #3630

Merged
merged 6 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
* Add method `copy_within` for TypedArray, add methods `find_last`,`find_last_index` for Array.
[#3888](https://github.com/rustwasm/wasm-bindgen/pull/3888)

* Added support for returning `Vec`s from async functions.
[#3630](https://github.com/rustwasm/wasm-bindgen/pull/3630)

### Changed

* Stabilize Web Share API.
Expand Down
12 changes: 12 additions & 0 deletions crates/backend/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -1509,6 +1515,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);
}
Expand Down
43 changes: 43 additions & 0 deletions crates/cli-support/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ fn slice(contents: Descriptor) -> Descriptor {
Descriptor::Ref(Box::new(Descriptor::Slice(Box::new(contents))))
}

fn vector(contents: Descriptor) -> Descriptor {
Descriptor::Vector(Box::new(contents))
}

intrinsics! {
pub enum Intrinsic {
#[symbol = "__wbindgen_jsval_eq"]
Expand Down Expand Up @@ -264,6 +268,45 @@ intrinsics! {
#[symbol = "__wbindgen_copy_to_typed_array"]
#[signature = fn(slice(U8), ref_externref()) -> Unit]
CopyToTypedArray,
#[symbol = "__wbindgen_uint8_array_new"]
#[signature = fn(vector(U8)) -> Externref]
Uint8ArrayNew,
#[symbol = "__wbindgen_uint8_clamped_array_new"]
#[signature = fn(vector(ClampedU8)) -> Externref]
Uint8ClampedArrayNew,
#[symbol = "__wbindgen_uint16_array_new"]
#[signature = fn(vector(U16)) -> Externref]
Uint16ArrayNew,
#[symbol = "__wbindgen_uint32_array_new"]
#[signature = fn(vector(U32)) -> Externref]
Uint32ArrayNew,
#[symbol = "__wbindgen_biguint64_array_new"]
#[signature = fn(vector(U64)) -> Externref]
BigUint64ArrayNew,
#[symbol = "__wbindgen_int8_array_new"]
#[signature = fn(vector(I8)) -> Externref]
Int8ArrayNew,
#[symbol = "__wbindgen_int16_array_new"]
#[signature = fn(vector(I16)) -> Externref]
Int16ArrayNew,
#[symbol = "__wbindgen_int32_array_new"]
#[signature = fn(vector(I32)) -> Externref]
Int32ArrayNew,
#[symbol = "__wbindgen_bigint64_array_new"]
#[signature = fn(vector(I64)) -> Externref]
BigInt64ArrayNew,
#[symbol = "__wbindgen_float32_array_new"]
#[signature = fn(vector(F32)) -> Externref]
Float32ArrayNew,
#[symbol = "__wbindgen_float64_array_new"]
#[signature = fn(vector(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,
Expand Down
25 changes: 25 additions & 0 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3577,6 +3577,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();
Expand Down
117 changes: 117 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,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;
Expand Down Expand Up @@ -1804,6 +1819,35 @@ pub mod __rt {
}
}
}

if_std! {
use core::mem;
use std::boxed::Box;

/// Trait for element types to implement `Into<JsValue>` 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<T: VectorIntoJsValue> From<Box<[T]>> for JsValue {
fn from(vector: Box<[T]>) -> Self {
T::vector_into_jsvalue(vector)
}
}

pub fn js_value_vector_into_jsvalue<T: 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`
Expand Down Expand Up @@ -1922,3 +1966,76 @@ impl From<JsError> for JsValue {
error.value
}
}

macro_rules! typed_arrays {
($($ty:ident $ctor:ident $clamped_ctor:ident,)*) => {
$(
impl From<Box<[$ty]>> for JsValue {
fn from(mut vector: Box<[$ty]>) -> Self {
let result = unsafe { JsValue::_new($ctor(vector.as_mut_ptr(), vector.len())) };
mem::forget(vector);
result
}
}

impl From<Clamped<Box<[$ty]>>> for JsValue {
fn from(mut vector: Clamped<Box<[$ty]>>) -> Self {
let result = unsafe { JsValue::_new($clamped_ctor(vector.as_mut_ptr(), vector.len())) };
mem::forget(vector);
result
}
}
)*
};
}

if_std! {
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::<JsValue>(vector)
}
}

impl<T: JsObject> __rt::VectorIntoJsValue for T {
fn vector_into_jsvalue(vector: Box<[T]>) -> JsValue {
__rt::js_value_vector_into_jsvalue::<T>(vector)
}
}

impl __rt::VectorIntoJsValue for String {
fn vector_into_jsvalue(vector: Box<[String]>) -> JsValue {
__rt::js_value_vector_into_jsvalue::<String>(vector)
}
}

impl<T> From<Vec<T>> for JsValue
where
JsValue: From<Box<[T]>>,
{
fn from(vector: Vec<T>) -> Self {
JsValue::from(vector.into_boxed_slice())
}
}

impl<T> From<Clamped<Vec<T>>> for JsValue
where
JsValue: From<Clamped<Box<[T]>>>,
{
fn from(vector: Clamped<Vec<T>>) -> Self {
JsValue::from(Clamped(vector.0.into_boxed_slice()))
}
}
}
17 changes: 17 additions & 0 deletions tests/wasm/async_vecs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const wasm = require('wasm-bindgen-test.js');
const assert = require('assert');

exports.js_works = async () => {
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]);

const numberVec = await wasm.async_number_vec();
assert.deepStrictEqual(numberVec, new Int32Array([1, -3, 7, 12]));
// Make sure `numberVec` is a fresh `Int32Array`, not a view into Wasm memory,
// so that it can be GC'd without the whole Wasm module having to be GC'd as
// well.
assert.strictEqual(numberVec.byteLength, numberVec.buffer.byteLength);
};
66 changes: 66 additions & 0 deletions tests/wasm/async_vecs.rs
Original file line number Diff line number Diff line change
@@ -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<i32> {
vec![1, -3, 7, 12]
}

#[wasm_bindgen]
pub async fn async_jsvalue_vec() -> Vec<JsValue> {
vec![
1u32.into(),
"hi".into(),
Vec::<f64>::new().into(),
JsValue::NULL,
]
}

#[wasm_bindgen]
pub async fn async_import_vec() -> Vec<RegExp> {
vec![RegExp::new("hi|bye"), RegExp::new("hello w[a-z]rld")]
}

#[wasm_bindgen]
pub async fn async_string_vec() -> Vec<String> {
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<AnotherStruct> {
vec![AnotherStruct; 2]
}

#[wasm_bindgen]
#[derive(Clone)]
pub enum AnotherEnum {
A,
B,
C,
}

#[wasm_bindgen]
pub async fn async_enum_vec() -> Vec<AnotherEnum> {
vec![AnotherEnum::C, AnotherEnum::A, AnotherEnum::B]
}

#[wasm_bindgen_test]
async fn works() {
js_works().await;
}
1 change: 1 addition & 0 deletions tests/wasm/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down