Skip to content

Commit afaf94a

Browse files
afdwalexcrichton
authored andcommitted
Add support for optional chars
1 parent 4a0c69f commit afaf94a

File tree

5 files changed

+85
-0
lines changed

5 files changed

+85
-0
lines changed

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,13 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
277277
self.rust_arguments.push(format!("isLikeNone({0}) ? 0xFFFFFF : {0} ? 1 : 0", name));
278278
return Ok(self);
279279
},
280+
Descriptor::Char => {
281+
self.cx.expose_is_like_none();
282+
self.js_arguments.push((name.clone(), "string".to_string()));
283+
self.rust_arguments.push(format!("!isLikeNone({0})", name));
284+
self.rust_arguments.push(format!("isLikeNone({0}) ? 0 : {0}.codePointAt(0)", name));
285+
return Ok(self);
286+
},
280287
_ => bail!("unsupported optional argument type for calling Rust function from JS: {:?}", arg),
281288
};
282289
}
@@ -514,6 +521,20 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
514521
".to_string();
515522
return Ok(self);
516523
},
524+
Descriptor::Char => {
525+
self.ret_ty = "string".to_string();
526+
self.cx.expose_global_argument_ptr()?;
527+
self.cx.expose_uint32_memory();
528+
self.prelude("const retptr = globalArgumentPtr();");
529+
self.rust_arguments.insert(0, "retptr".to_string());
530+
self.ret_expr = "
531+
RET;
532+
const present = getUint32Memory()[retptr / 4];
533+
const value = getUint32Memory()[retptr / 4 + 1];
534+
return present === 0 ? undefined : String.fromCodePoint(value);
535+
".to_string();
536+
return Ok(self);
537+
},
517538
_ => bail!("unsupported optional return type for calling Rust function from JS: {:?}", ty),
518539
};
519540
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,15 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
178178
self.js_arguments.push(format!("{0} === 0xFFFFFF ? undefined : {0} !== 0", abi));
179179
return Ok(())
180180
},
181+
Descriptor::Char => {
182+
let value = self.shim_argument();
183+
self.js_arguments.push(format!(
184+
"{present} === 0 ? undefined : String.fromCodePoint({value})",
185+
value = value,
186+
present = abi,
187+
));
188+
return Ok(())
189+
},
181190
_ => bail!("unsupported optional argument type for calling JS function from Rust: {:?}", arg),
182191
};
183192
}
@@ -439,6 +448,17 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
439448
".to_string();
440449
return Ok(());
441450
},
451+
Descriptor::Char => {
452+
self.cx.expose_is_like_none();
453+
self.cx.expose_uint32_memory();
454+
self.shim_arguments.insert(0, "ret".to_string());
455+
self.ret_expr = "
456+
const val = JS;
457+
getUint32Memory()[ret / 4] = !isLikeNone(val);
458+
getUint32Memory()[ret / 4 + 1] = isLikeNone(val) ? 0 : val.codePointAt(0);
459+
".to_string();
460+
return Ok(());
461+
},
442462
_ => bail!("unsupported optional return type for calling JS function from Rust: {:?}", ty),
443463
};
444464
}

src/convert/impls.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,35 @@ impl FromWasmAbi for char {
248248
}
249249
}
250250

251+
impl IntoWasmAbi for Option<char> {
252+
type Abi = WasmOptionalU32;
253+
254+
fn into_abi(self, _extra: &mut Stack) -> WasmOptionalU32 {
255+
match self {
256+
None => WasmOptionalU32 {
257+
present: 0,
258+
value: 0,
259+
},
260+
Some(me) => WasmOptionalU32 {
261+
present: 1,
262+
value: me as u32,
263+
},
264+
}
265+
}
266+
}
267+
268+
impl FromWasmAbi for Option<char> {
269+
type Abi = WasmOptionalU32;
270+
271+
unsafe fn from_abi(js: WasmOptionalU32, _extra: &mut Stack) -> Self {
272+
if js.present == 0 {
273+
None
274+
} else {
275+
Some(char::from_u32_unchecked(js.value))
276+
}
277+
}
278+
}
279+
251280
impl<T> IntoWasmAbi for *const T {
252281
type Abi = u32;
253282

tests/wasm/optional_numbers.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ exports.test_works = function() {
8080
assert.strictEqual(wasm.bool_identity(wasm.bool_none()), undefined);
8181
assert.strictEqual(wasm.bool_identity(wasm.bool_false()), false);
8282
assert.strictEqual(wasm.bool_identity(wasm.bool_true()), true);
83+
84+
assert.strictEqual(wasm.char_identity(wasm.char_none()), undefined);
85+
assert.strictEqual(wasm.char_identity(wasm.char_letter()), 'a');
86+
assert.strictEqual(wasm.char_identity(wasm.char_face()), '😀');
8387
};
8488

8589
exports.i32_js_identity = function(a) { return a; };
@@ -95,3 +99,4 @@ exports.u16_js_identity = function(a) { return a; };
9599
exports.i64_js_identity = function(a) { return a; };
96100
exports.u64_js_identity = function(a) { return a; };
97101
exports.bool_js_identity = function(a) { return a; };
102+
exports.char_js_identity = function(a) { return a; };

tests/wasm/optional_numbers.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extern {
1616
fn i64_js_identity(a: Option<i64>) -> Option<i64>;
1717
fn u64_js_identity(a: Option<u64>) -> Option<u64>;
1818
fn bool_js_identity(a: Option<bool>) -> Option<bool>;
19+
fn char_js_identity(a: Option<char>) -> Option<char>;
1920

2021
fn test_works();
2122
}
@@ -191,6 +192,15 @@ pub fn bool_true() -> Option<bool> { Some(true) }
191192
#[wasm_bindgen]
192193
pub fn bool_identity(a: Option<bool>) -> Option<bool> { bool_js_identity(a) }
193194

195+
#[wasm_bindgen]
196+
pub fn char_none() -> Option<char> { None }
197+
#[wasm_bindgen]
198+
pub fn char_letter() -> Option<char> { Some('a') }
199+
#[wasm_bindgen]
200+
pub fn char_face() -> Option<char> { Some('😀') }
201+
#[wasm_bindgen]
202+
pub fn char_identity(a: Option<char>) -> Option<char> { char_js_identity(a) }
203+
194204
#[wasm_bindgen_test]
195205
fn works() {
196206
test_works();

0 commit comments

Comments
 (0)