Skip to content

Commit 6f78a36

Browse files
committed
Add support for optional numbers
1 parent 595bb78 commit 6f78a36

25 files changed

+799
-55
lines changed

crates/cli-support/src/descriptor.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,27 @@ impl Descriptor {
154154
}
155155
}
156156

157-
pub fn get_64bit(&self) -> Option<bool> {
157+
pub fn is_primitive(&self) -> bool {
158+
match *self {
159+
Descriptor::I32
160+
| Descriptor::U32
161+
| Descriptor::F32
162+
| Descriptor::F64 => true,
163+
_ => return false,
164+
}
165+
}
166+
167+
pub fn is_as_u32(&self) -> bool {
168+
match *self {
169+
Descriptor::I8
170+
| Descriptor::U8
171+
| Descriptor::I16
172+
| Descriptor::U16 => true,
173+
_ => return false,
174+
}
175+
}
176+
177+
pub fn get_64(&self) -> Option<bool> {
158178
match *self {
159179
Descriptor::I64 => Some(true),
160180
Descriptor::U64 => Some(false),

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

Lines changed: 148 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,74 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
192192
}
193193

194194
if optional {
195-
bail!("unsupported optional argument to rust function {:?}", arg);
195+
if arg.is_primitive() {
196+
self.cx.expose_is_like_none();
197+
self.js_arguments.push((name.clone(), "number".to_string()));
198+
199+
if self.cx.config.debug {
200+
self.cx.expose_assert_num();
201+
self.prelude(&format!(
202+
"\n\
203+
if (!isLikeNone({0})) {{\n\
204+
_assertNum({0});\n\
205+
}}\n\
206+
",
207+
name
208+
));
209+
}
210+
211+
self.rust_arguments.push(format!("!isLikeNone({0})", name));
212+
self.rust_arguments.push(format!("isLikeNone({0}) ? 0 : {0}", name));
213+
return Ok(self);
214+
}
215+
216+
if arg.is_as_u32() {
217+
self.cx.expose_is_like_none();
218+
self.js_arguments.push((name.clone(), "number".to_string()));
219+
220+
if self.cx.config.debug {
221+
self.cx.expose_assert_num();
222+
self.prelude(&format!(
223+
"\n\
224+
if (!isLikeNone({0})) {{\n\
225+
_assertNum({0});\n\
226+
}}\n\
227+
",
228+
name
229+
));
230+
}
231+
232+
self.rust_arguments.push(format!("isLikeNone({0}) ? 0xFFFFFF : {0}", name));
233+
return Ok(self);
234+
}
235+
236+
if let Some(signed) = arg.get_64() {
237+
let f = if signed {
238+
self.cx.expose_int64_cvt_shim()
239+
} else {
240+
self.cx.expose_uint64_cvt_shim()
241+
};
242+
self.cx.expose_uint32_memory();
243+
self.cx.expose_global_argument_ptr()?;
244+
self.js_arguments.push((name.clone(), "BigInt".to_string()));
245+
self.prelude(&format!(
246+
"\
247+
{f}[0] = isLikeNone({name}) ? BigInt(0) : {name};\n\
248+
const low{i} = isLikeNone({name}) ? 0 : u32CvtShim[0];\n\
249+
const high{i} = isLikeNone({name}) ? 0 : u32CvtShim[1];\n\
250+
",
251+
i = i,
252+
f = f,
253+
name = name,
254+
));
255+
self.rust_arguments.push(format!("!isLikeNone({})", name));
256+
self.rust_arguments.push(format!("0"));
257+
self.rust_arguments.push(format!("low{}", i));
258+
self.rust_arguments.push(format!("high{}", i));
259+
return Ok(self);
260+
}
261+
262+
bail!("unsupported optional argument type for calling Rust function from JS: {:?}", arg);
196263
}
197264

198265
if let Some(s) = arg.rust_struct() {
@@ -240,7 +307,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
240307
return Ok(self);
241308
}
242309

243-
if let Some(signed) = arg.get_64bit() {
310+
if let Some(signed) = arg.get_64() {
244311
let f = if signed {
245312
self.cx.expose_int64_cvt_shim()
246313
} else {
@@ -252,15 +319,15 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
252319
self.prelude(&format!(
253320
"\
254321
{f}[0] = {name};\n\
255-
const lo{i} = u32CvtShim[0];\n\
256-
const hi{i} = u32CvtShim[1];\n\
322+
const low{i} = u32CvtShim[0];\n\
323+
const high{i} = u32CvtShim[1];\n\
257324
",
258325
i = i,
259326
f = f,
260327
name = name,
261328
));
262-
self.rust_arguments.push(format!("lo{}", i));
263-
self.rust_arguments.push(format!("hi{}", i));
329+
self.rust_arguments.push(format!("low{}", i));
330+
self.rust_arguments.push(format!("high{}", i));
264331
return Ok(self);
265332
}
266333

@@ -292,7 +359,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
292359
self.js_arguments.push((name.clone(), "string".to_string()));
293360
self.rust_arguments.push(format!("{}.codePointAt(0)", name))
294361
}
295-
_ => bail!("unsupported argument to rust function {:?}", arg),
362+
_ => bail!("unsupported argument type for calling Rust function from JS: {:?}", arg),
296363
}
297364
Ok(self)
298365
}
@@ -348,7 +415,78 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
348415
}
349416

350417
if optional {
351-
bail!("unsupported optional argument to rust function {:?}", ty);
418+
if ty.is_primitive() {
419+
self.ret_ty = "number".to_string();
420+
self.cx.expose_global_argument_ptr()?;
421+
self.cx.expose_uint32_memory();
422+
match ty {
423+
Descriptor::I32 => self.cx.expose_int32_memory(),
424+
Descriptor::U32 => (),
425+
Descriptor::F32 => self.cx.expose_f32_memory(),
426+
Descriptor::F64 => self.cx.expose_f64_memory(),
427+
_ => (),
428+
};
429+
self.prelude("const retptr = globalArgumentPtr();");
430+
self.rust_arguments.insert(0, "retptr".to_string());
431+
self.ret_expr = format!(
432+
"\
433+
RET;\n\
434+
const present = getUint32Memory()[retptr / 4];\n\
435+
const value = {mem}[retptr / {size} + 1];\n\
436+
return present === 0 ? undefined : value;\n\
437+
",
438+
size = match ty {
439+
Descriptor::I32 => 4,
440+
Descriptor::U32 => 4,
441+
Descriptor::F32 => 4,
442+
Descriptor::F64 => 8,
443+
_ => unreachable!(),
444+
},
445+
mem = match ty {
446+
Descriptor::I32 => "getInt32Memory()",
447+
Descriptor::U32 => "getUint32Memory()",
448+
Descriptor::F32 => "getFloat32Memory()",
449+
Descriptor::F64 => "getFloat64Memory()",
450+
_ => unreachable!(),
451+
}
452+
);
453+
return Ok(self);
454+
}
455+
456+
if ty.is_as_u32() {
457+
self.ret_ty = "number".to_string();
458+
self.ret_expr = "
459+
const ret = RET;
460+
return ret === 0xFFFFFF ? undefined : ret;
461+
".to_string();
462+
return Ok(self);
463+
}
464+
465+
if let Some(signed) = ty.get_64() {
466+
self.ret_ty = "BigInt".to_string();
467+
self.cx.expose_global_argument_ptr()?;
468+
let f = if signed {
469+
self.cx.expose_int64_memory();
470+
"getInt64Memory"
471+
} else {
472+
self.cx.expose_uint64_memory();
473+
"getUint64Memory"
474+
};
475+
self.prelude("const retptr = globalArgumentPtr();");
476+
self.rust_arguments.insert(0, "retptr".to_string());
477+
self.ret_expr = format!(
478+
"\
479+
RET;\n\
480+
const present = getUint32Memory()[retptr / 4];\n\
481+
const value = {}()[retptr / 8 + 1];\n\
482+
return present === 0 ? undefined : value;\n\
483+
",
484+
f
485+
);
486+
return Ok(self);
487+
}
488+
489+
bail!("unsupported optional return type for calling Rust function from JS: {:?}", ty);
352490
}
353491

354492
if ty.is_ref_anyref() {
@@ -374,7 +512,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
374512
return Ok(self);
375513
}
376514

377-
if let Some(signed) = ty.get_64bit() {
515+
if let Some(signed) = ty.get_64() {
378516
self.ret_ty = "BigInt".to_string();
379517
self.cx.expose_global_argument_ptr()?;
380518
let f = if signed {
@@ -405,7 +543,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
405543
self.ret_ty = "string".to_string();
406544
self.ret_expr = format!("return String.fromCodePoint(RET);")
407545
}
408-
_ => bail!("unsupported return from Rust to JS {:?}", ty),
546+
_ => bail!("unsupported return type for calling Rust function from JS: {:?}", ty),
409547
}
410548
Ok(self)
411549
}

0 commit comments

Comments
 (0)