Skip to content

Commit bf39cfd

Browse files
Implement To/FromWasmAbi traits for boxed slices of JsValue wrappers (#2614)
* Implement To/FromWasmAbi traits for boxed slices of JsValue wrappers * Add proper TypeScript types for NamedExternref Vecs * Hide "JsObject" from docs * Parenthesize array type * Use `format!` instead of `push_str` * Update formatting for JsCast/JsObject re-export * Add tests
1 parent 17eab63 commit bf39cfd

File tree

12 files changed

+158
-43
lines changed

12 files changed

+158
-43
lines changed

crates/backend/src/codegen.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ impl ToTokens for ast::ImportType {
624624
use wasm_bindgen::convert::{OptionIntoWasmAbi, OptionFromWasmAbi};
625625
use wasm_bindgen::convert::RefFromWasmAbi;
626626
use wasm_bindgen::describe::WasmDescribe;
627-
use wasm_bindgen::{JsValue, JsCast};
627+
use wasm_bindgen::{JsValue, JsCast, JsObject};
628628
use wasm_bindgen::__rt::core;
629629

630630
impl WasmDescribe for #rust_name {
@@ -761,6 +761,8 @@ impl ToTokens for ast::ImportType {
761761
}
762762
}
763763

764+
impl JsObject for #rust_name {}
765+
764766
()
765767
};
766768
})

crates/cli-support/src/descriptor.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub struct Closure {
8686
pub mutable: bool,
8787
}
8888

89-
#[derive(Copy, Clone, Debug)]
89+
#[derive(Clone, Debug)]
9090
pub enum VectorKind {
9191
I8,
9292
U8,
@@ -101,6 +101,7 @@ pub enum VectorKind {
101101
F64,
102102
String,
103103
Externref,
104+
NamedExternref(String),
104105
}
105106

106107
impl Descriptor {
@@ -193,6 +194,7 @@ impl Descriptor {
193194
Descriptor::F32 => Some(VectorKind::F32),
194195
Descriptor::F64 => Some(VectorKind::F64),
195196
Descriptor::Externref => Some(VectorKind::Externref),
197+
Descriptor::NamedExternref(ref name) => Some(VectorKind::NamedExternref(name.clone())),
196198
_ => None,
197199
}
198200
}
@@ -240,21 +242,24 @@ impl Function {
240242
}
241243

242244
impl VectorKind {
243-
pub fn js_ty(&self) -> &str {
245+
pub fn js_ty(&self) -> String {
244246
match *self {
245-
VectorKind::String => "string",
246-
VectorKind::I8 => "Int8Array",
247-
VectorKind::U8 => "Uint8Array",
248-
VectorKind::ClampedU8 => "Uint8ClampedArray",
249-
VectorKind::I16 => "Int16Array",
250-
VectorKind::U16 => "Uint16Array",
251-
VectorKind::I32 => "Int32Array",
252-
VectorKind::U32 => "Uint32Array",
253-
VectorKind::I64 => "BigInt64Array",
254-
VectorKind::U64 => "BigUint64Array",
255-
VectorKind::F32 => "Float32Array",
256-
VectorKind::F64 => "Float64Array",
257-
VectorKind::Externref => "any[]",
247+
VectorKind::String => "string".to_string(),
248+
VectorKind::I8 => "Int8Array".to_string(),
249+
VectorKind::U8 => "Uint8Array".to_string(),
250+
VectorKind::ClampedU8 => "Uint8ClampedArray".to_string(),
251+
VectorKind::I16 => "Int16Array".to_string(),
252+
VectorKind::U16 => "Uint16Array".to_string(),
253+
VectorKind::I32 => "Int32Array".to_string(),
254+
VectorKind::U32 => "Uint32Array".to_string(),
255+
VectorKind::I64 => "BigInt64Array".to_string(),
256+
VectorKind::U64 => "BigUint64Array".to_string(),
257+
VectorKind::F32 => "Float32Array".to_string(),
258+
VectorKind::F64 => "Float64Array".to_string(),
259+
VectorKind::Externref => "any[]".to_string(),
260+
VectorKind::NamedExternref(ref name) => {
261+
format!("({})[]", name)
262+
}
258263
}
259264
}
260265

@@ -273,6 +278,7 @@ impl VectorKind {
273278
VectorKind::F32 => 4,
274279
VectorKind::F64 => 8,
275280
VectorKind::Externref => 4,
281+
VectorKind::NamedExternref(_) => 4,
276282
}
277283
}
278284
}

crates/cli-support/src/externref.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -351,31 +351,31 @@ fn module_needs_externref_metadata(aux: &WasmBindgenAux, section: &NonstandardWi
351351
};
352352
instructions.iter().any(|instr| match instr.instr {
353353
VectorToMemory {
354-
kind: VectorKind::Externref,
354+
kind: VectorKind::Externref | VectorKind::NamedExternref(_),
355355
..
356356
}
357357
| MutableSliceToMemory {
358-
kind: VectorKind::Externref,
358+
kind: VectorKind::Externref | VectorKind::NamedExternref(_),
359359
..
360360
}
361361
| OptionVector {
362-
kind: VectorKind::Externref,
362+
kind: VectorKind::Externref | VectorKind::NamedExternref(_),
363363
..
364364
}
365365
| VectorLoad {
366-
kind: VectorKind::Externref,
366+
kind: VectorKind::Externref | VectorKind::NamedExternref(_),
367367
..
368368
}
369369
| OptionVectorLoad {
370-
kind: VectorKind::Externref,
370+
kind: VectorKind::Externref | VectorKind::NamedExternref(_),
371371
..
372372
}
373373
| View {
374-
kind: VectorKind::Externref,
374+
kind: VectorKind::Externref | VectorKind::NamedExternref(_),
375375
..
376376
}
377377
| OptionView {
378-
kind: VectorKind::Externref,
378+
kind: VectorKind::Externref | VectorKind::NamedExternref(_),
379379
..
380380
} => true,
381381
_ => false,

crates/cli-support/src/js/binding.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
762762

763763
Instruction::VectorToMemory { kind, malloc, mem } => {
764764
let val = js.pop();
765-
let func = js.cx.pass_to_wasm_function(*kind, *mem)?;
765+
let func = js.cx.pass_to_wasm_function(kind.clone(), *mem)?;
766766
let malloc = js.cx.export_name_of(*malloc);
767767
let i = js.tmp();
768768
js.prelude(&format!(
@@ -805,7 +805,7 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
805805
}
806806

807807
Instruction::OptionVector { kind, mem, malloc } => {
808-
let func = js.cx.pass_to_wasm_function(*kind, *mem)?;
808+
let func = js.cx.pass_to_wasm_function(kind.clone(), *mem)?;
809809
js.cx.expose_is_like_none();
810810
let i = js.tmp();
811811
let malloc = js.cx.export_name_of(*malloc);
@@ -832,7 +832,7 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
832832
// a length. These two pointer/length values get pushed onto the
833833
// value stack.
834834
let val = js.pop();
835-
let func = js.cx.pass_to_wasm_function(*kind, *mem)?;
835+
let func = js.cx.pass_to_wasm_function(kind.clone(), *mem)?;
836836
let malloc = js.cx.export_name_of(*malloc);
837837
let i = js.tmp();
838838
js.prelude(&format!(
@@ -850,7 +850,7 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
850850
// original mutable slice with any modifications, and then free the
851851
// Rust-backed memory.
852852
let free = js.cx.export_name_of(*free);
853-
let get = js.cx.memview_function(*kind, *mem);
853+
let get = js.cx.memview_function(kind.clone(), *mem);
854854
js.finally(&format!(
855855
"
856856
{val}.set({get}().subarray(ptr{i} / {size}, ptr{i} / {size} + len{i}));
@@ -1003,7 +1003,7 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
10031003
Instruction::VectorLoad { kind, mem, free } => {
10041004
let len = js.pop();
10051005
let ptr = js.pop();
1006-
let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?;
1006+
let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem)?;
10071007
let i = js.tmp();
10081008
let free = js.cx.export_name_of(*free);
10091009
js.prelude(&format!("var v{} = {}({}, {}).slice();", i, f, ptr, len));
@@ -1020,7 +1020,7 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
10201020
Instruction::OptionVectorLoad { kind, mem, free } => {
10211021
let len = js.pop();
10221022
let ptr = js.pop();
1023-
let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?;
1023+
let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem)?;
10241024
let i = js.tmp();
10251025
let free = js.cx.export_name_of(*free);
10261026
js.prelude(&format!("let v{};", i));
@@ -1040,14 +1040,14 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
10401040
Instruction::View { kind, mem } => {
10411041
let len = js.pop();
10421042
let ptr = js.pop();
1043-
let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?;
1043+
let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem)?;
10441044
js.push(format!("{f}({ptr}, {len})", ptr = ptr, len = len, f = f));
10451045
}
10461046

10471047
Instruction::OptionView { kind, mem } => {
10481048
let len = js.pop();
10491049
let ptr = js.pop();
1050-
let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?;
1050+
let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem)?;
10511051
js.push(format!(
10521052
"{ptr} === 0 ? undefined : {f}({ptr}, {len})",
10531053
ptr = ptr,
@@ -1226,7 +1226,7 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) {
12261226
AdapterType::String => dst.push_str("string"),
12271227
AdapterType::Externref => dst.push_str("any"),
12281228
AdapterType::Bool => dst.push_str("boolean"),
1229-
AdapterType::Vector(kind) => dst.push_str(kind.js_ty()),
1229+
AdapterType::Vector(kind) => dst.push_str(&kind.js_ty()),
12301230
AdapterType::Option(ty) => {
12311231
adapter2ts(ty, dst);
12321232
dst.push_str(" | undefined");

crates/cli-support/src/js/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,6 +1621,7 @@ impl<'a> Context<'a> {
16211621
VectorKind::F32 => self.expose_f32_memory(memory),
16221622
VectorKind::F64 => self.expose_f64_memory(memory),
16231623
VectorKind::Externref => self.expose_uint32_memory(memory),
1624+
VectorKind::NamedExternref(_) => self.expose_uint32_memory(memory),
16241625
}
16251626
}
16261627

@@ -1842,6 +1843,7 @@ impl<'a> Context<'a> {
18421843
VectorKind::F32 => self.expose_pass_array_f32_to_wasm(memory),
18431844
VectorKind::F64 => self.expose_pass_array_f64_to_wasm(memory),
18441845
VectorKind::Externref => self.expose_pass_array_jsvalue_to_wasm(memory),
1846+
VectorKind::NamedExternref(_) => self.expose_pass_array_jsvalue_to_wasm(memory),
18451847
}
18461848
}
18471849

@@ -1864,6 +1866,7 @@ impl<'a> Context<'a> {
18641866
VectorKind::F32 => self.expose_get_array_f32_from_wasm(memory),
18651867
VectorKind::F64 => self.expose_get_array_f64_from_wasm(memory),
18661868
VectorKind::Externref => self.expose_get_array_js_value_from_wasm(memory)?,
1869+
VectorKind::NamedExternref(_) => self.expose_get_array_js_value_from_wasm(memory)?,
18671870
})
18681871
}
18691872

crates/cli-support/src/wit/incoming.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ impl InstructionBuilder<'_, '_> {
121121
format_err!("unsupported argument type for calling Rust function from JS {:?}", arg)
122122
})?;
123123
self.instruction(
124-
&[AdapterType::Vector(kind)],
124+
&[AdapterType::Vector(kind.clone())],
125125
Instruction::VectorToMemory {
126126
kind,
127127
malloc: self.cx.malloc()?,
@@ -198,7 +198,7 @@ impl InstructionBuilder<'_, '_> {
198198
})?;
199199
if mutable {
200200
self.instruction(
201-
&[AdapterType::Vector(kind)],
201+
&[AdapterType::Vector(kind.clone())],
202202
Instruction::MutableSliceToMemory {
203203
kind,
204204
malloc: self.cx.malloc()?,
@@ -209,7 +209,7 @@ impl InstructionBuilder<'_, '_> {
209209
);
210210
} else {
211211
self.instruction(
212-
&[AdapterType::Vector(kind)],
212+
&[AdapterType::Vector(kind.clone())],
213213
Instruction::VectorToMemory {
214214
kind,
215215
malloc: self.cx.malloc()?,
@@ -322,7 +322,7 @@ impl InstructionBuilder<'_, '_> {
322322
let malloc = self.cx.malloc()?;
323323
let mem = self.cx.memory()?;
324324
self.instruction(
325-
&[AdapterType::Vector(kind).option()],
325+
&[AdapterType::Vector(kind.clone()).option()],
326326
Instruction::OptionVector { kind, malloc, mem },
327327
&[AdapterType::I32, AdapterType::I32],
328328
);

crates/cli-support/src/wit/outgoing.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,11 @@ impl InstructionBuilder<'_, '_> {
139139
let free = self.cx.free()?;
140140
self.instruction(
141141
&[AdapterType::I32, AdapterType::I32],
142-
Instruction::VectorLoad { kind, mem, free },
142+
Instruction::VectorLoad {
143+
kind: kind.clone(),
144+
mem,
145+
free,
146+
},
143147
&[AdapterType::Vector(kind)],
144148
);
145149
}
@@ -196,7 +200,10 @@ impl InstructionBuilder<'_, '_> {
196200
let mem = self.cx.memory()?;
197201
self.instruction(
198202
&[AdapterType::I32, AdapterType::I32],
199-
Instruction::View { kind, mem },
203+
Instruction::View {
204+
kind: kind.clone(),
205+
mem,
206+
},
200207
&[AdapterType::Vector(kind)],
201208
);
202209
}
@@ -313,7 +320,11 @@ impl InstructionBuilder<'_, '_> {
313320
let free = self.cx.free()?;
314321
self.instruction(
315322
&[AdapterType::I32, AdapterType::I32],
316-
Instruction::OptionVectorLoad { kind, mem, free },
323+
Instruction::OptionVectorLoad {
324+
kind: kind.clone(),
325+
mem,
326+
free,
327+
},
317328
&[AdapterType::Vector(kind).option()],
318329
);
319330
}
@@ -355,7 +366,10 @@ impl InstructionBuilder<'_, '_> {
355366
let mem = self.cx.memory()?;
356367
self.instruction(
357368
&[AdapterType::I32, AdapterType::I32],
358-
Instruction::OptionView { kind, mem },
369+
Instruction::OptionView {
370+
kind: kind.clone(),
371+
mem,
372+
},
359373
&[AdapterType::Vector(kind).option()],
360374
);
361375
}

src/cast.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::JsValue;
1+
use crate::{describe::WasmDescribe, JsValue};
22

33
/// A trait for checked and unchecked casting between JS types.
44
///
@@ -154,3 +154,7 @@ where
154154
/// won't need to call this.
155155
fn unchecked_from_js_ref(val: &JsValue) -> &Self;
156156
}
157+
158+
/// Trait implemented for wrappers around `JsValue`s generated by `#[wasm_bindgen]`.
159+
#[doc(hidden)]
160+
pub trait JsObject: JsCast + WasmDescribe {}

src/convert/slices.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::prelude::v1::*;
44
use core::slice;
55
use core::str;
66

7+
use crate::cast::JsObject;
78
use crate::convert::OptionIntoWasmAbi;
89
use crate::convert::{FromWasmAbi, IntoWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi, WasmAbi};
910
use cfg_if::cfg_if;
@@ -270,4 +271,41 @@ if_std! {
270271
#[inline]
271272
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
272273
}
274+
275+
impl<T> IntoWasmAbi for Box<[T]> where T: JsObject {
276+
type Abi = WasmSlice;
277+
278+
#[inline]
279+
fn into_abi(self) -> WasmSlice {
280+
let ptr = self.as_ptr();
281+
let len = self.len();
282+
mem::forget(self);
283+
WasmSlice {
284+
ptr: ptr.into_abi(),
285+
len: len as u32,
286+
}
287+
}
288+
}
289+
290+
impl<T> OptionIntoWasmAbi for Box<[T]> where T: JsObject {
291+
#[inline]
292+
fn none() -> WasmSlice { null_slice() }
293+
}
294+
295+
impl<T> FromWasmAbi for Box<[T]> where T: JsObject {
296+
type Abi = WasmSlice;
297+
298+
#[inline]
299+
unsafe fn from_abi(js: WasmSlice) -> Self {
300+
let ptr = <*mut JsValue>::from_abi(js.ptr);
301+
let len = js.len as usize;
302+
let vec: Vec<T> = Vec::from_raw_parts(ptr, len, len).drain(..).map(|js_value| T::unchecked_from_js(js_value)).collect();
303+
return vec.into_boxed_slice();
304+
}
305+
}
306+
307+
impl<T> OptionFromWasmAbi for Box<[T]> where T: JsObject {
308+
#[inline]
309+
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
310+
}
273311
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub mod convert;
6262
pub mod describe;
6363

6464
mod cast;
65-
pub use crate::cast::JsCast;
65+
pub use crate::cast::{JsCast, JsObject};
6666

6767
if_std! {
6868
extern crate std;

0 commit comments

Comments
 (0)