mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-16 02:00:51 +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};
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
use self::js2rust::Js2Rust;
|
||||
mod rust2js;
|
||||
use self::rust2js::Rust2Js;
|
||||
|
||||
pub struct Context<'a> {
|
||||
pub globals: String,
|
||||
@ -42,6 +44,7 @@ pub struct SubContext<'a, 'b: 'a> {
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
fn export(&mut self, name: &str, contents: &str) {
|
||||
let contents = deindent(contents);
|
||||
let contents = contents.trim();
|
||||
let global = if self.config.nodejs {
|
||||
format!("module.exports.{} = {};\n", name, contents)
|
||||
@ -49,7 +52,7 @@ impl<'a> Context<'a> {
|
||||
format!("__exports.{} = {}\n", name, contents)
|
||||
} else {
|
||||
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") {
|
||||
format!("export {}\n", contents)
|
||||
} else {
|
||||
@ -69,8 +72,7 @@ impl<'a> Context<'a> {
|
||||
return;
|
||||
}
|
||||
let contents = f(self);
|
||||
let contents = contents.trim();
|
||||
self.export(name, contents);
|
||||
self.export(name, &contents);
|
||||
};
|
||||
|
||||
bind("__wbindgen_object_clone_ref", &|me| {
|
||||
@ -108,9 +110,11 @@ impl<'a> Context<'a> {
|
||||
bind("__wbindgen_string_new", &|me| {
|
||||
me.expose_add_heap_object();
|
||||
me.expose_get_string_from_wasm();
|
||||
String::from("function(p, l) {
|
||||
return addHeapObject(getStringFromWasm(p, l));
|
||||
}")
|
||||
String::from("
|
||||
function(p, l) {
|
||||
return addHeapObject(getStringFromWasm(p, l));
|
||||
}
|
||||
")
|
||||
});
|
||||
|
||||
bind("__wbindgen_number_new", &|me| {
|
||||
@ -139,64 +143,78 @@ impl<'a> Context<'a> {
|
||||
|
||||
bind("__wbindgen_null_new", &|me| {
|
||||
me.expose_add_heap_object();
|
||||
String::from("function() {
|
||||
return addHeapObject(null);
|
||||
}")
|
||||
String::from("
|
||||
function() {
|
||||
return addHeapObject(null);
|
||||
}
|
||||
")
|
||||
});
|
||||
|
||||
bind("__wbindgen_is_null", &|me| {
|
||||
me.expose_get_object();
|
||||
String::from("function(idx) {
|
||||
return getObject(idx) === null ? 1 : 0;
|
||||
}")
|
||||
String::from("
|
||||
function(idx) {
|
||||
return getObject(idx) === null ? 1 : 0;
|
||||
}
|
||||
")
|
||||
});
|
||||
|
||||
bind("__wbindgen_is_undefined", &|me| {
|
||||
me.expose_get_object();
|
||||
String::from("function(idx) {
|
||||
return getObject(idx) === undefined ? 1 : 0;
|
||||
}")
|
||||
String::from("
|
||||
function(idx) {
|
||||
return getObject(idx) === undefined ? 1 : 0;
|
||||
}
|
||||
")
|
||||
});
|
||||
|
||||
bind("__wbindgen_boolean_new", &|me| {
|
||||
me.expose_add_heap_object();
|
||||
String::from("function(v) {
|
||||
return addHeapObject(v === 1);
|
||||
}")
|
||||
String::from("
|
||||
function(v) {
|
||||
return addHeapObject(v === 1);
|
||||
}
|
||||
")
|
||||
});
|
||||
|
||||
bind("__wbindgen_boolean_get", &|me| {
|
||||
me.expose_get_object();
|
||||
String::from("function(i) {
|
||||
let v = getObject(i);
|
||||
if (typeof(v) === 'boolean') {
|
||||
return v ? 1 : 0;
|
||||
} else {
|
||||
return 2;
|
||||
String::from("
|
||||
function(i) {
|
||||
let v = getObject(i);
|
||||
if (typeof(v) === 'boolean') {
|
||||
return v ? 1 : 0;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}")
|
||||
")
|
||||
});
|
||||
|
||||
bind("__wbindgen_symbol_new", &|me| {
|
||||
me.expose_get_string_from_wasm();
|
||||
me.expose_add_heap_object();
|
||||
format!("function(ptr, len) {{
|
||||
let a;
|
||||
console.log(ptr, len);
|
||||
if (ptr === 0) {{
|
||||
a = Symbol();
|
||||
}} else {{
|
||||
a = Symbol(getStringFromWasm(ptr, len));
|
||||
format!("
|
||||
function(ptr, len) {{
|
||||
let a;
|
||||
console.log(ptr, len);
|
||||
if (ptr === 0) {{
|
||||
a = Symbol();
|
||||
}} else {{
|
||||
a = Symbol(getStringFromWasm(ptr, len));
|
||||
}}
|
||||
return addHeapObject(a);
|
||||
}}
|
||||
return addHeapObject(a);
|
||||
}}")
|
||||
")
|
||||
});
|
||||
|
||||
bind("__wbindgen_is_symbol", &|me| {
|
||||
me.expose_get_object();
|
||||
String::from("function(i) {
|
||||
return typeof(getObject(i)) === 'symbol' ? 1 : 0;
|
||||
}")
|
||||
String::from("
|
||||
function(i) {
|
||||
return typeof(getObject(i)) === 'symbol' ? 1 : 0;
|
||||
}
|
||||
")
|
||||
});
|
||||
|
||||
bind("__wbindgen_throw", &|me| {
|
||||
@ -212,51 +230,35 @@ impl<'a> Context<'a> {
|
||||
me.expose_pass_string_to_wasm();
|
||||
me.expose_get_object();
|
||||
me.expose_uint32_memory();
|
||||
String::from("function(i, len_ptr) {
|
||||
let obj = getObject(i);
|
||||
if (typeof(obj) !== 'string')
|
||||
return 0;
|
||||
const [ptr, len] = passStringToWasm(obj);
|
||||
getUint32Memory()[len_ptr / 4] = len;
|
||||
return ptr;
|
||||
}")
|
||||
String::from("
|
||||
function(i, len_ptr) {
|
||||
let obj = getObject(i);
|
||||
if (typeof(obj) !== 'string')
|
||||
return 0;
|
||||
const [ptr, len] = passStringToWasm(obj);
|
||||
getUint32Memory()[len_ptr / 4] = len;
|
||||
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| {
|
||||
me.expose_drop_ref();
|
||||
String::from("function(i) {
|
||||
let obj = getObject(i).original;
|
||||
obj.a = obj.b = 0;
|
||||
dropRef(i);
|
||||
}")
|
||||
String::from("
|
||||
function(i) {
|
||||
let obj = getObject(i).original;
|
||||
obj.a = obj.b = 0;
|
||||
dropRef(i);
|
||||
}
|
||||
")
|
||||
});
|
||||
bind("__wbindgen_cb_forget", &|me| {
|
||||
me.expose_drop_ref();
|
||||
String::from("function(i) {
|
||||
dropRef(i);
|
||||
}")
|
||||
String::from("
|
||||
function(i) {
|
||||
dropRef(i);
|
||||
}
|
||||
")
|
||||
});
|
||||
}
|
||||
|
||||
@ -1222,49 +1224,17 @@ impl<'a> Context<'a> {
|
||||
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) {
|
||||
let amt_to_strip = s.lines()
|
||||
.filter(|l| !l.trim().is_empty())
|
||||
.map(|s| s.len() - s.trim_left().len())
|
||||
.min()
|
||||
.unwrap_or(0);
|
||||
for line in s.lines() {
|
||||
if !line.trim().is_empty() {
|
||||
self.globals.push_str(&line[amt_to_strip..]);
|
||||
}
|
||||
let s = deindent(s);
|
||||
let s = s.trim();
|
||||
|
||||
// Ensure a blank line between adjacent items, and ensure everything is
|
||||
// terminated with a newline.
|
||||
while !self.globals.ends_with("\n\n\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,
|
||||
import: &shared::ImportFunction) {
|
||||
let descriptor = self.cx.describe(&import.shim);
|
||||
let desc_function = descriptor.unwrap_function();
|
||||
|
||||
let mut dst = String::new();
|
||||
|
||||
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 {
|
||||
let target = match import.class {
|
||||
Some(ref class) if import.js_new => {
|
||||
format!("new {}", self.import_name(info, class))
|
||||
}
|
||||
@ -1530,6 +1366,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
}
|
||||
} else {
|
||||
if import.structural {
|
||||
let nargs = descriptor.unwrap_function().arguments.len();
|
||||
let mut s = format!("function(");
|
||||
for i in 0..nargs - 1 {
|
||||
if i > 0 {
|
||||
@ -1538,7 +1375,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
drop(write!(s, "x{}", i));
|
||||
}
|
||||
s.push_str(") { return this.");
|
||||
s.push_str(function_name);
|
||||
s.push_str(&import.function.name);
|
||||
s.push_str("(");
|
||||
for i in 0..nargs - 1 {
|
||||
if i > 0 {
|
||||
@ -1549,7 +1386,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
s.push_str("); }");
|
||||
s
|
||||
} else {
|
||||
format!("{}.prototype.{}", class, function_name)
|
||||
format!("{}.prototype.{}", class, import.function.name)
|
||||
}
|
||||
};
|
||||
self.cx.global(&format!("
|
||||
@ -1561,11 +1398,11 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
let class = self.import_name(info, class);
|
||||
self.cx.global(&format!("
|
||||
const {}_target = {}.{};
|
||||
", import.shim, class, function_name));
|
||||
", import.shim, class, import.function.name));
|
||||
format!("{}_target", import.shim)
|
||||
}
|
||||
None => {
|
||||
let name = self.import_name(info, function_name);
|
||||
let name = self.import_name(info, &import.function.name);
|
||||
if name.contains(".") {
|
||||
self.cx.global(&format!("
|
||||
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 {
|
||||
self.cx.expose_uint32_memory();
|
||||
self.cx.expose_add_heap_object();
|
||||
abi_args.push("exnptr".to_string());
|
||||
format!("
|
||||
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);
|
||||
let js = Rust2Js::new(self.cx)
|
||||
.catch(import.catch)
|
||||
.process(descriptor.unwrap_function())
|
||||
.finish(&target);
|
||||
self.cx.export(&import.shim, &js);
|
||||
}
|
||||
|
||||
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.config.nodejs {
|
||||
self.cx.imports.push_str(&format!("
|
||||
const {} = require('{}').{};
|
||||
self.cx.imports.push_str(&format!("\
|
||||
const {} = require('{}').{};\n\
|
||||
", name, module, name));
|
||||
} else {
|
||||
self.cx.imports.push_str(&format!("
|
||||
import {{ {} }} from '{}';
|
||||
self.cx.imports.push_str(&format!("\
|
||||
import {{ {} }} from '{}';\n\
|
||||
", 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