Skip to content

Commit 30b2585

Browse files
authored
Merge pull request #1401 from alexcrichton/fix-u32
Fix handling of `u32` between Rust and JS
2 parents 98e554c + 0160f6a commit 30b2585

File tree

5 files changed

+126
-9
lines changed

5 files changed

+126
-9
lines changed

crates/cli-support/src/descriptor.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ pub enum VectorKind {
9999
Anyref,
100100
}
101101

102+
pub struct Number {
103+
u32: bool,
104+
}
105+
102106
impl Descriptor {
103107
pub fn decode(mut data: &[u32]) -> Descriptor {
104108
let descriptor = Descriptor::_decode(&mut data);
@@ -149,18 +153,20 @@ impl Descriptor {
149153
}
150154
}
151155

152-
pub fn is_number(&self) -> bool {
156+
/// Returns `Some` if this type is a number, and the returned `Number` type
157+
/// can be accessed to learn more about what kind of number this is.
158+
pub fn number(&self) -> Option<Number> {
153159
match *self {
154160
Descriptor::I8
155161
| Descriptor::U8
156162
| Descriptor::I16
157163
| Descriptor::U16
158164
| Descriptor::I32
159-
| Descriptor::U32
160165
| Descriptor::F32
161166
| Descriptor::F64
162-
| Descriptor::Enum { .. } => true,
163-
_ => return false,
167+
| Descriptor::Enum { .. } => Some(Number { u32: false }),
168+
Descriptor::U32 => Some(Number { u32: true }),
169+
_ => None,
164170
}
165171
}
166172

@@ -360,3 +366,9 @@ impl VectorKind {
360366
}
361367
}
362368
}
369+
370+
impl Number {
371+
pub fn is_u32(&self) -> bool {
372+
self.u32
373+
}
374+
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
384384
return Ok(self);
385385
}
386386

387-
if arg.is_number() {
387+
if arg.number().is_some() {
388388
self.js_arguments.push((name.clone(), "number".to_string()));
389389

390390
if self.cx.config.debug {
@@ -681,9 +681,13 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
681681
return Ok(self);
682682
}
683683

684-
if ty.is_number() {
684+
if let Some(num) = ty.number() {
685685
self.ret_ty = "number".to_string();
686-
self.ret_expr = format!("return RET;");
686+
if num.is_u32() {
687+
self.ret_expr = format!("return RET >>> 0;");
688+
} else {
689+
self.ret_expr = format!("return RET;");
690+
}
687691
return Ok(self);
688692
}
689693

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,16 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
309309
return Ok(());
310310
}
311311

312+
if let Some(num) = arg.number() {
313+
if num.is_u32() {
314+
self.js_arguments.push(format!("{} >>> 0", abi));
315+
} else {
316+
self.js_arguments.push(abi);
317+
}
318+
return Ok(());
319+
}
320+
312321
let invoc_arg = match *arg {
313-
ref d if d.is_number() => abi,
314322
Descriptor::Boolean => format!("{} !== 0", abi),
315323
Descriptor::Char => format!("String.fromCodePoint({})", abi),
316324
_ => bail!(
@@ -504,7 +512,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
504512

505513
return Ok(());
506514
}
507-
if ty.is_number() {
515+
if ty.number().is_some() {
508516
self.ret_expr = "return JS;".to_string();
509517
return Ok(());
510518
}

tests/wasm/math.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
const wasm = require('wasm-bindgen-test.js');
2+
const assert = require('assert');
23

34
exports.js_auto_bind_math = () => {
45
wasm.math(1.0, 2.0);
56
};
7+
8+
exports.roundtrip = x => x;
9+
10+
exports.test_js_roundtrip = () => {
11+
assert.strictEqual(wasm.rust_roundtrip_i8(0), 0);
12+
assert.strictEqual(wasm.rust_roundtrip_i8(0x80), -128);
13+
assert.strictEqual(wasm.rust_roundtrip_i8(0x7f), 127);
14+
15+
assert.strictEqual(wasm.rust_roundtrip_i16(0), 0);
16+
assert.strictEqual(wasm.rust_roundtrip_i16(0x8000), -32768);
17+
assert.strictEqual(wasm.rust_roundtrip_i16(0x7fff), 32767);
18+
19+
assert.strictEqual(wasm.rust_roundtrip_i32(0), 0);
20+
assert.strictEqual(wasm.rust_roundtrip_i32(0x80000000), -2147483648);
21+
assert.strictEqual(wasm.rust_roundtrip_i32(0x7fffffff), 2147483647);
22+
23+
assert.strictEqual(wasm.rust_roundtrip_u8(0), 0);
24+
assert.strictEqual(wasm.rust_roundtrip_u8(0x80), 128);
25+
assert.strictEqual(wasm.rust_roundtrip_u8(0x7f), 127);
26+
assert.strictEqual(wasm.rust_roundtrip_u8(0xff), 255);
27+
28+
assert.strictEqual(wasm.rust_roundtrip_u16(0), 0);
29+
assert.strictEqual(wasm.rust_roundtrip_u16(0x8000), 32768);
30+
assert.strictEqual(wasm.rust_roundtrip_u16(0x7fff), 32767);
31+
assert.strictEqual(wasm.rust_roundtrip_u16(0xffff), 65535);
32+
33+
assert.strictEqual(wasm.rust_roundtrip_u32(0), 0);
34+
assert.strictEqual(wasm.rust_roundtrip_u32(0x80000000), 2147483648);
35+
assert.strictEqual(wasm.rust_roundtrip_u32(0x7fffffff), 2147483647);
36+
assert.strictEqual(wasm.rust_roundtrip_u32(0xffffffff), 4294967295);
37+
};

tests/wasm/math.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@ use wasm_bindgen_test::*;
44
#[wasm_bindgen(module = "tests/wasm/math.js")]
55
extern "C" {
66
fn js_auto_bind_math();
7+
8+
// There's an identity function called `roundtrip` in the module and we bind
9+
// that one function with multiple different signatures here. Note that the
10+
// return value is always `f64` to faithfully capture what was sent to JS
11+
// (what we're interested in) because all JS numbers fit in `f64` anyway.
12+
// This is testing what happens when we pass numbers to JS and what it sees.
13+
#[wasm_bindgen(js_name = roundtrip)]
14+
fn roundtrip_i8(a: i8) -> f64;
15+
#[wasm_bindgen(js_name = roundtrip)]
16+
fn roundtrip_i16(a: i16) -> f64;
17+
#[wasm_bindgen(js_name = roundtrip)]
18+
fn roundtrip_i32(a: i32) -> f64;
19+
#[wasm_bindgen(js_name = roundtrip)]
20+
fn roundtrip_u8(a: u8) -> f64;
21+
#[wasm_bindgen(js_name = roundtrip)]
22+
fn roundtrip_u16(a: u16) -> f64;
23+
#[wasm_bindgen(js_name = roundtrip)]
24+
fn roundtrip_u32(a: u32) -> f64;
25+
26+
fn test_js_roundtrip();
727
}
828

929
#[wasm_bindgen]
@@ -65,3 +85,44 @@ pub fn math(a: f32, b: f64) -> f64 {
6585
fn auto_bind_math() {
6686
js_auto_bind_math();
6787
}
88+
89+
macro_rules! t_roundtrip {
90+
($f:ident($e:expr)) => (assert_eq!($f($e), $e as f64))
91+
}
92+
93+
#[wasm_bindgen_test]
94+
fn limits_correct() {
95+
t_roundtrip!(roundtrip_i8(i8::min_value()));
96+
t_roundtrip!(roundtrip_i8(0));
97+
t_roundtrip!(roundtrip_i8(i8::max_value()));
98+
t_roundtrip!(roundtrip_i16(i16::min_value()));
99+
t_roundtrip!(roundtrip_i16(0));
100+
t_roundtrip!(roundtrip_i16(i16::max_value()));
101+
t_roundtrip!(roundtrip_i32(i32::min_value()));
102+
t_roundtrip!(roundtrip_i32(0));
103+
t_roundtrip!(roundtrip_i32(i32::max_value()));
104+
t_roundtrip!(roundtrip_u8(u8::min_value()));
105+
t_roundtrip!(roundtrip_u8(0));
106+
t_roundtrip!(roundtrip_u8(u8::max_value()));
107+
t_roundtrip!(roundtrip_u16(u16::min_value()));
108+
t_roundtrip!(roundtrip_u16(0));
109+
t_roundtrip!(roundtrip_u16(u16::max_value()));
110+
t_roundtrip!(roundtrip_u32(u32::min_value()));
111+
t_roundtrip!(roundtrip_u32(0));
112+
t_roundtrip!(roundtrip_u32(u32::max_value()));
113+
114+
test_js_roundtrip();
115+
116+
#[wasm_bindgen]
117+
pub fn rust_roundtrip_i8(a: i8) -> i8 { a }
118+
#[wasm_bindgen]
119+
pub fn rust_roundtrip_i16(a: i16) -> i16 { a }
120+
#[wasm_bindgen]
121+
pub fn rust_roundtrip_i32(a: i32) -> i32 { a }
122+
#[wasm_bindgen]
123+
pub fn rust_roundtrip_u8(a: u8) -> u8 { a }
124+
#[wasm_bindgen]
125+
pub fn rust_roundtrip_u16(a: u16) -> u16 { a }
126+
#[wasm_bindgen]
127+
pub fn rust_roundtrip_u32(a: u32) -> u32 { a }
128+
}

0 commit comments

Comments
 (0)