Skip to content

Commit

Permalink
Fix optional arguments in TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
dbrgn committed Apr 25, 2019
1 parent cc89109 commit 671f193
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 21 deletions.
62 changes: 43 additions & 19 deletions crates/cli-support/src/js/js2rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ use crate::descriptor::{Descriptor, Function};
use crate::js::Context;
use failure::{bail, Error};

pub struct JsArgument {
pub optional: bool,
pub name: String,
pub type_: String,
}

impl JsArgument {
fn required(name: String, type_: String) -> Self {
Self { optional: false, name, type_ }
}

fn optional(name: String, type_: String) -> Self {
Self { optional: true, name, type_ }
}
}

/// Helper struct for manufacturing a shim in JS used to translate JS types to
/// Rust, aka pass from JS back into Rust
pub struct Js2Rust<'a, 'b: 'a> {
Expand All @@ -12,7 +28,7 @@ pub struct Js2Rust<'a, 'b: 'a> {
rust_arguments: Vec<String>,

/// Arguments and their types to the JS shim.
pub js_arguments: Vec<(String, String)>,
pub js_arguments: Vec<JsArgument>,

/// Conversions that happen before we invoke the wasm function, such as
/// converting a string to a ptr/length pair.
Expand Down Expand Up @@ -175,7 +191,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {

if let Some(kind) = arg.vector_kind() {
self.js_arguments
.push((name.clone(), kind.js_ty().to_string()));
.push(JsArgument::required(name.clone(), kind.js_ty().to_string()));

let func = self.cx.pass_to_wasm_function(kind)?;
let val = if optional {
Expand Down Expand Up @@ -223,7 +239,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
}

if arg.is_anyref() {
self.js_arguments.push((name.clone(), "any".to_string()));
self.js_arguments.push(JsArgument::required(name.clone(), "any".to_string()));
if self.cx.config.anyref {
if optional {
self.cx.expose_add_to_anyref_table()?;
Expand Down Expand Up @@ -252,7 +268,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {

if arg.is_wasm_native() {
self.js_arguments
.push((name.clone(), "number | undefined".to_string()));
.push(JsArgument::optional(name.clone(), "number".to_string()));

if self.cx.config.debug {
self.cx.expose_assert_num();
Expand All @@ -274,7 +290,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {

if arg.is_abi_as_u32() {
self.js_arguments
.push((name.clone(), "number | undefined".to_string()));
.push(JsArgument::optional(name.clone(), "number".to_string()));

if self.cx.config.debug {
self.cx.expose_assert_num();
Expand All @@ -301,7 +317,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
};
self.cx.expose_uint32_memory();
self.js_arguments
.push((name.clone(), "BigInt | undefined".to_string()));
.push(JsArgument::optional(name.clone(), "BigInt".to_string()));
self.prelude(&format!(
"
{f}[0] = isLikeNone({name}) ? BigInt(0) : {name};
Expand All @@ -322,7 +338,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
match *arg {
Descriptor::Boolean => {
self.js_arguments
.push((name.clone(), "boolean | undefined".to_string()));
.push(JsArgument::optional(name.clone(), "boolean".to_string()));
if self.cx.config.debug {
self.cx.expose_assert_bool();
self.prelude(&format!(
Expand All @@ -339,21 +355,21 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
}
Descriptor::Char => {
self.js_arguments
.push((name.clone(), "string | undefined".to_string()));
.push(JsArgument::optional(name.clone(), "string".to_string()));
self.rust_arguments.push(format!(
"isLikeNone({0}) ? 0xFFFFFF : {0}.codePointAt(0)",
name
));
}
Descriptor::Enum { hole } => {
self.js_arguments
.push((name.clone(), "number | undefined".to_string()));
.push(JsArgument::optional(name.clone(), "number".to_string()));
self.rust_arguments
.push(format!("isLikeNone({0}) ? {1} : {0}", name, hole));
}
Descriptor::RustStruct(ref s) => {
self.js_arguments
.push((name.clone(), format!("{} | undefined", s)));
.push(JsArgument::optional(name.clone(), s.to_string()));
self.prelude(&format!("let ptr{} = 0;", i));
self.prelude(&format!("if (!isLikeNone({0})) {{", name));
self.assert_class(&name, s);
Expand All @@ -373,7 +389,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
}

if let Some(s) = arg.rust_struct() {
self.js_arguments.push((name.clone(), s.to_string()));
self.js_arguments.push(JsArgument::required(name.clone(), s.to_string()));
self.assert_class(&name, s);
self.assert_not_moved(&name);
if arg.is_by_ref() {
Expand All @@ -387,7 +403,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
}

if arg.number().is_some() {
self.js_arguments.push((name.clone(), "number".to_string()));
self.js_arguments.push(JsArgument::required(name.clone(), "number".to_string()));

if self.cx.config.debug {
self.cx.expose_assert_num();
Expand All @@ -405,7 +421,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.cx.expose_uint64_cvt_shim()
};
self.cx.expose_uint32_memory();
self.js_arguments.push((name.clone(), "BigInt".to_string()));
self.js_arguments.push(JsArgument::required(name.clone(), "BigInt".to_string()));
self.prelude(&format!(
"
{f}[0] = {name};
Expand All @@ -422,7 +438,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
}

if arg.is_ref_anyref() {
self.js_arguments.push((name.clone(), "any".to_string()));
self.js_arguments.push(JsArgument::required(name.clone(), "any".to_string()));
if self.cx.config.anyref {
self.anyref_args.push((self.rust_arguments.len(), false));
self.rust_arguments.push(name);
Expand All @@ -442,7 +458,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
match *arg {
Descriptor::Boolean => {
self.js_arguments
.push((name.clone(), "boolean".to_string()));
.push(JsArgument::required(name.clone(), "boolean".to_string()));
if self.cx.config.debug {
self.cx.expose_assert_bool();
self.prelude(&format!(
Expand All @@ -455,7 +471,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.rust_arguments.push(format!("{}", name));
}
Descriptor::Char => {
self.js_arguments.push((name.clone(), "string".to_string()));
self.js_arguments.push(JsArgument::required(name.clone(), "string".to_string()));
self.rust_arguments.push(format!("{}.codePointAt(0)", name))
}
_ => bail!(
Expand Down Expand Up @@ -737,7 +753,11 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
let mut ret: String = self
.js_arguments
.iter()
.map(|a| format!("@param {{{}}} {}\n", a.1, a.0))
.map(|a| if a.optional {
format!("@param {{{} | undefined}} {}\n", a.type_, a.name)
} else {
format!("@param {{{}}} {}\n", a.type_, a.name)
})
.collect();
ret.push_str(&format!("@returns {{{}}}", self.ret_ty));
ret
Expand All @@ -761,7 +781,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
let js_args = self
.js_arguments
.iter()
.map(|s| &s.0[..])
.map(|s| &s.name[..])
.collect::<Vec<_>>()
.join(", ");
let mut js = format!("{}({}) {{\n", prefix, js_args);
Expand Down Expand Up @@ -790,7 +810,11 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
let ts_args = self
.js_arguments
.iter()
.map(|s| format!("{}: {}", s.0, s.1))
.map(|s| if s.optional {
format!("{}?: {}", s.name, s.type_)
} else {
format!("{}: {}", s.name, s.type_)
})
.collect::<Vec<_>>()
.join(", ");
let mut ts = if prefix.is_empty() {
Expand Down
2 changes: 1 addition & 1 deletion crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2878,7 +2878,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
"\n {}{}: {};",
if field.readonly { "readonly " } else { "" },
field.name,
&cx.js_arguments[0].1
&cx.js_arguments[0].type_
));
cx.finish("", &format!("wasm.{}", wasm_setter), setter).0
};
Expand Down
2 changes: 1 addition & 1 deletion crates/typescript-tests/src/opt_args_and_ret.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import * as wbg from '../pkg/typescript_tests';

const opt_fn: (a: number | undefined) => number | undefined = wbg.opt_fn;
const opt_fn: (a?: number) => number | undefined = wbg.opt_fn;

0 comments on commit 671f193

Please sign in to comment.