diff --git a/README.md b/README.md index 150024ba..d46017c3 100644 --- a/README.md +++ b/README.md @@ -402,9 +402,10 @@ are: * Imported types in a foreign module annotated with `#[wasm_bindgen]` * Borrowed exported structs (`&Foo` or `&mut Bar`) * The `JsValue` type and `&JsValue` (not mutable references) -* Vectors and slices of supported integer types +* Vectors and slices of supported integer types and of the `JsValue` type. -All of the above can also be returned except borrowed references. Strings are +All of the above can also be returned except borrowed references. Passing +`Vec` as an argument to a function is not currently supported. Strings are implemented with shim functions to copy data in/out of the Rust heap. That is, a string passed to Rust from JS is copied to the Rust heap (using a generated shim to malloc some space) and then will be freed appropriately. diff --git a/crates/wasm-bindgen-cli-support/src/js.rs b/crates/wasm-bindgen-cli-support/src/js.rs index b19767e2..a099d646 100644 --- a/crates/wasm-bindgen-cli-support/src/js.rs +++ b/crates/wasm-bindgen-cli-support/src/js.rs @@ -656,6 +656,25 @@ impl<'a> Context<'a> { } } + fn expose_get_array_js_value_from_wasm(&mut self) { + if !self.exposed_globals.insert("get_array_js_value_from_wasm") { + return + } + self.expose_get_array_u32_from_wasm(); + self.expose_get_object(); + self.globals.push_str(&format!(" + function getArrayJsValueFromWasm(ptr, len) {{ + const mem = getUint32Memory(); + const slice = mem.slice(ptr / 4, ptr / 4 + len); + const result = [] + for (ptr in slice) {{ + result.push(getObject(ptr)) + }} + return result; + }} + ")); + } + fn expose_get_array_i8_from_wasm(&mut self) { self.expose_uint8_memory(); if !self.exposed_globals.insert("get_array_i8_from_wasm") { @@ -925,6 +944,7 @@ impl<'a> Context<'a> { self.expose_pass_array_f64_to_wasm(); "passArrayF64ToWasm" } + VectorKind::JsValue => panic!("Cannot pass Vec to function") } } @@ -966,6 +986,10 @@ impl<'a> Context<'a> { self.expose_get_array_f64_from_wasm(); "getArrayF64FromWasm" } + VectorKind::JsValue => { + self.expose_get_array_js_value_from_wasm(); + "getArrayJsValueFromWasm" + } } } } @@ -1184,7 +1208,8 @@ impl<'a, 'b> SubContext<'a, 'b> { Some(shared::TYPE_VECTOR_U32) | Some(shared::TYPE_VECTOR_I32) | Some(shared::TYPE_VECTOR_F32) | - Some(shared::TYPE_VECTOR_F64) => { + Some(shared::TYPE_VECTOR_F64) | + Some(shared::TYPE_VECTOR_JSVALUE) => { let ty = VectorType::from(function.ret.unwrap()); dst_ts.push_str(": "); dst_ts.push_str(ty.js_ty()); @@ -1466,6 +1491,7 @@ enum VectorKind { U32, F32, F64, + JsValue } impl VectorType { @@ -1525,6 +1551,9 @@ impl VectorType { shared::TYPE_SLICE_F64 => { VectorType { owned: false, kind: VectorKind::F64 } } + shared::TYPE_VECTOR_JSVALUE => { + VectorType { owned: true, kind: VectorKind::JsValue } + } _ => panic!() } } @@ -1540,6 +1569,7 @@ impl VectorType { VectorKind::U32 => "Uint32Array", VectorKind::F32 => "Float32Array", VectorKind::F64 => "Float64Array", + VectorKind::JsValue => "any[]", } } } diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index 7abacbde..f6ec107b 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -72,6 +72,7 @@ pub enum VectorType { U32, F32, F64, + JsValue, } impl Program { @@ -544,6 +545,8 @@ impl Type { Type::Vector(VectorType::F32, false) => a.char(shared::TYPE_SLICE_F32), Type::Vector(VectorType::F64, true) => a.char(shared::TYPE_VECTOR_F64), Type::Vector(VectorType::F64, false) => a.char(shared::TYPE_SLICE_F64), + Type::Vector(VectorType::JsValue, true) => a.char(shared::TYPE_VECTOR_JSVALUE), + Type::Vector(VectorType::JsValue, false) => panic!("Slices of JsValues not supported"), Type::ByValue(ref t) => { a.as_char(my_quote! { <#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR @@ -978,6 +981,7 @@ impl VectorType { "u32" => Some(VectorType::U32), "f32" => Some(VectorType::F32), "f64" => Some(VectorType::F64), + "JsValue" => Some(VectorType::JsValue), _ => None, } } @@ -993,6 +997,7 @@ impl VectorType { VectorType::U32 => syn::Ident::from("u32"), VectorType::F32 => syn::Ident::from("f32"), VectorType::F64 => syn::Ident::from("f64"), + VectorType::JsValue => syn::Ident::from("JsValue"), } } } @@ -1009,6 +1014,7 @@ impl ToTokens for VectorType { VectorType::U32 => my_quote! { Vec }, VectorType::F32 => my_quote! { Vec }, VectorType::F64 => my_quote! { Vec }, + VectorType::JsValue => my_quote! { Vec }, }; me.to_tokens(tokens); } diff --git a/crates/wasm-bindgen-shared/src/lib.rs b/crates/wasm-bindgen-shared/src/lib.rs index 62d0d220..d4ef64d7 100644 --- a/crates/wasm-bindgen-shared/src/lib.rs +++ b/crates/wasm-bindgen-shared/src/lib.rs @@ -90,6 +90,8 @@ pub fn mangled_import_name(struct_: Option<&str>, f: &str) -> String { pub type Type = char; +pub const TYPE_VECTOR_JSVALUE: char = '\u{5b}'; +// Note: '\u{5c}' is '\' which breaks json encoding/decoding pub const TYPE_ENUM: char = '\u{5d}'; pub const TYPE_NUMBER: char = '\u{5e}'; pub const TYPE_BORROWED_STR: char = '\u{5f}'; diff --git a/tests/jsobjects.rs b/tests/jsobjects.rs index 265e06dc..06b338e6 100644 --- a/tests/jsobjects.rs +++ b/tests/jsobjects.rs @@ -176,3 +176,42 @@ fn promote() { "#) .test(); } + +#[test] +fn returning_vector() { + test_support::project() + .file("src/lib.rs", r#" + #![feature(proc_macro)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen(module = "./test")] + extern { + fn foo() -> JsValue; + } + + #[wasm_bindgen] + #[no_mangle] + pub extern fn bar() -> Vec { + let mut res = Vec::new(); + for _ in 0..10 { + res.push(foo()) + } + res + } + "#) + .file("test.ts", r#" + import * as wasm from "./out"; + import * as assert from "assert"; + + export function foo(): any { return { "foo": "bar" }; } + + export function test() { + const result = wasm.bar(); + assert.strictEqual(result.length, 10); + } + "#) + .test(); +}