Migrate `wasm-bindgen` to using `walrus`
This commit moves `wasm-bindgen` the CLI tool from internally using
`parity-wasm` for wasm parsing/serialization to instead use `walrus`.
The `walrus` crate is something we've been working on recently with an
aim to replace the usage of `parity-wasm` in `wasm-bindgen` to make the
current CLI tool more maintainable as well as more future-proof.
The `walrus` crate provides a much nicer AST to work with as well as a
structured `Module`, whereas `parity-wasm` provides a very raw interface
to the wasm module which isn't really appropriate for our use case. The
many transformations and tweaks that wasm-bindgen does have a huge
amount of ad-hoc index management to carefully craft a final wasm
binary, but this is all entirely taken care for us with the `walrus`
crate.
Additionally, `wasm-bindgen` will ingest and rewrite the wasm file,
often changing the binary offsets of functions. Eventually with DWARF
debug information we'll need to be sure to preserve the debug
information throughout the transformations that `wasm-bindgen` does
today. This is practically impossible to do with the `parity-wasm`
architecture, but `walrus` was designed from the get-go to solve this
problem transparently in the `walrus` crate itself. (it doesn't today,
but this is planned work)
It is the intention that this does not end up regressing any
`wasm-bindgen` use cases, neither in functionality or in speed. As a
large change and refactoring, however, it's likely that at least
something will arise! We'll want to continue to remain vigilant to any
issues that come up with this commit.
Note that the `gc` crate has been deleted as part of this change, as the
`gc` crate is no longer necessary since `walrus` does it automatically.
Additionally the `gc` crate was one of the main problems with preserving
debug information as it often deletes wasm items!
Finally, this also starts moving crates to the 2018 edition where
necessary since `walrus` requires the 2018 edition, and in general it's
more pleasant to work within the 2018 edition!
2019-01-31 09:54:23 -08:00
|
|
|
use crate::descriptor::{Descriptor, Function};
|
|
|
|
use crate::js::Context;
|
|
|
|
use failure::{bail, Error};
|
2018-04-14 09:13:07 -07:00
|
|
|
|
2019-04-25 18:44:28 +02:00
|
|
|
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_ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-22 12:34:41 -05:00
|
|
|
/// Helper struct for manufacturing a shim in JS used to translate JS types to
|
2018-04-14 09:13:07 -07:00
|
|
|
/// Rust, aka pass from JS back into Rust
|
|
|
|
pub struct Js2Rust<'a, 'b: 'a> {
|
|
|
|
cx: &'a mut Context<'b>,
|
|
|
|
|
|
|
|
/// Arguments passed to the invocation of the wasm function, aka things that
|
|
|
|
/// are only numbers.
|
|
|
|
rust_arguments: Vec<String>,
|
|
|
|
|
|
|
|
/// Arguments and their types to the JS shim.
|
2019-04-25 18:44:28 +02:00
|
|
|
pub js_arguments: Vec<JsArgument>,
|
2018-04-14 09:13:07 -07:00
|
|
|
|
|
|
|
/// Conversions that happen before we invoke the wasm function, such as
|
|
|
|
/// converting a string to a ptr/length pair.
|
|
|
|
prelude: String,
|
|
|
|
|
|
|
|
/// "Destructors" or cleanup that must happen after the wasm function
|
|
|
|
/// finishes. This is scheduled in a `finally` block.
|
|
|
|
finally: String,
|
|
|
|
|
|
|
|
/// Index of the next argument for unique name generation purposes.
|
|
|
|
arg_idx: usize,
|
|
|
|
|
|
|
|
/// Typescript expression representing the type of the return value of this
|
|
|
|
/// function.
|
|
|
|
ret_ty: String,
|
|
|
|
|
|
|
|
/// Expression used to generate the return value. The string "RET" in this
|
|
|
|
/// expression is replaced with the actual wasm invocation eventually.
|
|
|
|
ret_expr: String,
|
|
|
|
|
|
|
|
/// Name of the JS shim/function that we're generating, primarily for
|
|
|
|
/// TypeScript right now.
|
|
|
|
js_name: String,
|
2018-09-21 15:45:31 -07:00
|
|
|
|
|
|
|
/// whether or not this generated function body will act like a constructor,
|
|
|
|
/// meaning it doesn't actually return something but rather assigns to
|
|
|
|
/// `this`
|
|
|
|
///
|
|
|
|
/// The string value here is the class that this should be a constructor
|
|
|
|
/// for.
|
|
|
|
constructor: Option<String>,
|
2018-10-18 08:43:36 -07:00
|
|
|
|
|
|
|
/// metadata for anyref transformations
|
|
|
|
anyref_args: Vec<(usize, bool)>,
|
|
|
|
ret_anyref: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum ExportedShim<'a> {
|
|
|
|
Named(&'a str),
|
|
|
|
TableElement(&'a mut u32),
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b> Js2Rust<'a, 'b> {
|
|
|
|
pub fn new(js_name: &str, cx: &'a mut Context<'b>) -> Js2Rust<'a, 'b> {
|
|
|
|
Js2Rust {
|
|
|
|
cx,
|
|
|
|
js_name: js_name.to_string(),
|
|
|
|
rust_arguments: Vec::new(),
|
|
|
|
js_arguments: Vec::new(),
|
|
|
|
prelude: String::new(),
|
|
|
|
finally: String::new(),
|
|
|
|
arg_idx: 0,
|
|
|
|
ret_ty: String::new(),
|
|
|
|
ret_expr: String::new(),
|
2018-09-21 15:45:31 -07:00
|
|
|
constructor: None,
|
2018-10-18 08:43:36 -07:00
|
|
|
anyref_args: Vec::new(),
|
|
|
|
ret_anyref: false,
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generates all bindings necessary for the signature in `Function`,
|
|
|
|
/// creating necessary argument conversions and return value processing.
|
2019-03-14 08:46:42 -03:00
|
|
|
pub fn process<'c, I>(
|
|
|
|
&mut self,
|
|
|
|
function: &Function,
|
|
|
|
opt_arg_names: I,
|
|
|
|
) -> Result<&mut Self, Error>
|
|
|
|
where
|
|
|
|
I: Into<Option<&'c Vec<String>>>,
|
|
|
|
{
|
|
|
|
if let Some(arg_names) = opt_arg_names.into() {
|
|
|
|
for (arg, arg_name) in function.arguments.iter().zip(arg_names) {
|
2019-03-14 12:21:41 -03:00
|
|
|
self.argument(arg, arg_name.as_str())?;
|
2019-03-14 08:46:42 -03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for arg in function.arguments.iter() {
|
|
|
|
self.argument(arg, None)?;
|
|
|
|
}
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
2018-04-25 11:42:22 -07:00
|
|
|
self.ret(&function.ret)?;
|
|
|
|
Ok(self)
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
2018-09-21 15:45:31 -07:00
|
|
|
pub fn constructor(&mut self, class: Option<&str>) -> &mut Self {
|
|
|
|
self.constructor = class.map(|s| s.to_string());
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-04-14 09:13:07 -07:00
|
|
|
/// Flag this shim as a method call into Rust, so the first Rust argument
|
|
|
|
/// passed should be `this.ptr`.
|
2019-04-30 10:26:03 -03:00
|
|
|
pub fn method(&mut self, consumed: bool) -> &mut Self {
|
|
|
|
if self.cx.config.debug {
|
|
|
|
self.prelude(
|
|
|
|
"if (this.ptr === 0) {
|
|
|
|
throw new Error('Attempt to use a moved value');
|
|
|
|
}",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if consumed {
|
|
|
|
self.prelude(
|
|
|
|
"\
|
|
|
|
const ptr = this.ptr;\n\
|
|
|
|
this.ptr = 0;\n\
|
|
|
|
",
|
|
|
|
);
|
|
|
|
self.rust_arguments.insert(0, "ptr".to_string());
|
|
|
|
} else {
|
|
|
|
self.rust_arguments.insert(0, "this.ptr".to_string());
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add extra processing to the prelude of this shim.
|
|
|
|
pub fn prelude(&mut self, s: &str) -> &mut Self {
|
2018-04-14 09:30:38 -07:00
|
|
|
for line in s.lines() {
|
|
|
|
self.prelude.push_str(line);
|
|
|
|
self.prelude.push_str("\n");
|
|
|
|
}
|
2018-04-14 09:13:07 -07:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add extra processing to the finally block of this shim.
|
|
|
|
pub fn finally(&mut self, s: &str) -> &mut Self {
|
2018-04-14 09:30:38 -07:00
|
|
|
for line in s.lines() {
|
|
|
|
self.finally.push_str(line);
|
|
|
|
self.finally.push_str("\n");
|
|
|
|
}
|
2018-04-14 09:13:07 -07:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add an Rust argument to be passed manually.
|
|
|
|
pub fn rust_argument(&mut self, s: &str) -> &mut Self {
|
|
|
|
self.rust_arguments.push(s.to_string());
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2019-03-14 12:21:41 -03:00
|
|
|
fn abi_arg(&mut self, opt_arg_name: Option<&str>) -> String {
|
2019-03-14 08:46:42 -03:00
|
|
|
let ret = if let Some(x) = opt_arg_name {
|
2019-03-14 12:21:41 -03:00
|
|
|
x.into()
|
2019-03-14 08:46:42 -03:00
|
|
|
} else {
|
|
|
|
format!("arg{}", self.arg_idx)
|
|
|
|
};
|
2018-05-02 21:03:50 -07:00
|
|
|
self.arg_idx += 1;
|
2019-03-14 08:46:42 -03:00
|
|
|
ret
|
2018-05-02 21:03:50 -07:00
|
|
|
}
|
|
|
|
|
2019-03-14 12:21:41 -03:00
|
|
|
pub fn argument<'c, I>(&mut self, arg: &Descriptor, opt_arg_name: I) -> Result<&mut Self, Error>
|
2019-03-14 08:46:42 -03:00
|
|
|
where
|
2019-03-14 12:21:41 -03:00
|
|
|
I: Into<Option<&'c str>>,
|
2019-03-14 08:46:42 -03:00
|
|
|
{
|
2018-04-14 09:13:07 -07:00
|
|
|
let i = self.arg_idx;
|
2019-03-14 08:46:42 -03:00
|
|
|
let name = self.abi_arg(opt_arg_name.into());
|
2018-04-14 09:13:07 -07:00
|
|
|
|
2018-07-19 14:44:23 -05:00
|
|
|
let (arg, optional) = match arg {
|
|
|
|
Descriptor::Option(t) => (&**t, true),
|
|
|
|
_ => (arg, false),
|
|
|
|
};
|
|
|
|
|
2018-04-14 09:13:07 -07:00
|
|
|
if let Some(kind) = arg.vector_kind() {
|
2018-06-27 22:42:34 -07:00
|
|
|
self.js_arguments
|
2019-04-25 18:44:28 +02:00
|
|
|
.push(JsArgument::required(name.clone(), kind.js_ty().to_string()));
|
2018-04-14 09:13:07 -07:00
|
|
|
|
2018-04-25 11:42:22 -07:00
|
|
|
let func = self.cx.pass_to_wasm_function(kind)?;
|
2018-07-19 14:44:23 -05:00
|
|
|
let val = if optional {
|
|
|
|
self.cx.expose_is_like_none();
|
|
|
|
format!("isLikeNone({}) ? [0, 0] : {}({})", name, func, name)
|
|
|
|
} else {
|
|
|
|
format!("{}({})", func, name)
|
|
|
|
};
|
2018-06-27 22:42:34 -07:00
|
|
|
self.prelude(&format!(
|
2018-11-13 08:10:05 -08:00
|
|
|
"const ptr{i} = {val};\nconst len{i} = WASM_VECTOR_LEN;",
|
2018-06-27 22:42:34 -07:00
|
|
|
i = i,
|
2018-07-19 14:44:23 -05:00
|
|
|
val = val,
|
2018-06-27 22:42:34 -07:00
|
|
|
));
|
2018-09-24 13:49:12 -07:00
|
|
|
if arg.is_by_ref() || arg.is_clamped_by_ref() {
|
2018-07-19 14:44:23 -05:00
|
|
|
if optional {
|
|
|
|
bail!("optional slices aren't currently supported");
|
|
|
|
}
|
2018-05-01 10:06:35 -07:00
|
|
|
if arg.is_mut_ref() {
|
|
|
|
let get = self.cx.memview_function(kind);
|
2018-06-27 22:42:34 -07:00
|
|
|
self.finally(&format!(
|
|
|
|
"\
|
|
|
|
{arg}.set({get}().subarray(\
|
|
|
|
ptr{i} / {size}, \
|
|
|
|
ptr{i} / {size} + len{i}\
|
|
|
|
));\n\
|
|
|
|
",
|
|
|
|
i = i,
|
|
|
|
arg = name,
|
|
|
|
get = get,
|
|
|
|
size = kind.size()
|
|
|
|
));
|
2018-05-01 10:06:35 -07:00
|
|
|
}
|
2018-06-27 22:42:34 -07:00
|
|
|
self.finally(&format!(
|
|
|
|
"\
|
|
|
|
wasm.__wbindgen_free(ptr{i}, len{i} * {size});\n\
|
|
|
|
",
|
|
|
|
i = i,
|
|
|
|
size = kind.size()
|
|
|
|
));
|
2018-04-25 11:42:22 -07:00
|
|
|
self.cx.require_internal_export("__wbindgen_free")?;
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
self.rust_arguments.push(format!("ptr{}", i));
|
2018-05-02 21:03:50 -07:00
|
|
|
self.rust_arguments.push(format!("len{}", i));
|
2018-06-27 22:42:34 -07:00
|
|
|
return Ok(self);
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
2018-07-19 14:44:23 -05:00
|
|
|
if arg.is_anyref() {
|
2019-04-25 18:44:28 +02:00
|
|
|
self.js_arguments.push(JsArgument::required(name.clone(), "any".to_string()));
|
2018-10-18 08:43:36 -07:00
|
|
|
if self.cx.config.anyref {
|
|
|
|
if optional {
|
|
|
|
self.cx.expose_add_to_anyref_table()?;
|
|
|
|
self.cx.expose_is_like_none();
|
|
|
|
self.rust_arguments
|
|
|
|
.push(format!("isLikeNone({0}) ? 0 : addToAnyrefTable({0})", name));
|
|
|
|
} else {
|
|
|
|
self.anyref_args.push((self.rust_arguments.len(), true));
|
|
|
|
self.rust_arguments.push(name);
|
|
|
|
}
|
2018-07-19 14:44:23 -05:00
|
|
|
} else {
|
2018-10-18 08:43:36 -07:00
|
|
|
self.cx.expose_add_heap_object();
|
|
|
|
if optional {
|
|
|
|
self.cx.expose_is_like_none();
|
|
|
|
self.rust_arguments
|
|
|
|
.push(format!("isLikeNone({0}) ? 0 : addHeapObject({0})", name));
|
|
|
|
} else {
|
|
|
|
self.rust_arguments.push(format!("addHeapObject({})", name));
|
|
|
|
}
|
2018-07-19 14:44:23 -05:00
|
|
|
}
|
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
if optional {
|
2019-03-26 18:27:49 +00:00
|
|
|
self.cx.expose_is_like_none();
|
|
|
|
|
2018-08-03 18:40:55 +03:00
|
|
|
if arg.is_wasm_native() {
|
2019-01-23 20:23:47 +09:00
|
|
|
self.js_arguments
|
2019-04-25 18:44:28 +02:00
|
|
|
.push(JsArgument::optional(name.clone(), "number".to_string()));
|
2018-08-03 16:28:35 +03:00
|
|
|
|
|
|
|
if self.cx.config.debug {
|
|
|
|
self.cx.expose_assert_num();
|
|
|
|
self.prelude(&format!(
|
2018-08-03 18:40:55 +03:00
|
|
|
"
|
|
|
|
if (!isLikeNone({0})) {{
|
|
|
|
_assertNum({0});
|
|
|
|
}}
|
2018-08-03 16:28:35 +03:00
|
|
|
",
|
|
|
|
name
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
self.rust_arguments.push(format!("!isLikeNone({0})", name));
|
2018-09-26 08:26:00 -07:00
|
|
|
self.rust_arguments
|
|
|
|
.push(format!("isLikeNone({0}) ? 0 : {0}", name));
|
2018-08-03 16:28:35 +03:00
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
2018-08-03 18:40:55 +03:00
|
|
|
if arg.is_abi_as_u32() {
|
2019-01-23 20:23:47 +09:00
|
|
|
self.js_arguments
|
2019-04-25 18:44:28 +02:00
|
|
|
.push(JsArgument::optional(name.clone(), "number".to_string()));
|
2018-08-03 16:28:35 +03:00
|
|
|
|
|
|
|
if self.cx.config.debug {
|
|
|
|
self.cx.expose_assert_num();
|
|
|
|
self.prelude(&format!(
|
2018-08-03 18:40:55 +03:00
|
|
|
"
|
|
|
|
if (!isLikeNone({0})) {{
|
|
|
|
_assertNum({0});
|
|
|
|
}}
|
2018-08-03 16:28:35 +03:00
|
|
|
",
|
|
|
|
name
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2018-09-26 08:26:00 -07:00
|
|
|
self.rust_arguments
|
|
|
|
.push(format!("isLikeNone({0}) ? 0xFFFFFF : {0}", name));
|
2018-08-03 16:28:35 +03:00
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(signed) = arg.get_64() {
|
|
|
|
let f = if signed {
|
|
|
|
self.cx.expose_int64_cvt_shim()
|
|
|
|
} else {
|
|
|
|
self.cx.expose_uint64_cvt_shim()
|
|
|
|
};
|
|
|
|
self.cx.expose_uint32_memory();
|
2019-01-23 20:23:47 +09:00
|
|
|
self.js_arguments
|
2019-04-25 18:44:28 +02:00
|
|
|
.push(JsArgument::optional(name.clone(), "BigInt".to_string()));
|
2018-08-03 16:28:35 +03:00
|
|
|
self.prelude(&format!(
|
2018-08-03 18:40:55 +03:00
|
|
|
"
|
|
|
|
{f}[0] = isLikeNone({name}) ? BigInt(0) : {name};
|
|
|
|
const low{i} = isLikeNone({name}) ? 0 : u32CvtShim[0];
|
|
|
|
const high{i} = isLikeNone({name}) ? 0 : u32CvtShim[1];
|
2018-08-03 16:28:35 +03:00
|
|
|
",
|
|
|
|
i = i,
|
|
|
|
f = f,
|
|
|
|
name = name,
|
|
|
|
));
|
|
|
|
self.rust_arguments.push(format!("!isLikeNone({})", name));
|
|
|
|
self.rust_arguments.push(format!("0"));
|
|
|
|
self.rust_arguments.push(format!("low{}", i));
|
|
|
|
self.rust_arguments.push(format!("high{}", i));
|
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
2018-08-03 19:07:12 +03:00
|
|
|
match *arg {
|
|
|
|
Descriptor::Boolean => {
|
2018-09-26 08:26:00 -07:00
|
|
|
self.js_arguments
|
2019-04-25 18:44:28 +02:00
|
|
|
.push(JsArgument::optional(name.clone(), "boolean".to_string()));
|
2018-08-03 19:07:12 +03:00
|
|
|
if self.cx.config.debug {
|
|
|
|
self.cx.expose_assert_bool();
|
|
|
|
self.prelude(&format!(
|
|
|
|
"
|
|
|
|
if (!isLikeNone({0})) {{
|
|
|
|
_assertBoolean({0});
|
|
|
|
}}
|
|
|
|
",
|
|
|
|
name,
|
|
|
|
));
|
|
|
|
}
|
2018-09-26 08:26:00 -07:00
|
|
|
self.rust_arguments
|
|
|
|
.push(format!("isLikeNone({0}) ? 0xFFFFFF : {0} ? 1 : 0", name));
|
|
|
|
}
|
2018-08-03 20:45:57 +03:00
|
|
|
Descriptor::Char => {
|
2019-01-23 20:23:47 +09:00
|
|
|
self.js_arguments
|
2019-04-25 18:44:28 +02:00
|
|
|
.push(JsArgument::optional(name.clone(), "string".to_string()));
|
2019-04-16 10:52:27 -07:00
|
|
|
self.rust_arguments.push(format!(
|
|
|
|
"isLikeNone({0}) ? 0xFFFFFF : {0}.codePointAt(0)",
|
|
|
|
name
|
|
|
|
));
|
2018-09-26 08:26:00 -07:00
|
|
|
}
|
2019-01-28 14:12:02 -08:00
|
|
|
Descriptor::Enum { hole } => {
|
|
|
|
self.js_arguments
|
2019-04-25 18:44:28 +02:00
|
|
|
.push(JsArgument::optional(name.clone(), "number".to_string()));
|
2019-01-28 14:12:02 -08:00
|
|
|
self.rust_arguments
|
|
|
|
.push(format!("isLikeNone({0}) ? {1} : {0}", name, hole));
|
|
|
|
}
|
2019-02-19 09:08:37 -08:00
|
|
|
Descriptor::RustStruct(ref s) => {
|
2019-03-26 08:00:16 -07:00
|
|
|
self.js_arguments
|
2019-04-25 18:44:28 +02:00
|
|
|
.push(JsArgument::optional(name.clone(), s.to_string()));
|
2019-02-19 09:08:37 -08:00
|
|
|
self.prelude(&format!("let ptr{} = 0;", i));
|
2019-03-26 18:27:49 +00:00
|
|
|
self.prelude(&format!("if (!isLikeNone({0})) {{", name));
|
2019-02-19 09:08:37 -08:00
|
|
|
self.assert_class(&name, s);
|
|
|
|
self.assert_not_moved(&name);
|
|
|
|
self.prelude(&format!("ptr{} = {}.ptr;", i, name));
|
|
|
|
self.prelude(&format!("{}.ptr = 0;", name));
|
|
|
|
self.prelude("}");
|
|
|
|
self.rust_arguments.push(format!("ptr{}", i));
|
|
|
|
}
|
2018-09-26 08:26:00 -07:00
|
|
|
_ => bail!(
|
|
|
|
"unsupported optional argument type for calling Rust function from JS: {:?}",
|
|
|
|
arg
|
|
|
|
),
|
2019-03-26 18:27:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Ok(self);
|
2018-07-19 14:44:23 -05:00
|
|
|
}
|
|
|
|
|
2018-04-14 09:13:07 -07:00
|
|
|
if let Some(s) = arg.rust_struct() {
|
2019-04-25 18:44:28 +02:00
|
|
|
self.js_arguments.push(JsArgument::required(name.clone(), s.to_string()));
|
2019-02-19 09:08:37 -08:00
|
|
|
self.assert_class(&name, s);
|
|
|
|
self.assert_not_moved(&name);
|
2018-04-14 09:13:07 -07:00
|
|
|
if arg.is_by_ref() {
|
|
|
|
self.rust_arguments.push(format!("{}.ptr", name));
|
|
|
|
} else {
|
2019-02-19 09:08:37 -08:00
|
|
|
self.prelude(&format!("const ptr{} = {}.ptr;", i, name));
|
|
|
|
self.prelude(&format!("{}.ptr = 0;", name));
|
2018-04-14 09:13:07 -07:00
|
|
|
self.rust_arguments.push(format!("ptr{}", i));
|
|
|
|
}
|
2018-06-27 22:42:34 -07:00
|
|
|
return Ok(self);
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
2019-03-26 17:29:53 -07:00
|
|
|
if arg.number().is_some() {
|
2019-04-25 18:44:28 +02:00
|
|
|
self.js_arguments.push(JsArgument::required(name.clone(), "number".to_string()));
|
2018-04-14 09:13:07 -07:00
|
|
|
|
|
|
|
if self.cx.config.debug {
|
|
|
|
self.cx.expose_assert_num();
|
2018-04-14 09:30:38 -07:00
|
|
|
self.prelude(&format!("_assertNum({});", name));
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
self.rust_arguments.push(name);
|
2018-06-27 22:42:34 -07:00
|
|
|
return Ok(self);
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
2018-08-03 16:28:35 +03:00
|
|
|
if let Some(signed) = arg.get_64() {
|
2018-05-05 14:10:25 -07:00
|
|
|
let f = if signed {
|
|
|
|
self.cx.expose_int64_cvt_shim()
|
|
|
|
} else {
|
|
|
|
self.cx.expose_uint64_cvt_shim()
|
|
|
|
};
|
|
|
|
self.cx.expose_uint32_memory();
|
2019-04-25 18:44:28 +02:00
|
|
|
self.js_arguments.push(JsArgument::required(name.clone(), "BigInt".to_string()));
|
2018-06-27 22:42:34 -07:00
|
|
|
self.prelude(&format!(
|
2018-08-03 18:40:55 +03:00
|
|
|
"
|
|
|
|
{f}[0] = {name};
|
|
|
|
const low{i} = u32CvtShim[0];
|
|
|
|
const high{i} = u32CvtShim[1];
|
2018-06-27 22:42:34 -07:00
|
|
|
",
|
2018-05-05 14:10:25 -07:00
|
|
|
i = i,
|
|
|
|
f = f,
|
|
|
|
name = name,
|
|
|
|
));
|
2018-08-03 16:28:35 +03:00
|
|
|
self.rust_arguments.push(format!("low{}", i));
|
|
|
|
self.rust_arguments.push(format!("high{}", i));
|
2018-06-27 22:42:34 -07:00
|
|
|
return Ok(self);
|
2018-05-05 14:10:25 -07:00
|
|
|
}
|
|
|
|
|
2018-04-14 09:13:07 -07:00
|
|
|
if arg.is_ref_anyref() {
|
2019-04-25 18:44:28 +02:00
|
|
|
self.js_arguments.push(JsArgument::required(name.clone(), "any".to_string()));
|
2018-10-18 08:43:36 -07:00
|
|
|
if self.cx.config.anyref {
|
|
|
|
self.anyref_args.push((self.rust_arguments.len(), false));
|
|
|
|
self.rust_arguments.push(name);
|
|
|
|
} else {
|
|
|
|
// the "stack-ful" nature means that we're always popping from the
|
|
|
|
// stack, and make sure that we actually clear our reference to
|
|
|
|
// allow stale values to get GC'd
|
|
|
|
self.cx.expose_borrowed_objects();
|
|
|
|
self.cx.expose_global_stack_pointer();
|
|
|
|
self.finally("heap[stack_pointer++] = undefined;");
|
|
|
|
self.rust_arguments
|
|
|
|
.push(format!("addBorrowedObject({})", name));
|
|
|
|
}
|
2018-06-27 22:42:34 -07:00
|
|
|
return Ok(self);
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
match *arg {
|
|
|
|
Descriptor::Boolean => {
|
2018-06-27 22:42:34 -07:00
|
|
|
self.js_arguments
|
2019-04-25 18:44:28 +02:00
|
|
|
.push(JsArgument::required(name.clone(), "boolean".to_string()));
|
2018-04-14 09:13:07 -07:00
|
|
|
if self.cx.config.debug {
|
|
|
|
self.cx.expose_assert_bool();
|
2018-06-27 22:42:34 -07:00
|
|
|
self.prelude(&format!(
|
|
|
|
"\
|
|
|
|
_assertBoolean({name});\n\
|
|
|
|
",
|
|
|
|
name = name
|
|
|
|
));
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
2018-11-08 13:06:03 -08:00
|
|
|
self.rust_arguments.push(format!("{}", name));
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
2018-05-22 12:34:41 -05:00
|
|
|
Descriptor::Char => {
|
2019-04-25 18:44:28 +02:00
|
|
|
self.js_arguments.push(JsArgument::required(name.clone(), "string".to_string()));
|
2018-05-22 12:34:41 -05:00
|
|
|
self.rust_arguments.push(format!("{}.codePointAt(0)", name))
|
2018-06-27 22:42:34 -07:00
|
|
|
}
|
2018-09-26 08:26:00 -07:00
|
|
|
_ => bail!(
|
|
|
|
"unsupported argument type for calling Rust function from JS: {:?}",
|
|
|
|
arg
|
|
|
|
),
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
2018-04-25 11:42:22 -07:00
|
|
|
Ok(self)
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
2018-09-17 18:26:45 -07:00
|
|
|
pub fn ret(&mut self, ty: &Descriptor) -> Result<&mut Self, Error> {
|
2018-09-21 15:45:31 -07:00
|
|
|
if let Some(name) = ty.rust_struct() {
|
|
|
|
match &self.constructor {
|
|
|
|
Some(class) if class == name => {
|
|
|
|
self.ret_expr = format!("this.ptr = RET;");
|
|
|
|
if self.cx.config.weak_refs {
|
2018-09-26 08:26:00 -07:00
|
|
|
self.ret_expr.push_str(&format!(
|
|
|
|
"\
|
2019-05-26 09:30:33 -05:00
|
|
|
{}FinalizationGroup.register(this, this.ptr, this.ptr);
|
|
|
|
",
|
2018-09-26 08:26:00 -07:00
|
|
|
name
|
|
|
|
));
|
2018-09-21 15:45:31 -07:00
|
|
|
}
|
|
|
|
}
|
2018-09-26 08:26:00 -07:00
|
|
|
Some(class) => bail!("constructor for `{}` cannot return `{}`", class, name),
|
2018-09-21 15:45:31 -07:00
|
|
|
None => {
|
|
|
|
self.ret_ty = name.to_string();
|
|
|
|
self.cx.require_class_wrap(name);
|
|
|
|
self.ret_expr = format!("return {name}.__wrap(RET);", name = name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.constructor.is_some() {
|
|
|
|
bail!("constructor functions must return a Rust structure")
|
|
|
|
}
|
|
|
|
|
2018-09-17 18:26:45 -07:00
|
|
|
if let Descriptor::Unit = ty {
|
|
|
|
self.ret_ty = "void".to_string();
|
|
|
|
self.ret_expr = format!("return RET;");
|
|
|
|
return Ok(self);
|
|
|
|
}
|
2018-04-14 09:13:07 -07:00
|
|
|
|
2018-07-19 14:44:23 -05:00
|
|
|
let (ty, optional) = match ty {
|
|
|
|
Descriptor::Option(t) => (&**t, true),
|
|
|
|
_ => (ty, false),
|
|
|
|
};
|
2018-04-14 09:13:07 -07:00
|
|
|
|
|
|
|
if let Some(ty) = ty.vector_kind() {
|
|
|
|
self.ret_ty = ty.js_ty().to_string();
|
2018-10-18 08:43:36 -07:00
|
|
|
let f = self.cx.expose_get_vector_from_wasm(ty)?;
|
2018-05-02 21:03:50 -07:00
|
|
|
self.cx.expose_global_argument_ptr()?;
|
|
|
|
self.cx.expose_uint32_memory();
|
2018-04-25 11:42:22 -07:00
|
|
|
self.cx.require_internal_export("__wbindgen_free")?;
|
2018-05-02 21:03:50 -07:00
|
|
|
self.prelude("const retptr = globalArgumentPtr();");
|
|
|
|
self.rust_arguments.insert(0, "retptr".to_string());
|
2018-06-27 22:42:34 -07:00
|
|
|
self.ret_expr = format!(
|
|
|
|
"\
|
|
|
|
RET;\n\
|
|
|
|
const mem = getUint32Memory();\n\
|
2018-08-07 07:53:30 -07:00
|
|
|
const rustptr = mem[retptr / 4];\n\
|
|
|
|
const rustlen = mem[retptr / 4 + 1];\n\
|
2018-07-19 14:44:23 -05:00
|
|
|
{guard}
|
2018-08-07 07:53:30 -07:00
|
|
|
const realRet = {}(rustptr, rustlen).slice();\n\
|
|
|
|
wasm.__wbindgen_free(rustptr, rustlen * {});\n\
|
2018-06-27 22:42:34 -07:00
|
|
|
return realRet;\n\
|
|
|
|
",
|
|
|
|
f,
|
2018-07-19 14:44:23 -05:00
|
|
|
ty.size(),
|
2018-09-26 08:26:00 -07:00
|
|
|
guard = if optional {
|
|
|
|
"if (rustptr === 0) return;"
|
|
|
|
} else {
|
|
|
|
""
|
|
|
|
},
|
2018-06-27 22:42:34 -07:00
|
|
|
);
|
|
|
|
return Ok(self);
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
2018-07-19 14:44:23 -05:00
|
|
|
// No need to worry about `optional` here, the abi representation means
|
|
|
|
// that `takeObject` will naturally pluck out `undefined`.
|
|
|
|
if ty.is_anyref() {
|
|
|
|
self.ret_ty = "any".to_string();
|
2018-10-18 08:43:36 -07:00
|
|
|
self.ret_expr = format!("return {};", self.cx.take_object("RET"));
|
|
|
|
self.ret_anyref = true;
|
2018-07-19 14:44:23 -05:00
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
if optional {
|
2018-08-03 18:40:55 +03:00
|
|
|
if ty.is_wasm_native() {
|
2018-11-04 10:26:20 +01:00
|
|
|
self.ret_ty = "number | undefined".to_string();
|
2018-08-03 16:28:35 +03:00
|
|
|
self.cx.expose_global_argument_ptr()?;
|
|
|
|
self.cx.expose_uint32_memory();
|
|
|
|
match ty {
|
|
|
|
Descriptor::I32 => self.cx.expose_int32_memory(),
|
|
|
|
Descriptor::U32 => (),
|
|
|
|
Descriptor::F32 => self.cx.expose_f32_memory(),
|
|
|
|
Descriptor::F64 => self.cx.expose_f64_memory(),
|
|
|
|
_ => (),
|
|
|
|
};
|
|
|
|
self.prelude("const retptr = globalArgumentPtr();");
|
|
|
|
self.rust_arguments.insert(0, "retptr".to_string());
|
|
|
|
self.ret_expr = format!(
|
2018-08-03 18:40:55 +03:00
|
|
|
"
|
|
|
|
RET;
|
|
|
|
const present = getUint32Memory()[retptr / 4];
|
|
|
|
const value = {mem}[retptr / {size} + 1];
|
|
|
|
return present === 0 ? undefined : value;
|
2018-08-03 16:28:35 +03:00
|
|
|
",
|
|
|
|
size = match ty {
|
|
|
|
Descriptor::I32 => 4,
|
|
|
|
Descriptor::U32 => 4,
|
|
|
|
Descriptor::F32 => 4,
|
|
|
|
Descriptor::F64 => 8,
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
mem = match ty {
|
|
|
|
Descriptor::I32 => "getInt32Memory()",
|
|
|
|
Descriptor::U32 => "getUint32Memory()",
|
|
|
|
Descriptor::F32 => "getFloat32Memory()",
|
|
|
|
Descriptor::F64 => "getFloat64Memory()",
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
2018-08-03 18:40:55 +03:00
|
|
|
if ty.is_abi_as_u32() {
|
2018-11-04 10:26:20 +01:00
|
|
|
self.ret_ty = "number | undefined".to_string();
|
2018-08-03 16:28:35 +03:00
|
|
|
self.ret_expr = "
|
|
|
|
const ret = RET;
|
|
|
|
return ret === 0xFFFFFF ? undefined : ret;
|
2018-11-27 12:07:59 -08:00
|
|
|
"
|
|
|
|
.to_string();
|
2018-08-03 16:28:35 +03:00
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(signed) = ty.get_64() {
|
2018-11-04 10:26:20 +01:00
|
|
|
self.ret_ty = "BigInt | undefined".to_string();
|
2018-08-03 16:28:35 +03:00
|
|
|
self.cx.expose_global_argument_ptr()?;
|
|
|
|
let f = if signed {
|
|
|
|
self.cx.expose_int64_memory();
|
|
|
|
"getInt64Memory"
|
|
|
|
} else {
|
|
|
|
self.cx.expose_uint64_memory();
|
|
|
|
"getUint64Memory"
|
|
|
|
};
|
|
|
|
self.prelude("const retptr = globalArgumentPtr();");
|
|
|
|
self.rust_arguments.insert(0, "retptr".to_string());
|
|
|
|
self.ret_expr = format!(
|
2018-08-03 18:40:55 +03:00
|
|
|
"
|
|
|
|
RET;
|
|
|
|
const present = getUint32Memory()[retptr / 4];
|
|
|
|
const value = {}()[retptr / 8 + 1];
|
|
|
|
return present === 0 ? undefined : value;
|
2018-08-03 16:28:35 +03:00
|
|
|
",
|
|
|
|
f
|
|
|
|
);
|
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
2018-08-03 19:07:12 +03:00
|
|
|
match *ty {
|
|
|
|
Descriptor::Boolean => {
|
2018-11-04 10:26:20 +01:00
|
|
|
self.ret_ty = "boolean | undefined".to_string();
|
2018-08-03 19:07:12 +03:00
|
|
|
self.ret_expr = "
|
|
|
|
const ret = RET;
|
|
|
|
return ret === 0xFFFFFF ? undefined : ret !== 0;
|
2018-11-27 12:07:59 -08:00
|
|
|
"
|
|
|
|
.to_string();
|
2018-08-03 20:45:57 +03:00
|
|
|
return Ok(self);
|
2018-09-26 08:26:00 -07:00
|
|
|
}
|
2018-08-03 20:45:57 +03:00
|
|
|
Descriptor::Char => {
|
2018-11-04 10:26:20 +01:00
|
|
|
self.ret_ty = "string | undefined".to_string();
|
2018-08-03 20:45:57 +03:00
|
|
|
self.ret_expr = "
|
2019-03-26 18:12:24 +00:00
|
|
|
const ret = RET;
|
|
|
|
return ret === 0xFFFFFF ? undefined : String.fromCodePoint(ret);
|
2019-04-16 10:52:27 -07:00
|
|
|
"
|
|
|
|
.to_string();
|
2018-08-03 19:07:12 +03:00
|
|
|
return Ok(self);
|
2018-09-26 08:26:00 -07:00
|
|
|
}
|
2019-01-28 14:12:02 -08:00
|
|
|
Descriptor::Enum { hole } => {
|
|
|
|
self.ret_ty = "number | undefined".to_string();
|
Migrate `wasm-bindgen` to using `walrus`
This commit moves `wasm-bindgen` the CLI tool from internally using
`parity-wasm` for wasm parsing/serialization to instead use `walrus`.
The `walrus` crate is something we've been working on recently with an
aim to replace the usage of `parity-wasm` in `wasm-bindgen` to make the
current CLI tool more maintainable as well as more future-proof.
The `walrus` crate provides a much nicer AST to work with as well as a
structured `Module`, whereas `parity-wasm` provides a very raw interface
to the wasm module which isn't really appropriate for our use case. The
many transformations and tweaks that wasm-bindgen does have a huge
amount of ad-hoc index management to carefully craft a final wasm
binary, but this is all entirely taken care for us with the `walrus`
crate.
Additionally, `wasm-bindgen` will ingest and rewrite the wasm file,
often changing the binary offsets of functions. Eventually with DWARF
debug information we'll need to be sure to preserve the debug
information throughout the transformations that `wasm-bindgen` does
today. This is practically impossible to do with the `parity-wasm`
architecture, but `walrus` was designed from the get-go to solve this
problem transparently in the `walrus` crate itself. (it doesn't today,
but this is planned work)
It is the intention that this does not end up regressing any
`wasm-bindgen` use cases, neither in functionality or in speed. As a
large change and refactoring, however, it's likely that at least
something will arise! We'll want to continue to remain vigilant to any
issues that come up with this commit.
Note that the `gc` crate has been deleted as part of this change, as the
`gc` crate is no longer necessary since `walrus` does it automatically.
Additionally the `gc` crate was one of the main problems with preserving
debug information as it often deletes wasm items!
Finally, this also starts moving crates to the 2018 edition where
necessary since `walrus` requires the 2018 edition, and in general it's
more pleasant to work within the 2018 edition!
2019-01-31 09:54:23 -08:00
|
|
|
self.ret_expr = format!(
|
|
|
|
"
|
2019-01-28 14:12:02 -08:00
|
|
|
const ret = RET;
|
|
|
|
return ret === {} ? undefined : ret;
|
Migrate `wasm-bindgen` to using `walrus`
This commit moves `wasm-bindgen` the CLI tool from internally using
`parity-wasm` for wasm parsing/serialization to instead use `walrus`.
The `walrus` crate is something we've been working on recently with an
aim to replace the usage of `parity-wasm` in `wasm-bindgen` to make the
current CLI tool more maintainable as well as more future-proof.
The `walrus` crate provides a much nicer AST to work with as well as a
structured `Module`, whereas `parity-wasm` provides a very raw interface
to the wasm module which isn't really appropriate for our use case. The
many transformations and tweaks that wasm-bindgen does have a huge
amount of ad-hoc index management to carefully craft a final wasm
binary, but this is all entirely taken care for us with the `walrus`
crate.
Additionally, `wasm-bindgen` will ingest and rewrite the wasm file,
often changing the binary offsets of functions. Eventually with DWARF
debug information we'll need to be sure to preserve the debug
information throughout the transformations that `wasm-bindgen` does
today. This is practically impossible to do with the `parity-wasm`
architecture, but `walrus` was designed from the get-go to solve this
problem transparently in the `walrus` crate itself. (it doesn't today,
but this is planned work)
It is the intention that this does not end up regressing any
`wasm-bindgen` use cases, neither in functionality or in speed. As a
large change and refactoring, however, it's likely that at least
something will arise! We'll want to continue to remain vigilant to any
issues that come up with this commit.
Note that the `gc` crate has been deleted as part of this change, as the
`gc` crate is no longer necessary since `walrus` does it automatically.
Additionally the `gc` crate was one of the main problems with preserving
debug information as it often deletes wasm items!
Finally, this also starts moving crates to the 2018 edition where
necessary since `walrus` requires the 2018 edition, and in general it's
more pleasant to work within the 2018 edition!
2019-01-31 09:54:23 -08:00
|
|
|
",
|
|
|
|
hole
|
|
|
|
);
|
2019-01-28 14:12:02 -08:00
|
|
|
return Ok(self);
|
|
|
|
}
|
2019-02-19 09:08:37 -08:00
|
|
|
Descriptor::RustStruct(ref name) => {
|
|
|
|
self.ret_ty = format!("{} | undefined", name);
|
|
|
|
self.cx.require_class_wrap(name);
|
2019-03-26 08:00:16 -07:00
|
|
|
self.ret_expr = format!(
|
|
|
|
"
|
2019-02-19 09:08:37 -08:00
|
|
|
const ptr = RET;
|
|
|
|
return ptr === 0 ? undefined : {}.__wrap(ptr);
|
|
|
|
",
|
|
|
|
name,
|
|
|
|
);
|
|
|
|
return Ok(self);
|
|
|
|
}
|
2018-09-26 08:26:00 -07:00
|
|
|
_ => bail!(
|
|
|
|
"unsupported optional return type for calling Rust function from JS: {:?}",
|
|
|
|
ty
|
|
|
|
),
|
2018-08-03 19:07:12 +03:00
|
|
|
};
|
2018-07-19 14:44:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if ty.is_ref_anyref() {
|
|
|
|
self.ret_ty = "any".to_string();
|
|
|
|
self.cx.expose_get_object();
|
|
|
|
self.ret_expr = format!("return getObject(RET);");
|
|
|
|
return Ok(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ty.is_by_ref() {
|
|
|
|
bail!("cannot return references from Rust to JS yet")
|
|
|
|
}
|
|
|
|
|
2018-04-14 09:13:07 -07:00
|
|
|
if let Some(name) = ty.rust_struct() {
|
|
|
|
self.ret_ty = name.to_string();
|
2018-09-21 15:45:31 -07:00
|
|
|
self.cx.require_class_wrap(name);
|
|
|
|
self.ret_expr = format!("return {name}.__wrap(RET);", name = name);
|
2018-06-27 22:42:34 -07:00
|
|
|
return Ok(self);
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
2019-03-26 17:29:53 -07:00
|
|
|
if let Some(num) = ty.number() {
|
2018-04-14 09:13:07 -07:00
|
|
|
self.ret_ty = "number".to_string();
|
2019-03-26 17:29:53 -07:00
|
|
|
if num.is_u32() {
|
|
|
|
self.ret_expr = format!("return RET >>> 0;");
|
|
|
|
} else {
|
|
|
|
self.ret_expr = format!("return RET;");
|
|
|
|
}
|
2018-06-27 22:42:34 -07:00
|
|
|
return Ok(self);
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
2018-08-03 16:28:35 +03:00
|
|
|
if let Some(signed) = ty.get_64() {
|
2018-05-05 14:10:25 -07:00
|
|
|
self.ret_ty = "BigInt".to_string();
|
|
|
|
self.cx.expose_global_argument_ptr()?;
|
|
|
|
let f = if signed {
|
|
|
|
self.cx.expose_int64_memory();
|
|
|
|
"getInt64Memory"
|
|
|
|
} else {
|
|
|
|
self.cx.expose_uint64_memory();
|
|
|
|
"getUint64Memory"
|
|
|
|
};
|
|
|
|
self.prelude("const retptr = globalArgumentPtr();");
|
|
|
|
self.rust_arguments.insert(0, "retptr".to_string());
|
2018-06-27 22:42:34 -07:00
|
|
|
self.ret_expr = format!(
|
|
|
|
"\
|
|
|
|
RET;\n\
|
|
|
|
return {}()[retptr / 8];\n\
|
|
|
|
",
|
|
|
|
f
|
|
|
|
);
|
|
|
|
return Ok(self);
|
2018-05-05 14:10:25 -07:00
|
|
|
}
|
|
|
|
|
2018-04-14 09:13:07 -07:00
|
|
|
match *ty {
|
|
|
|
Descriptor::Boolean => {
|
|
|
|
self.ret_ty = "boolean".to_string();
|
|
|
|
self.ret_expr = format!("return (RET) !== 0;");
|
|
|
|
}
|
2018-05-22 12:34:41 -05:00
|
|
|
Descriptor::Char => {
|
|
|
|
self.ret_ty = "string".to_string();
|
2018-06-15 12:55:37 -05:00
|
|
|
self.ret_expr = format!("return String.fromCodePoint(RET);")
|
2018-05-22 12:34:41 -05:00
|
|
|
}
|
2018-09-26 08:26:00 -07:00
|
|
|
_ => bail!(
|
|
|
|
"unsupported return type for calling Rust function from JS: {:?}",
|
|
|
|
ty
|
|
|
|
),
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
2018-04-25 11:42:22 -07:00
|
|
|
Ok(self)
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
|
|
|
|
2018-07-09 11:07:57 -05:00
|
|
|
pub fn js_doc_comments(&self) -> String {
|
2018-09-26 08:26:00 -07:00
|
|
|
let mut ret: String = self
|
|
|
|
.js_arguments
|
|
|
|
.iter()
|
2019-04-25 18:44:28 +02:00
|
|
|
.map(|a| if a.optional {
|
|
|
|
format!("@param {{{} | undefined}} {}\n", a.type_, a.name)
|
|
|
|
} else {
|
|
|
|
format!("@param {{{}}} {}\n", a.type_, a.name)
|
|
|
|
})
|
2018-09-26 08:26:00 -07:00
|
|
|
.collect();
|
2018-07-09 11:07:57 -05:00
|
|
|
ret.push_str(&format!("@returns {{{}}}", self.ret_ty));
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
|
2018-04-14 09:13:07 -07:00
|
|
|
/// Generate the actual function.
|
|
|
|
///
|
|
|
|
/// The `prefix` specified is typically the string "function" but may be
|
|
|
|
/// different for classes. The `invoc` is the function expression that we're
|
|
|
|
/// invoking, like `wasm.bar` or `this.f`.
|
|
|
|
///
|
|
|
|
/// Returns two strings, the first of which is the JS expression for the
|
2018-05-22 12:34:41 -05:00
|
|
|
/// generated function shim and the second is a TypeScript signature of the
|
2018-04-14 09:13:07 -07:00
|
|
|
/// JS expression.
|
2018-10-18 08:43:36 -07:00
|
|
|
pub fn finish(
|
|
|
|
&mut self,
|
|
|
|
prefix: &str,
|
|
|
|
invoc: &str,
|
|
|
|
exported_shim: ExportedShim,
|
|
|
|
) -> (String, String, String) {
|
2018-06-27 22:42:34 -07:00
|
|
|
let js_args = self
|
|
|
|
.js_arguments
|
2018-04-14 09:13:07 -07:00
|
|
|
.iter()
|
2019-04-25 18:44:28 +02:00
|
|
|
.map(|s| &s.name[..])
|
2018-04-14 09:13:07 -07:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", ");
|
|
|
|
let mut js = format!("{}({}) {{\n", prefix, js_args);
|
2018-06-15 12:55:37 -05:00
|
|
|
js.push_str(&self.prelude);
|
2018-04-14 09:13:07 -07:00
|
|
|
let rust_args = self.rust_arguments.join(", ");
|
|
|
|
|
2018-06-27 22:42:34 -07:00
|
|
|
let invoc = self
|
|
|
|
.ret_expr
|
|
|
|
.replace("RET", &format!("{}({})", invoc, rust_args));
|
2018-04-14 09:30:38 -07:00
|
|
|
let invoc = if self.finally.len() == 0 {
|
|
|
|
invoc
|
2018-04-14 09:13:07 -07:00
|
|
|
} else {
|
2018-06-27 22:42:34 -07:00
|
|
|
format!(
|
|
|
|
"\
|
2018-04-14 09:30:38 -07:00
|
|
|
try {{\n\
|
2018-06-15 12:55:37 -05:00
|
|
|
{}
|
|
|
|
\n}} finally {{\n\
|
|
|
|
{}
|
2018-04-14 09:30:38 -07:00
|
|
|
}}\n\
|
2018-06-15 23:39:51 -07:00
|
|
|
",
|
2018-06-27 22:42:34 -07:00
|
|
|
&invoc, &self.finally,
|
2018-04-14 09:30:38 -07:00
|
|
|
)
|
|
|
|
};
|
2018-06-15 12:55:37 -05:00
|
|
|
js.push_str(&invoc);
|
|
|
|
js.push_str("\n}");
|
2019-05-09 18:22:44 +02:00
|
|
|
|
|
|
|
// Determine TS parameter list
|
|
|
|
let mut omittable = true;
|
|
|
|
let mut ts_args = Vec::with_capacity(self.js_arguments.len());
|
|
|
|
for arg in self.js_arguments.iter().rev() {
|
|
|
|
// In TypeScript, we can mark optional parameters as omittable
|
|
|
|
// using the `?` suffix, but only if they're not followed by
|
|
|
|
// non-omittable parameters. Therefore iterate the parameter list
|
|
|
|
// in reverse and stop using the `?` suffix for optional params as
|
|
|
|
// soon as a non-optional parameter is encountered.
|
|
|
|
if arg.optional {
|
|
|
|
if omittable {
|
|
|
|
ts_args.push(format!("{}?: {}", arg.name, arg.type_));
|
|
|
|
} else {
|
|
|
|
ts_args.push(format!("{}: {} | undefined", arg.name, arg.type_));
|
|
|
|
}
|
2019-04-25 18:44:28 +02:00
|
|
|
} else {
|
2019-05-09 18:22:44 +02:00
|
|
|
omittable = false;
|
|
|
|
ts_args.push(format!("{}: {}", arg.name, arg.type_));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ts_args.reverse();
|
|
|
|
let ts_args = ts_args.join(", ");
|
|
|
|
|
2019-02-01 16:07:12 +09:00
|
|
|
let mut ts = if prefix.is_empty() {
|
|
|
|
format!("{}({})", self.js_name, ts_args)
|
|
|
|
} else {
|
|
|
|
format!("{} {}({})", prefix, self.js_name, ts_args)
|
|
|
|
};
|
2018-10-03 00:00:34 -07:00
|
|
|
if self.constructor.is_none() {
|
|
|
|
ts.push_str(": ");
|
|
|
|
ts.push_str(&self.ret_ty);
|
|
|
|
}
|
2019-02-01 16:07:31 +09:00
|
|
|
ts.push(';');
|
2018-10-18 08:43:36 -07:00
|
|
|
|
|
|
|
if self.ret_anyref || self.anyref_args.len() > 0 {
|
|
|
|
match exported_shim {
|
|
|
|
ExportedShim::Named(name) => {
|
|
|
|
self.cx
|
|
|
|
.anyref
|
|
|
|
.export_xform(name, &self.anyref_args, self.ret_anyref);
|
|
|
|
}
|
|
|
|
ExportedShim::TableElement(idx) => {
|
|
|
|
*idx = self.cx.anyref.table_element_xform(
|
|
|
|
*idx,
|
|
|
|
&self.anyref_args,
|
|
|
|
self.ret_anyref,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-09 11:07:57 -05:00
|
|
|
(js, ts, self.js_doc_comments())
|
2018-04-14 09:13:07 -07:00
|
|
|
}
|
2019-02-19 09:08:37 -08:00
|
|
|
|
|
|
|
fn assert_class(&mut self, arg: &str, class: &str) {
|
|
|
|
if !self.cx.config.debug {
|
2019-03-26 08:00:16 -07:00
|
|
|
return;
|
2019-02-19 09:08:37 -08:00
|
|
|
}
|
|
|
|
self.cx.expose_assert_class();
|
|
|
|
self.prelude(&format!("_assertClass({}, {});", arg, class));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn assert_not_moved(&mut self, arg: &str) {
|
|
|
|
if !self.cx.config.debug {
|
2019-03-26 08:00:16 -07:00
|
|
|
return;
|
2019-02-19 09:08:37 -08:00
|
|
|
}
|
|
|
|
self.prelude(&format!(
|
|
|
|
"\
|
|
|
|
if ({0}.ptr === 0) {{
|
|
|
|
throw new Error('Attempt to use a moved value');
|
|
|
|
}}
|
|
|
|
",
|
|
|
|
arg,
|
|
|
|
));
|
|
|
|
}
|
2018-06-27 22:42:34 -07:00
|
|
|
}
|