mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-04-01 18:01:06 +00:00
Extract Rust2Js like Js2Rust was extracted
Along the way clean up a lot of the formatting of the auto-generated code to make it a bit prettier by default.
This commit is contained in:
parent
49ead6a0d0
commit
efb64b87d4
@ -1,4 +1,4 @@
|
|||||||
use super::Context;
|
use super::{indent, Context};
|
||||||
use descriptor::{Descriptor, Function};
|
use descriptor::{Descriptor, Function};
|
||||||
|
|
||||||
/// Helper struct for manfuacturing a shim in JS used to translate JS types to
|
/// Helper struct for manfuacturing a shim in JS used to translate JS types to
|
||||||
@ -305,13 +305,3 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indent(s: &str) -> String {
|
|
||||||
let mut ret = String::new();
|
|
||||||
for line in s.lines() {
|
|
||||||
ret.push_str(" ");
|
|
||||||
ret.push_str(line);
|
|
||||||
ret.push_str("\n");
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
@ -12,6 +12,8 @@ use descriptor::{Descriptor, VectorKind};
|
|||||||
|
|
||||||
mod js2rust;
|
mod js2rust;
|
||||||
use self::js2rust::Js2Rust;
|
use self::js2rust::Js2Rust;
|
||||||
|
mod rust2js;
|
||||||
|
use self::rust2js::Rust2Js;
|
||||||
|
|
||||||
pub struct Context<'a> {
|
pub struct Context<'a> {
|
||||||
pub globals: String,
|
pub globals: String,
|
||||||
@ -42,6 +44,7 @@ pub struct SubContext<'a, 'b: 'a> {
|
|||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
fn export(&mut self, name: &str, contents: &str) {
|
fn export(&mut self, name: &str, contents: &str) {
|
||||||
|
let contents = deindent(contents);
|
||||||
let contents = contents.trim();
|
let contents = contents.trim();
|
||||||
let global = if self.config.nodejs {
|
let global = if self.config.nodejs {
|
||||||
format!("module.exports.{} = {};\n", name, contents)
|
format!("module.exports.{} = {};\n", name, contents)
|
||||||
@ -49,7 +52,7 @@ impl<'a> Context<'a> {
|
|||||||
format!("__exports.{} = {}\n", name, contents)
|
format!("__exports.{} = {}\n", name, contents)
|
||||||
} else {
|
} else {
|
||||||
if contents.starts_with("function") {
|
if contents.starts_with("function") {
|
||||||
format!("export function {} {}\n", name, &contents[8..])
|
format!("export function {}{}\n", name, &contents[8..])
|
||||||
} else if contents.starts_with("class") {
|
} else if contents.starts_with("class") {
|
||||||
format!("export {}\n", contents)
|
format!("export {}\n", contents)
|
||||||
} else {
|
} else {
|
||||||
@ -69,8 +72,7 @@ impl<'a> Context<'a> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let contents = f(self);
|
let contents = f(self);
|
||||||
let contents = contents.trim();
|
self.export(name, &contents);
|
||||||
self.export(name, contents);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bind("__wbindgen_object_clone_ref", &|me| {
|
bind("__wbindgen_object_clone_ref", &|me| {
|
||||||
@ -108,9 +110,11 @@ impl<'a> Context<'a> {
|
|||||||
bind("__wbindgen_string_new", &|me| {
|
bind("__wbindgen_string_new", &|me| {
|
||||||
me.expose_add_heap_object();
|
me.expose_add_heap_object();
|
||||||
me.expose_get_string_from_wasm();
|
me.expose_get_string_from_wasm();
|
||||||
String::from("function(p, l) {
|
String::from("
|
||||||
|
function(p, l) {
|
||||||
return addHeapObject(getStringFromWasm(p, l));
|
return addHeapObject(getStringFromWasm(p, l));
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
|
|
||||||
bind("__wbindgen_number_new", &|me| {
|
bind("__wbindgen_number_new", &|me| {
|
||||||
@ -139,48 +143,59 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
bind("__wbindgen_null_new", &|me| {
|
bind("__wbindgen_null_new", &|me| {
|
||||||
me.expose_add_heap_object();
|
me.expose_add_heap_object();
|
||||||
String::from("function() {
|
String::from("
|
||||||
|
function() {
|
||||||
return addHeapObject(null);
|
return addHeapObject(null);
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
|
|
||||||
bind("__wbindgen_is_null", &|me| {
|
bind("__wbindgen_is_null", &|me| {
|
||||||
me.expose_get_object();
|
me.expose_get_object();
|
||||||
String::from("function(idx) {
|
String::from("
|
||||||
|
function(idx) {
|
||||||
return getObject(idx) === null ? 1 : 0;
|
return getObject(idx) === null ? 1 : 0;
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
|
|
||||||
bind("__wbindgen_is_undefined", &|me| {
|
bind("__wbindgen_is_undefined", &|me| {
|
||||||
me.expose_get_object();
|
me.expose_get_object();
|
||||||
String::from("function(idx) {
|
String::from("
|
||||||
|
function(idx) {
|
||||||
return getObject(idx) === undefined ? 1 : 0;
|
return getObject(idx) === undefined ? 1 : 0;
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
|
|
||||||
bind("__wbindgen_boolean_new", &|me| {
|
bind("__wbindgen_boolean_new", &|me| {
|
||||||
me.expose_add_heap_object();
|
me.expose_add_heap_object();
|
||||||
String::from("function(v) {
|
String::from("
|
||||||
|
function(v) {
|
||||||
return addHeapObject(v === 1);
|
return addHeapObject(v === 1);
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
|
|
||||||
bind("__wbindgen_boolean_get", &|me| {
|
bind("__wbindgen_boolean_get", &|me| {
|
||||||
me.expose_get_object();
|
me.expose_get_object();
|
||||||
String::from("function(i) {
|
String::from("
|
||||||
|
function(i) {
|
||||||
let v = getObject(i);
|
let v = getObject(i);
|
||||||
if (typeof(v) === 'boolean') {
|
if (typeof(v) === 'boolean') {
|
||||||
return v ? 1 : 0;
|
return v ? 1 : 0;
|
||||||
} else {
|
} else {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
|
|
||||||
bind("__wbindgen_symbol_new", &|me| {
|
bind("__wbindgen_symbol_new", &|me| {
|
||||||
me.expose_get_string_from_wasm();
|
me.expose_get_string_from_wasm();
|
||||||
me.expose_add_heap_object();
|
me.expose_add_heap_object();
|
||||||
format!("function(ptr, len) {{
|
format!("
|
||||||
|
function(ptr, len) {{
|
||||||
let a;
|
let a;
|
||||||
console.log(ptr, len);
|
console.log(ptr, len);
|
||||||
if (ptr === 0) {{
|
if (ptr === 0) {{
|
||||||
@ -189,14 +204,17 @@ impl<'a> Context<'a> {
|
|||||||
a = Symbol(getStringFromWasm(ptr, len));
|
a = Symbol(getStringFromWasm(ptr, len));
|
||||||
}}
|
}}
|
||||||
return addHeapObject(a);
|
return addHeapObject(a);
|
||||||
}}")
|
}}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
|
|
||||||
bind("__wbindgen_is_symbol", &|me| {
|
bind("__wbindgen_is_symbol", &|me| {
|
||||||
me.expose_get_object();
|
me.expose_get_object();
|
||||||
String::from("function(i) {
|
String::from("
|
||||||
|
function(i) {
|
||||||
return typeof(getObject(i)) === 'symbol' ? 1 : 0;
|
return typeof(getObject(i)) === 'symbol' ? 1 : 0;
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
|
|
||||||
bind("__wbindgen_throw", &|me| {
|
bind("__wbindgen_throw", &|me| {
|
||||||
@ -212,51 +230,35 @@ impl<'a> Context<'a> {
|
|||||||
me.expose_pass_string_to_wasm();
|
me.expose_pass_string_to_wasm();
|
||||||
me.expose_get_object();
|
me.expose_get_object();
|
||||||
me.expose_uint32_memory();
|
me.expose_uint32_memory();
|
||||||
String::from("function(i, len_ptr) {
|
String::from("
|
||||||
|
function(i, len_ptr) {
|
||||||
let obj = getObject(i);
|
let obj = getObject(i);
|
||||||
if (typeof(obj) !== 'string')
|
if (typeof(obj) !== 'string')
|
||||||
return 0;
|
return 0;
|
||||||
const [ptr, len] = passStringToWasm(obj);
|
const [ptr, len] = passStringToWasm(obj);
|
||||||
getUint32Memory()[len_ptr / 4] = len;
|
getUint32Memory()[len_ptr / 4] = len;
|
||||||
return ptr;
|
return ptr;
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
|
|
||||||
for i in 0..8 {
|
|
||||||
let name = format!("__wbindgen_cb_arity{}", i);
|
|
||||||
bind(&name, &|me| {
|
|
||||||
me.expose_add_heap_object();
|
|
||||||
me.function_table_needed = true;
|
|
||||||
let args = (0..i)
|
|
||||||
.map(|x| format!("arg{}", x))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ");
|
|
||||||
format!("function(a, b, c) {{
|
|
||||||
const cb = function({0}) {{
|
|
||||||
return this.f(this.a, this.b {1} {0});
|
|
||||||
}};
|
|
||||||
cb.a = b;
|
|
||||||
cb.b = c;
|
|
||||||
cb.f = wasm.__wbg_function_table.get(a);
|
|
||||||
let real = cb.bind(cb);
|
|
||||||
real.original = cb;
|
|
||||||
return addHeapObject(real);
|
|
||||||
}}", args, if i == 0 {""} else {","})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
bind("__wbindgen_cb_drop", &|me| {
|
bind("__wbindgen_cb_drop", &|me| {
|
||||||
me.expose_drop_ref();
|
me.expose_drop_ref();
|
||||||
String::from("function(i) {
|
String::from("
|
||||||
|
function(i) {
|
||||||
let obj = getObject(i).original;
|
let obj = getObject(i).original;
|
||||||
obj.a = obj.b = 0;
|
obj.a = obj.b = 0;
|
||||||
dropRef(i);
|
dropRef(i);
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
bind("__wbindgen_cb_forget", &|me| {
|
bind("__wbindgen_cb_forget", &|me| {
|
||||||
me.expose_drop_ref();
|
me.expose_drop_ref();
|
||||||
String::from("function(i) {
|
String::from("
|
||||||
|
function(i) {
|
||||||
dropRef(i);
|
dropRef(i);
|
||||||
}")
|
}
|
||||||
|
")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1222,49 +1224,17 @@ impl<'a> Context<'a> {
|
|||||||
Descriptor::decode(&ret)
|
Descriptor::decode(&ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_from_js(&mut self, ty: &Option<Descriptor>, invoc: &str) -> String {
|
|
||||||
let ty = match *ty {
|
|
||||||
Some(ref t) => t,
|
|
||||||
None => return invoc.to_string(),
|
|
||||||
};
|
|
||||||
if ty.is_by_ref() {
|
|
||||||
panic!("cannot return a reference from JS to Rust")
|
|
||||||
}
|
|
||||||
if let Some(ty) = ty.vector_kind() {
|
|
||||||
let f = self.pass_to_wasm_function(ty);
|
|
||||||
self.expose_uint32_memory();
|
|
||||||
self.expose_set_global_argument();
|
|
||||||
return format!("
|
|
||||||
const [retptr, retlen] = {}({});
|
|
||||||
setGlobalArgument(retlen, 0);
|
|
||||||
return retptr;
|
|
||||||
", f, invoc)
|
|
||||||
}
|
|
||||||
if ty.is_number() {
|
|
||||||
return format!("return {};", invoc)
|
|
||||||
}
|
|
||||||
match *ty {
|
|
||||||
Descriptor::Boolean => format!("return {} ? 1 : 0;", invoc),
|
|
||||||
Descriptor::Anyref => {
|
|
||||||
self.expose_add_heap_object();
|
|
||||||
format!("return addHeapObject({});", invoc)
|
|
||||||
}
|
|
||||||
_ => panic!("unimplemented return from JS to Rust: {:?}", ty),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn global(&mut self, s: &str) {
|
fn global(&mut self, s: &str) {
|
||||||
let amt_to_strip = s.lines()
|
let s = deindent(s);
|
||||||
.filter(|l| !l.trim().is_empty())
|
let s = s.trim();
|
||||||
.map(|s| s.len() - s.trim_left().len())
|
|
||||||
.min()
|
// Ensure a blank line between adjacent items, and ensure everything is
|
||||||
.unwrap_or(0);
|
// terminated with a newline.
|
||||||
for line in s.lines() {
|
while !self.globals.ends_with("\n\n\n") {
|
||||||
if !line.trim().is_empty() {
|
|
||||||
self.globals.push_str(&line[amt_to_strip..]);
|
|
||||||
}
|
|
||||||
self.globals.push_str("\n");
|
self.globals.push_str("\n");
|
||||||
}
|
}
|
||||||
|
self.globals.push_str(s);
|
||||||
|
self.globals.push_str("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1363,142 +1333,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
info: &shared::Import,
|
info: &shared::Import,
|
||||||
import: &shared::ImportFunction) {
|
import: &shared::ImportFunction) {
|
||||||
let descriptor = self.cx.describe(&import.shim);
|
let descriptor = self.cx.describe(&import.shim);
|
||||||
let desc_function = descriptor.unwrap_function();
|
|
||||||
|
|
||||||
let mut dst = String::new();
|
let target = match import.class {
|
||||||
|
|
||||||
dst.push_str("function(");
|
|
||||||
let mut invoc_args = Vec::new();
|
|
||||||
let mut abi_args = Vec::new();
|
|
||||||
|
|
||||||
let mut extra = String::new();
|
|
||||||
let mut finally = String::new();
|
|
||||||
|
|
||||||
let mut next_global = 0;
|
|
||||||
for (i, arg) in desc_function.arguments.iter().enumerate() {
|
|
||||||
abi_args.push(format!("arg{}", i));
|
|
||||||
|
|
||||||
if let Some(ty) = arg.vector_kind() {
|
|
||||||
let f = self.cx.expose_get_vector_from_wasm(ty);
|
|
||||||
self.cx.expose_get_global_argument();
|
|
||||||
extra.push_str(&format!("
|
|
||||||
let len{0} = getGlobalArgument({next_global});
|
|
||||||
let v{0} = {func}(arg{0}, len{0});
|
|
||||||
", i, func = f, next_global = next_global));
|
|
||||||
next_global += 1;
|
|
||||||
|
|
||||||
if !arg.is_by_ref() {
|
|
||||||
extra.push_str(&format!("
|
|
||||||
wasm.__wbindgen_free(arg{0}, len{0} * {size});
|
|
||||||
", i, size = ty.size()));
|
|
||||||
self.cx.required_internal_exports.insert(
|
|
||||||
"__wbindgen_free"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
invoc_args.push(format!("v{}", i));
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(class) = arg.rust_struct() {
|
|
||||||
if arg.is_by_ref() {
|
|
||||||
panic!("cannot invoke JS functions with custom ref types yet")
|
|
||||||
}
|
|
||||||
let assign = format!("let c{0} = {1}.__construct(arg{0});", i, class);
|
|
||||||
extra.push_str(&assign);
|
|
||||||
invoc_args.push(format!("c{}", i));
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((f, mutable)) = arg.stack_closure() {
|
|
||||||
let (js, _ts) = {
|
|
||||||
let mut builder = Js2Rust::new("", self.cx);
|
|
||||||
if mutable {
|
|
||||||
builder.prelude("let a = this.a;\n")
|
|
||||||
.prelude("this.a = 0;\n")
|
|
||||||
.rust_argument("a")
|
|
||||||
.finally("this.a = a;\n");
|
|
||||||
} else {
|
|
||||||
builder.rust_argument("this.a");
|
|
||||||
}
|
|
||||||
builder
|
|
||||||
.rust_argument("this.b")
|
|
||||||
.process(f)
|
|
||||||
.finish("function", "this.f")
|
|
||||||
};
|
|
||||||
self.cx.expose_get_global_argument();
|
|
||||||
self.cx.function_table_needed = true;
|
|
||||||
extra.push_str(&format!("
|
|
||||||
let cb{0} = {js};
|
|
||||||
cb{0}.f = wasm.__wbg_function_table.get(arg{0});
|
|
||||||
cb{0}.a = getGlobalArgument({next_global});
|
|
||||||
cb{0}.b = getGlobalArgument({next_global} + 1);
|
|
||||||
", i, js = js, next_global = next_global));
|
|
||||||
next_global += 2;
|
|
||||||
finally.push_str(&format!("
|
|
||||||
cb{0}.a = cb{0}.b = 0;
|
|
||||||
", i));
|
|
||||||
invoc_args.push(format!("cb{0}.bind(cb{0})", i));
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((f, mutable)) = arg.ref_closure() {
|
|
||||||
let (js, _ts) = {
|
|
||||||
let mut builder = Js2Rust::new("", self.cx);
|
|
||||||
if mutable {
|
|
||||||
builder.prelude("let a = this.a;\n")
|
|
||||||
.prelude("this.a = 0;\n")
|
|
||||||
.rust_argument("a")
|
|
||||||
.finally("this.a = a;\n");
|
|
||||||
} else {
|
|
||||||
builder.rust_argument("this.a");
|
|
||||||
}
|
|
||||||
builder
|
|
||||||
.rust_argument("this.b")
|
|
||||||
.process(f)
|
|
||||||
.finish("function", "this.f")
|
|
||||||
};
|
|
||||||
self.cx.expose_get_global_argument();
|
|
||||||
self.cx.expose_uint32_memory();
|
|
||||||
self.cx.expose_add_heap_object();
|
|
||||||
self.cx.function_table_needed = true;
|
|
||||||
extra.push_str(&format!("
|
|
||||||
let idx{0} = getUint32Memory()[arg{0} / 4];
|
|
||||||
if (idx{0} === 0xffffffff) {{
|
|
||||||
let cb{0} = {js};
|
|
||||||
cb{0}.a = getGlobalArgument({next_global});
|
|
||||||
cb{0}.b = getGlobalArgument({next_global} + 1);
|
|
||||||
cb{0}.f = wasm.__wbg_function_table.get(getGlobalArgument({next_global} + 2));
|
|
||||||
let real = cb{0}.bind(cb{0});
|
|
||||||
real.original = cb{0};
|
|
||||||
idx{0} = getUint32Memory()[arg{0} / 4] = addHeapObject(real);
|
|
||||||
}}
|
|
||||||
", i, js = js, next_global = next_global));
|
|
||||||
next_global += 3;
|
|
||||||
self.cx.expose_get_object();
|
|
||||||
invoc_args.push(format!("getObject(idx{})", i));
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
let invoc_arg = match *arg {
|
|
||||||
ref d if d.is_number() => format!("arg{}", i),
|
|
||||||
Descriptor::Boolean => format!("arg{} !== 0", i),
|
|
||||||
Descriptor::Anyref => {
|
|
||||||
self.cx.expose_take_object();
|
|
||||||
format!("takeObject(arg{})", i)
|
|
||||||
}
|
|
||||||
ref d if d.is_ref_anyref() => {
|
|
||||||
self.cx.expose_get_object();
|
|
||||||
format!("getObject(arg{})", i)
|
|
||||||
}
|
|
||||||
_ => panic!("unimplemented argument type in imported function: {:?}", arg),
|
|
||||||
};
|
|
||||||
invoc_args.push(invoc_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
let nargs = invoc_args.len();
|
|
||||||
let invoc_args = invoc_args.join(", ");
|
|
||||||
let function_name = &import.function.name;
|
|
||||||
let invoc = match import.class {
|
|
||||||
Some(ref class) if import.js_new => {
|
Some(ref class) if import.js_new => {
|
||||||
format!("new {}", self.import_name(info, class))
|
format!("new {}", self.import_name(info, class))
|
||||||
}
|
}
|
||||||
@ -1530,6 +1366,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if import.structural {
|
if import.structural {
|
||||||
|
let nargs = descriptor.unwrap_function().arguments.len();
|
||||||
let mut s = format!("function(");
|
let mut s = format!("function(");
|
||||||
for i in 0..nargs - 1 {
|
for i in 0..nargs - 1 {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
@ -1538,7 +1375,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
drop(write!(s, "x{}", i));
|
drop(write!(s, "x{}", i));
|
||||||
}
|
}
|
||||||
s.push_str(") { return this.");
|
s.push_str(") { return this.");
|
||||||
s.push_str(function_name);
|
s.push_str(&import.function.name);
|
||||||
s.push_str("(");
|
s.push_str("(");
|
||||||
for i in 0..nargs - 1 {
|
for i in 0..nargs - 1 {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
@ -1549,7 +1386,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
s.push_str("); }");
|
s.push_str("); }");
|
||||||
s
|
s
|
||||||
} else {
|
} else {
|
||||||
format!("{}.prototype.{}", class, function_name)
|
format!("{}.prototype.{}", class, import.function.name)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.cx.global(&format!("
|
self.cx.global(&format!("
|
||||||
@ -1561,11 +1398,11 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
let class = self.import_name(info, class);
|
let class = self.import_name(info, class);
|
||||||
self.cx.global(&format!("
|
self.cx.global(&format!("
|
||||||
const {}_target = {}.{};
|
const {}_target = {}.{};
|
||||||
", import.shim, class, function_name));
|
", import.shim, class, import.function.name));
|
||||||
format!("{}_target", import.shim)
|
format!("{}_target", import.shim)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let name = self.import_name(info, function_name);
|
let name = self.import_name(info, &import.function.name);
|
||||||
if name.contains(".") {
|
if name.contains(".") {
|
||||||
self.cx.global(&format!("
|
self.cx.global(&format!("
|
||||||
const {}_target = {};
|
const {}_target = {};
|
||||||
@ -1576,42 +1413,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let invoc = format!("{}({})", invoc, invoc_args);
|
|
||||||
let invoc = self.cx.return_from_js(&desc_function.ret, &invoc);
|
|
||||||
|
|
||||||
let invoc = if import.catch {
|
let js = Rust2Js::new(self.cx)
|
||||||
self.cx.expose_uint32_memory();
|
.catch(import.catch)
|
||||||
self.cx.expose_add_heap_object();
|
.process(descriptor.unwrap_function())
|
||||||
abi_args.push("exnptr".to_string());
|
.finish(&target);
|
||||||
format!("
|
self.cx.export(&import.shim, &js);
|
||||||
try {{
|
|
||||||
{}
|
|
||||||
}} catch (e) {{
|
|
||||||
const view = getUint32Memory();
|
|
||||||
view[exnptr / 4] = 1;
|
|
||||||
view[exnptr / 4 + 1] = addHeapObject(e);
|
|
||||||
}}
|
|
||||||
", invoc)
|
|
||||||
} else {
|
|
||||||
invoc
|
|
||||||
};
|
|
||||||
let invoc = if finally.len() > 0 {
|
|
||||||
format!("
|
|
||||||
try {{
|
|
||||||
{}
|
|
||||||
}} finally {{
|
|
||||||
{}
|
|
||||||
}}
|
|
||||||
", invoc, finally)
|
|
||||||
} else {
|
|
||||||
invoc
|
|
||||||
};
|
|
||||||
|
|
||||||
dst.push_str(&abi_args.join(", "));
|
|
||||||
dst.push_str(") {\n");
|
|
||||||
dst.push_str(&extra);
|
|
||||||
dst.push_str(&format!("{}\n}}", invoc));
|
|
||||||
self.cx.export(&import.shim, &dst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_enum(&mut self, enum_: &shared::Enum) {
|
pub fn generate_enum(&mut self, enum_: &shared::Enum) {
|
||||||
@ -1641,12 +1448,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
|
|
||||||
if self.cx.imported_names.insert(name.to_string()) {
|
if self.cx.imported_names.insert(name.to_string()) {
|
||||||
if self.cx.config.nodejs {
|
if self.cx.config.nodejs {
|
||||||
self.cx.imports.push_str(&format!("
|
self.cx.imports.push_str(&format!("\
|
||||||
const {} = require('{}').{};
|
const {} = require('{}').{};\n\
|
||||||
", name, module, name));
|
", name, module, name));
|
||||||
} else {
|
} else {
|
||||||
self.cx.imports.push_str(&format!("
|
self.cx.imports.push_str(&format!("\
|
||||||
import {{ {} }} from '{}';
|
import {{ {} }} from '{}';\n\
|
||||||
", name, module));
|
", name, module));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1657,3 +1464,29 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn indent(s: &str) -> String {
|
||||||
|
let mut ret = String::new();
|
||||||
|
for line in s.lines() {
|
||||||
|
ret.push_str(" ");
|
||||||
|
ret.push_str(line);
|
||||||
|
ret.push_str("\n");
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deindent(s: &str) -> String {
|
||||||
|
let amt_to_strip = s.lines()
|
||||||
|
.filter(|l| !l.trim().is_empty())
|
||||||
|
.map(|s| s.len() - s.trim_left().len())
|
||||||
|
.min()
|
||||||
|
.unwrap_or(0);
|
||||||
|
let mut ret = String::new();
|
||||||
|
for line in s.lines() {
|
||||||
|
if !line.trim().is_empty() {
|
||||||
|
ret.push_str(&line[amt_to_strip..]);
|
||||||
|
}
|
||||||
|
ret.push_str("\n");
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
310
crates/cli-support/src/js/rust2js.rs
Normal file
310
crates/cli-support/src/js/rust2js.rs
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
use super::Context;
|
||||||
|
use descriptor::{Descriptor, Function};
|
||||||
|
|
||||||
|
use super::{indent, Js2Rust};
|
||||||
|
|
||||||
|
/// Helper struct for manfuacturing a shim in JS used to translate Rust types to
|
||||||
|
/// JS, then invoking an imported JS function.
|
||||||
|
pub struct Rust2Js<'a, 'b: 'a> {
|
||||||
|
cx: &'a mut Context<'b>,
|
||||||
|
|
||||||
|
/// Arguments of the JS shim that we're generating, aka the variables passed
|
||||||
|
/// from Rust which are only numbers.
|
||||||
|
shim_arguments: Vec<String>,
|
||||||
|
|
||||||
|
/// Arguments which are forwarded to the imported JS function
|
||||||
|
js_arguments: Vec<String>,
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
|
||||||
|
/// Next global index to write to when passing arguments via the single
|
||||||
|
/// global stack.
|
||||||
|
global_idx: usize,
|
||||||
|
|
||||||
|
/// Index of the next argument for unique name generation purposes.
|
||||||
|
arg_idx: usize,
|
||||||
|
|
||||||
|
/// Expression used to generate the return value. The string "JS" in this
|
||||||
|
/// expression is replaced with the actual JS invocation eventually.
|
||||||
|
ret_expr: String,
|
||||||
|
|
||||||
|
/// Whether or not we're catching JS exceptions
|
||||||
|
catch: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||||
|
pub fn new(cx: &'a mut Context<'b>) -> Rust2Js<'a, 'b> {
|
||||||
|
Rust2Js {
|
||||||
|
cx,
|
||||||
|
shim_arguments: Vec::new(),
|
||||||
|
js_arguments: Vec::new(),
|
||||||
|
prelude: String::new(),
|
||||||
|
finally: String::new(),
|
||||||
|
global_idx: 0,
|
||||||
|
arg_idx: 0,
|
||||||
|
ret_expr: String::new(),
|
||||||
|
catch: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn catch(&mut self, catch: bool) -> &mut Self {
|
||||||
|
if catch {
|
||||||
|
self.cx.expose_uint32_memory();
|
||||||
|
self.cx.expose_add_heap_object();
|
||||||
|
}
|
||||||
|
self.catch = catch;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates all bindings necessary for the signature in `Function`,
|
||||||
|
/// creating necessary argument conversions and return value processing.
|
||||||
|
pub fn process(&mut self, function: &Function) -> &mut Self {
|
||||||
|
for arg in function.arguments.iter() {
|
||||||
|
self.argument(arg);
|
||||||
|
}
|
||||||
|
self.ret(&function.ret);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn argument(&mut self, arg: &Descriptor) {
|
||||||
|
let i = self.arg_idx;
|
||||||
|
self.arg_idx += 1;
|
||||||
|
|
||||||
|
self.shim_arguments.push(format!("arg{}", i));
|
||||||
|
|
||||||
|
if let Some(ty) = arg.vector_kind() {
|
||||||
|
let f = self.cx.expose_get_vector_from_wasm(ty);
|
||||||
|
self.cx.expose_get_global_argument();
|
||||||
|
let next_global = self.global_idx();
|
||||||
|
self.prelude(&format!("\
|
||||||
|
let len{0} = getGlobalArgument({next_global});\n\
|
||||||
|
let v{0} = {func}(arg{0}, len{0});\n\
|
||||||
|
", i, func = f, next_global = next_global));
|
||||||
|
|
||||||
|
if !arg.is_by_ref() {
|
||||||
|
self.prelude(&format!("\
|
||||||
|
wasm.__wbindgen_free(arg{0}, len{0} * {size});\
|
||||||
|
", i, size = ty.size()));
|
||||||
|
self.cx.required_internal_exports.insert(
|
||||||
|
"__wbindgen_free"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.js_arguments.push(format!("v{}", i));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(class) = arg.rust_struct() {
|
||||||
|
if arg.is_by_ref() {
|
||||||
|
panic!("cannot invoke JS functions with custom ref types yet")
|
||||||
|
}
|
||||||
|
let assign = format!("let c{0} = {1}.__construct(arg{0});", i, class);
|
||||||
|
self.prelude(&assign);
|
||||||
|
self.js_arguments.push(format!("c{}", i));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((f, mutable)) = arg.stack_closure() {
|
||||||
|
let (js, _ts) = {
|
||||||
|
let mut builder = Js2Rust::new("", self.cx);
|
||||||
|
if mutable {
|
||||||
|
builder.prelude("let a = this.a;\n")
|
||||||
|
.prelude("this.a = 0;\n")
|
||||||
|
.rust_argument("a")
|
||||||
|
.finally("this.a = a;\n");
|
||||||
|
} else {
|
||||||
|
builder.rust_argument("this.a");
|
||||||
|
}
|
||||||
|
builder
|
||||||
|
.rust_argument("this.b")
|
||||||
|
.process(f)
|
||||||
|
.finish("function", "this.f")
|
||||||
|
};
|
||||||
|
self.cx.expose_get_global_argument();
|
||||||
|
self.cx.function_table_needed = true;
|
||||||
|
let next_global = self.global_idx();
|
||||||
|
self.global_idx();
|
||||||
|
self.prelude(&format!("\
|
||||||
|
let cb{0} = {js};\n\
|
||||||
|
cb{0}.f = wasm.__wbg_function_table.get(arg{0});\n\
|
||||||
|
cb{0}.a = getGlobalArgument({next_global});\n\
|
||||||
|
cb{0}.b = getGlobalArgument({next_global} + 1);\n\
|
||||||
|
", i, js = js, next_global = next_global));
|
||||||
|
self.finally(&format!("cb{0}.a = cb{0}.b = 0;", i));
|
||||||
|
self.js_arguments.push(format!("cb{0}.bind(cb{0})", i));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((f, mutable)) = arg.ref_closure() {
|
||||||
|
let (js, _ts) = {
|
||||||
|
let mut builder = Js2Rust::new("", self.cx);
|
||||||
|
if mutable {
|
||||||
|
builder.prelude("let a = this.a;\n")
|
||||||
|
.prelude("this.a = 0;\n")
|
||||||
|
.rust_argument("a")
|
||||||
|
.finally("this.a = a;\n");
|
||||||
|
} else {
|
||||||
|
builder.rust_argument("this.a");
|
||||||
|
}
|
||||||
|
builder
|
||||||
|
.rust_argument("this.b")
|
||||||
|
.process(f)
|
||||||
|
.finish("function", "this.f")
|
||||||
|
};
|
||||||
|
self.cx.expose_get_global_argument();
|
||||||
|
self.cx.expose_uint32_memory();
|
||||||
|
self.cx.expose_add_heap_object();
|
||||||
|
self.cx.function_table_needed = true;
|
||||||
|
let reset_idx = format!("\
|
||||||
|
let cb{0} = {js};\n\
|
||||||
|
cb{0}.a = getGlobalArgument({a});\n\
|
||||||
|
cb{0}.b = getGlobalArgument({b});\n\
|
||||||
|
cb{0}.f = wasm.__wbg_function_table.get(getGlobalArgument({c}));\n\
|
||||||
|
let real = cb{0}.bind(cb{0});\n\
|
||||||
|
real.original = cb{0};\n\
|
||||||
|
idx{0} = getUint32Memory()[arg{0} / 4] = addHeapObject(real);\n\
|
||||||
|
",
|
||||||
|
i,
|
||||||
|
js = js,
|
||||||
|
a = self.global_idx(),
|
||||||
|
b = self.global_idx(),
|
||||||
|
c = self.global_idx(),
|
||||||
|
);
|
||||||
|
self.prelude(&format!("\
|
||||||
|
let idx{0} = getUint32Memory()[arg{0} / 4];\n\
|
||||||
|
if (idx{0} === 0xffffffff) {{\n\
|
||||||
|
{1}\
|
||||||
|
}}\n\
|
||||||
|
", i, indent(&reset_idx)));
|
||||||
|
self.cx.expose_get_object();
|
||||||
|
self.js_arguments.push(format!("getObject(idx{})", i));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let invoc_arg = match *arg {
|
||||||
|
ref d if d.is_number() => format!("arg{}", i),
|
||||||
|
Descriptor::Boolean => format!("arg{} !== 0", i),
|
||||||
|
Descriptor::Anyref => {
|
||||||
|
self.cx.expose_take_object();
|
||||||
|
format!("takeObject(arg{})", i)
|
||||||
|
}
|
||||||
|
ref d if d.is_ref_anyref() => {
|
||||||
|
self.cx.expose_get_object();
|
||||||
|
format!("getObject(arg{})", i)
|
||||||
|
}
|
||||||
|
_ => panic!("unimplemented argument type in imported function: {:?}", arg),
|
||||||
|
};
|
||||||
|
self.js_arguments.push(invoc_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ret(&mut self, ret: &Option<Descriptor>) {
|
||||||
|
let ty = match *ret {
|
||||||
|
Some(ref t) => t,
|
||||||
|
None => {
|
||||||
|
self.ret_expr = "JS;".to_string();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if ty.is_by_ref() {
|
||||||
|
panic!("cannot return a reference from JS to Rust")
|
||||||
|
}
|
||||||
|
if let Some(ty) = ty.vector_kind() {
|
||||||
|
let f = self.cx.pass_to_wasm_function(ty);
|
||||||
|
self.cx.expose_uint32_memory();
|
||||||
|
self.cx.expose_set_global_argument();
|
||||||
|
self.ret_expr = format!("\
|
||||||
|
const [retptr, retlen] = {}(JS);\n\
|
||||||
|
setGlobalArgument(retlen, 0);\n\
|
||||||
|
return retptr;\n\
|
||||||
|
", f);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ty.is_number() {
|
||||||
|
self.ret_expr = "return JS;".to_string();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.ret_expr = match *ty {
|
||||||
|
Descriptor::Boolean => "return JS ? 1 : 0;".to_string(),
|
||||||
|
Descriptor::Anyref => {
|
||||||
|
self.cx.expose_add_heap_object();
|
||||||
|
"return addHeapObject(JS);".to_string()
|
||||||
|
}
|
||||||
|
_ => panic!("unimplemented return from JS to Rust: {:?}", ty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(&self, invoc: &str) -> String {
|
||||||
|
let mut ret = String::new();
|
||||||
|
ret.push_str("function(");
|
||||||
|
ret.push_str(&self.shim_arguments.join(", "));
|
||||||
|
if self.catch {
|
||||||
|
if self.shim_arguments.len() > 0 {
|
||||||
|
ret.push_str(", ")
|
||||||
|
}
|
||||||
|
ret.push_str("exnptr");
|
||||||
|
}
|
||||||
|
ret.push_str(") {\n");
|
||||||
|
ret.push_str(&indent(&self.prelude));
|
||||||
|
|
||||||
|
let mut invoc = self.ret_expr.replace(
|
||||||
|
"JS",
|
||||||
|
&format!("{}({})", invoc, self.js_arguments.join(", ")),
|
||||||
|
);
|
||||||
|
if self.catch {
|
||||||
|
let catch = "\
|
||||||
|
const view = getUint32Memory();\n\
|
||||||
|
view[exnptr / 4] = 1;\n\
|
||||||
|
view[exnptr / 4 + 1] = addHeapObject(e);\n\
|
||||||
|
";
|
||||||
|
|
||||||
|
invoc = format!("\
|
||||||
|
try {{\n\
|
||||||
|
{}\
|
||||||
|
}} catch (e) {{\n\
|
||||||
|
{}\
|
||||||
|
}}\
|
||||||
|
", indent(&invoc), indent(catch));
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.finally.len() > 0 {
|
||||||
|
invoc = format!("\
|
||||||
|
try {{\n\
|
||||||
|
{}\
|
||||||
|
}} finally {{\n\
|
||||||
|
{}\
|
||||||
|
}}\
|
||||||
|
", indent(&invoc), indent(&self.finally));
|
||||||
|
}
|
||||||
|
ret.push_str(&indent(&invoc));
|
||||||
|
|
||||||
|
ret.push_str("}\n");
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn global_idx(&mut self) -> usize {
|
||||||
|
let ret = self.global_idx;
|
||||||
|
self.global_idx += 1;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prelude(&mut self, s: &str) -> &mut Self {
|
||||||
|
for line in s.lines() {
|
||||||
|
self.prelude.push_str(line);
|
||||||
|
self.prelude.push_str("\n");
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finally(&mut self, s: &str) -> &mut Self {
|
||||||
|
for line in s.lines() {
|
||||||
|
self.finally.push_str(line);
|
||||||
|
self.finally.push_str("\n");
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user