mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-31 01:11:06 +00:00
1754 lines
62 KiB
Rust
1754 lines
62 KiB
Rust
use std::collections::{HashSet, HashMap};
|
|
use std::fmt::Write;
|
|
use std::mem;
|
|
|
|
use shared;
|
|
use parity_wasm::elements::*;
|
|
|
|
use super::Bindgen;
|
|
|
|
pub struct Context<'a> {
|
|
pub globals: String,
|
|
pub imports: String,
|
|
pub footer: String,
|
|
pub typescript: String,
|
|
pub exposed_globals: HashSet<&'static str>,
|
|
pub required_internal_exports: HashSet<&'static str>,
|
|
pub config: &'a Bindgen,
|
|
pub module: &'a mut Module,
|
|
pub custom_type_names: HashMap<u32, String>,
|
|
pub imported_names: HashSet<String>,
|
|
pub exported_classes: HashMap<String, ExportedClass>,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct ExportedClass {
|
|
pub contents: String,
|
|
pub typescript: String,
|
|
}
|
|
|
|
pub struct SubContext<'a, 'b: 'a> {
|
|
pub program: &'a shared::Program,
|
|
pub cx: &'a mut Context<'b>,
|
|
}
|
|
|
|
impl<'a> Context<'a> {
|
|
pub fn add_custom_type_names(&mut self, program: &shared::Program) {
|
|
for custom in program.custom_type_names.iter() {
|
|
let prev = self.custom_type_names.insert(custom.descriptor,
|
|
custom.name.clone());
|
|
if let Some(prev) = prev {
|
|
assert_eq!(prev, custom.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn export(&mut self, name: &str, contents: &str) {
|
|
let contents = contents.trim();
|
|
let global = if self.config.nodejs {
|
|
format!("module.exports.{} = {};\n", name, contents)
|
|
} else if self.config.no_modules {
|
|
format!("__exports.{} = {}\n", name, contents)
|
|
} else {
|
|
if contents.starts_with("function") {
|
|
format!("export function {} {}\n", name, &contents[8..])
|
|
} else if contents.starts_with("class") {
|
|
format!("export {}\n", contents)
|
|
} else {
|
|
format!("export const {} = {};\n", name, contents)
|
|
}
|
|
};
|
|
self.globals.push_str(&global);
|
|
}
|
|
|
|
pub fn finalize(&mut self, module_name: &str) -> (String, String) {
|
|
self.write_classes();
|
|
{
|
|
let mut bind = |name: &str, f: &Fn(&mut Self) -> String| {
|
|
if !self.wasm_import_needed(name) {
|
|
return;
|
|
}
|
|
let contents = f(self);
|
|
let contents = contents.trim();
|
|
self.export(name, contents);
|
|
};
|
|
|
|
bind("__wbindgen_object_clone_ref", &|me| {
|
|
me.expose_add_heap_object();
|
|
me.expose_get_object();
|
|
let bump_cnt = if me.config.debug {
|
|
String::from("
|
|
if (typeof(val) === 'number')
|
|
throw new Error('corrupt slab');
|
|
val.cnt += 1;
|
|
")
|
|
} else {
|
|
String::from("val.cnt += 1;")
|
|
};
|
|
format!("
|
|
function(idx) {{
|
|
// If this object is on the stack promote it to the heap.
|
|
if ((idx & 1) === 1)
|
|
return addHeapObject(getObject(idx));
|
|
|
|
// Otherwise if the object is on the heap just bump the
|
|
// refcount and move on
|
|
const val = slab[idx >> 1];
|
|
{}
|
|
return idx;
|
|
}}
|
|
", bump_cnt)
|
|
});
|
|
|
|
bind("__wbindgen_object_drop_ref", &|me| {
|
|
me.expose_drop_ref();
|
|
"function(i) { dropRef(i); }".to_string()
|
|
});
|
|
|
|
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));
|
|
}")
|
|
});
|
|
|
|
bind("__wbindgen_number_new", &|me| {
|
|
me.expose_add_heap_object();
|
|
String::from("function(i) { return addHeapObject(i); }")
|
|
});
|
|
|
|
bind("__wbindgen_number_get", &|me| {
|
|
me.expose_get_object();
|
|
me.expose_uint8_memory();
|
|
format!("
|
|
function(n, invalid) {{
|
|
let obj = getObject(n);
|
|
if (typeof(obj) === 'number')
|
|
return obj;
|
|
getUint8Memory()[invalid] = 1;
|
|
return 0;
|
|
}}
|
|
")
|
|
});
|
|
|
|
bind("__wbindgen_undefined_new", &|me| {
|
|
me.expose_add_heap_object();
|
|
String::from("function() { return addHeapObject(undefined); }")
|
|
});
|
|
|
|
bind("__wbindgen_null_new", &|me| {
|
|
me.expose_add_heap_object();
|
|
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;
|
|
}")
|
|
});
|
|
|
|
bind("__wbindgen_is_undefined", &|me| {
|
|
me.expose_get_object();
|
|
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);
|
|
}")
|
|
});
|
|
|
|
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;
|
|
}
|
|
}")
|
|
});
|
|
|
|
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));
|
|
}}
|
|
return addHeapObject(a);
|
|
}}")
|
|
});
|
|
|
|
bind("__wbindgen_is_symbol", &|me| {
|
|
me.expose_get_object();
|
|
String::from("function(i) {
|
|
return typeof(getObject(i)) === 'symbol' ? 1 : 0;
|
|
}")
|
|
});
|
|
|
|
bind("__wbindgen_throw", &|me| {
|
|
me.expose_get_string_from_wasm();
|
|
format!("
|
|
function(ptr, len) {{
|
|
throw new Error(getStringFromWasm(ptr, len));
|
|
}}
|
|
")
|
|
});
|
|
|
|
bind("__wbindgen_string_get", &|me| {
|
|
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;
|
|
}")
|
|
});
|
|
}
|
|
|
|
self.rewrite_imports(module_name);
|
|
|
|
let import_wasm = if self.config.nodejs {
|
|
self.footer.push_str(&format!("wasm = require('./{}_bg');",
|
|
module_name));
|
|
format!("var wasm;")
|
|
} else if self.config.no_modules {
|
|
format!("
|
|
window.{module} = fetch('{module}_bg.wasm')
|
|
.then(response => response.arrayBuffer())
|
|
.then(buffer => WebAssembly.instantiate(buffer, {{ './{module}': __exports }}))
|
|
.then(({{instance}}) => {{
|
|
wasm = instance.exports;
|
|
return wasm;
|
|
}})
|
|
.catch(error => {{
|
|
console.log('Error loading wasm module `{module}`:', error);
|
|
throw error;
|
|
}});
|
|
", module = module_name)
|
|
} else {
|
|
format!("import * as wasm from './{}_bg';", module_name)
|
|
};
|
|
|
|
let js = if self.config.no_modules {
|
|
format!("
|
|
let wasm;
|
|
const __exports = {{}};
|
|
{globals}
|
|
{import_wasm}
|
|
",
|
|
globals = self.globals,
|
|
import_wasm = import_wasm,
|
|
)
|
|
} else {
|
|
format!("
|
|
/* tslint:disable */
|
|
{import_wasm}
|
|
{imports}
|
|
|
|
{globals}
|
|
{footer}",
|
|
import_wasm = import_wasm,
|
|
globals = self.globals,
|
|
imports = self.imports,
|
|
footer = self.footer,
|
|
)
|
|
};
|
|
|
|
self.unexport_unused_internal_exports();
|
|
|
|
(js, self.typescript.clone())
|
|
}
|
|
|
|
fn write_classes(&mut self) {
|
|
let classes = mem::replace(&mut self.exported_classes, Default::default());
|
|
for (class, exports) in classes {
|
|
let mut dst = format!("class {} {{\n", class);
|
|
let mut ts_dst = format!("export {}", dst);
|
|
ts_dst.push_str("
|
|
public ptr: number;
|
|
");
|
|
if self.config.debug {
|
|
self.expose_check_token();
|
|
dst.push_str(&format!("
|
|
constructor(ptr, sym) {{
|
|
_checkToken(sym);
|
|
this.ptr = ptr;
|
|
}}
|
|
"));
|
|
ts_dst.push_str("constructor(ptr: number, sym: Symbol);\n");
|
|
|
|
let new_name = shared::new_function(&class);
|
|
if self.wasm_import_needed(&new_name) {
|
|
self.export(&new_name, &format!("
|
|
function(ptr) {{
|
|
return addHeapObject(new {class}(ptr, token));
|
|
}}
|
|
", class = class));
|
|
}
|
|
} else {
|
|
dst.push_str(&format!("
|
|
constructor(ptr) {{
|
|
this.ptr = ptr;
|
|
}}
|
|
"));
|
|
ts_dst.push_str("constructor(ptr: number);\n");
|
|
|
|
let new_name = shared::new_function(&class);
|
|
if self.wasm_import_needed(&new_name) {
|
|
self.export(&new_name, &format!("
|
|
function(ptr) {{
|
|
return addHeapObject(new {class}(ptr));
|
|
}}
|
|
", class = class));
|
|
}
|
|
}
|
|
|
|
dst.push_str(&format!("
|
|
free() {{
|
|
const ptr = this.ptr;
|
|
this.ptr = 0;
|
|
wasm.{}(ptr);
|
|
}}
|
|
", shared::free_function(&class)));
|
|
ts_dst.push_str("free(): void;\n");
|
|
|
|
dst.push_str(&exports.contents);
|
|
ts_dst.push_str(&exports.typescript);
|
|
dst.push_str("}\n");
|
|
ts_dst.push_str("}\n");
|
|
|
|
self.export(&class, &dst);
|
|
self.typescript.push_str(&ts_dst);
|
|
}
|
|
}
|
|
|
|
fn rewrite_imports(&mut self, module_name: &str) {
|
|
for (name, contents) in self._rewrite_imports(module_name) {
|
|
self.export(&name, &contents);
|
|
}
|
|
}
|
|
|
|
fn _rewrite_imports(&mut self, module_name: &str)
|
|
-> Vec<(String, String)>
|
|
{
|
|
let mut math_imports = Vec::new();
|
|
let imports = self.module.sections_mut()
|
|
.iter_mut()
|
|
.filter_map(|s| {
|
|
match *s {
|
|
Section::Import(ref mut s) => Some(s),
|
|
_ => None,
|
|
}
|
|
})
|
|
.flat_map(|s| s.entries_mut());
|
|
|
|
for import in imports {
|
|
if import.module() == "__wbindgen_placeholder__" {
|
|
import.module_mut().truncate(0);
|
|
import.module_mut().push_str("./");
|
|
import.module_mut().push_str(module_name);
|
|
continue;
|
|
}
|
|
|
|
if import.module() != "env" {
|
|
continue;
|
|
}
|
|
|
|
let renamed_import = format!("__wbindgen_{}", import.field());
|
|
let mut bind_math = |expr: &str| {
|
|
math_imports.push((
|
|
renamed_import.clone(),
|
|
format!("function{}", expr),
|
|
));
|
|
};
|
|
|
|
// FIXME(#32): try to not use function shims
|
|
match import.field() {
|
|
"Math_acos" => bind_math("(x) { return Math.acos(x); }"),
|
|
"Math_asin" => bind_math("(x) { return Math.asin(x); }"),
|
|
"Math_atan" => bind_math("(x) { return Math.atan(x); }"),
|
|
"Math_atan2" => bind_math("(x, y) { return Math.atan2(x, y); }"),
|
|
"Math_cbrt" => bind_math("(x) { return Math.cbrt(x); }"),
|
|
"Math_cosh" => bind_math("(x) { return Math.cosh(x); }"),
|
|
"Math_expm1" => bind_math("(x) { return Math.expm1(x); }"),
|
|
"Math_hypot" => bind_math("(x, y) { return Math.hypot(x, y); }"),
|
|
"Math_log1p" => bind_math("(x) { return Math.log1p(x); }"),
|
|
"Math_sinh" => bind_math("(x) { return Math.sinh(x); }"),
|
|
"Math_tan" => bind_math("(x) { return Math.tan(x); }"),
|
|
"Math_tanh" => bind_math("(x) { return Math.tanh(x); }"),
|
|
"cos" => bind_math("(x) { return Math.cos(x); }"),
|
|
"cosf" => bind_math("(x) { return Math.cos(x); }"),
|
|
"exp" => bind_math("(x) { return Math.exp(x); }"),
|
|
"expf" => bind_math("(x) { return Math.exp(x); }"),
|
|
"log2" => bind_math("(x) { return Math.log2(x); }"),
|
|
"log2f" => bind_math("(x) { return Math.log2(x); }"),
|
|
"log10" => bind_math("(x) { return Math.log10(x); }"),
|
|
"log10f" => bind_math("(x) { return Math.log10(x); }"),
|
|
"log" => bind_math("(x) { return Math.log(x); }"),
|
|
"logf" => bind_math("(x) { return Math.log(x); }"),
|
|
"round" => bind_math("(x) { return Math.round(x); }"),
|
|
"roundf" => bind_math("(x) { return Math.round(x); }"),
|
|
"sin" => bind_math("(x) { return Math.sin(x); }"),
|
|
"sinf" => bind_math("(x) { return Math.sin(x); }"),
|
|
"pow" => bind_math("(x, y) { return Math.pow(x, y); }"),
|
|
"powf" => bind_math("(x, y) { return Math.pow(x, y); }"),
|
|
"exp2" => bind_math("(a) { return Math.pow(2, a); }"),
|
|
"exp2f" => bind_math("(a) { return Math.pow(2, a); }"),
|
|
"fmod" => bind_math("(a, b) { return a % b; }"),
|
|
"fmodf" => bind_math("(a, b) { return a % b; }"),
|
|
"fma" => bind_math("(a, b, c) { return (a * b) + c; }"),
|
|
"fmaf" => bind_math("(a, b, c) { return (a * b) + c; }"),
|
|
_ => continue,
|
|
}
|
|
|
|
import.module_mut().truncate(0);
|
|
import.module_mut().push_str("./");
|
|
import.module_mut().push_str(module_name);
|
|
*import.field_mut() = renamed_import.clone();
|
|
}
|
|
|
|
math_imports
|
|
}
|
|
|
|
fn unexport_unused_internal_exports(&mut self) {
|
|
let required = &self.required_internal_exports;
|
|
for section in self.module.sections_mut() {
|
|
let exports = match *section {
|
|
Section::Export(ref mut s) => s,
|
|
_ => continue,
|
|
};
|
|
exports.entries_mut().retain(|export| {
|
|
!export.field().starts_with("__wbindgen") ||
|
|
required.contains(export.field())
|
|
});
|
|
}
|
|
}
|
|
|
|
fn expose_drop_ref(&mut self) {
|
|
if !self.exposed_globals.insert("drop_ref") {
|
|
return;
|
|
}
|
|
self.expose_global_slab();
|
|
self.expose_global_slab_next();
|
|
let validate_owned = if self.config.debug {
|
|
String::from("
|
|
if ((idx & 1) === 1)
|
|
throw new Error('cannot drop ref of stack objects');
|
|
")
|
|
} else {
|
|
String::new()
|
|
};
|
|
let dec_ref = if self.config.debug {
|
|
String::from("
|
|
if (typeof(obj) === 'number')
|
|
throw new Error('corrupt slab');
|
|
obj.cnt -= 1;
|
|
if (obj.cnt > 0)
|
|
return;
|
|
")
|
|
} else {
|
|
String::from("
|
|
obj.cnt -= 1;
|
|
if (obj.cnt > 0)
|
|
return;
|
|
")
|
|
};
|
|
self.globals.push_str(&format!("
|
|
function dropRef(idx) {{
|
|
{}
|
|
|
|
let obj = slab[idx >> 1];
|
|
{}
|
|
|
|
// If we hit 0 then free up our space in the slab
|
|
slab[idx >> 1] = slab_next;
|
|
slab_next = idx >> 1;
|
|
}}
|
|
", validate_owned, dec_ref));
|
|
}
|
|
|
|
fn expose_global_stack(&mut self) {
|
|
if !self.exposed_globals.insert("stack") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
let stack = [];
|
|
"));
|
|
}
|
|
|
|
fn expose_global_slab(&mut self) {
|
|
if !self.exposed_globals.insert("slab") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("let slab = [];"));
|
|
}
|
|
|
|
fn expose_global_slab_next(&mut self) {
|
|
if !self.exposed_globals.insert("slab_next") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
let slab_next = 0;
|
|
"));
|
|
}
|
|
|
|
fn expose_get_object(&mut self) {
|
|
if !self.exposed_globals.insert("get_object") {
|
|
return;
|
|
}
|
|
self.expose_global_stack();
|
|
self.expose_global_slab();
|
|
|
|
let get_obj = if self.config.debug {
|
|
String::from("
|
|
if (typeof(val) === 'number')
|
|
throw new Error('corrupt slab');
|
|
return val.obj;
|
|
")
|
|
} else {
|
|
String::from("
|
|
return val.obj;
|
|
")
|
|
};
|
|
self.globals.push_str(&format!("
|
|
function getObject(idx) {{
|
|
if ((idx & 1) === 1) {{
|
|
return stack[idx >> 1];
|
|
}} else {{
|
|
const val = slab[idx >> 1];
|
|
{}
|
|
}}
|
|
}}
|
|
", get_obj));
|
|
}
|
|
|
|
fn expose_check_token(&mut self) {
|
|
if !self.exposed_globals.insert("check_token") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
const token = Symbol('foo');
|
|
function _checkToken(sym) {{
|
|
if (token !== sym)
|
|
throw new Error('cannot invoke `new` directly');
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_assert_num(&mut self) {
|
|
if !self.exposed_globals.insert("assert_num") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function _assertNum(n) {{
|
|
if (typeof(n) !== 'number')
|
|
throw new Error('expected a number argument');
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_assert_bool(&mut self) {
|
|
if !self.exposed_globals.insert("assert_bool") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function _assertBoolean(n) {{
|
|
if (typeof(n) !== 'boolean')
|
|
throw new Error('expected a boolean argument');
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_pass_string_to_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("pass_string_to_wasm") {
|
|
return;
|
|
}
|
|
self.required_internal_exports.insert("__wbindgen_malloc");
|
|
self.expose_text_encoder();
|
|
self.expose_uint8_memory();
|
|
let debug = if self.config.debug {
|
|
"
|
|
if (typeof(arg) !== 'string')
|
|
throw new Error('expected a string argument');
|
|
"
|
|
} else {
|
|
""
|
|
};
|
|
self.globals.push_str(&format!("
|
|
function passStringToWasm(arg) {{
|
|
{}
|
|
const buf = cachedEncoder.encode(arg);
|
|
const ptr = wasm.__wbindgen_malloc(buf.length);
|
|
getUint8Memory().set(buf, ptr);
|
|
return [ptr, buf.length];
|
|
}}
|
|
", debug));
|
|
}
|
|
|
|
fn expose_pass_array8_to_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("pass_array8_to_wasm") {
|
|
return;
|
|
}
|
|
self.required_internal_exports.insert("__wbindgen_malloc");
|
|
self.expose_uint8_memory();
|
|
self.globals.push_str(&format!("
|
|
function passArray8ToWasm(arg) {{
|
|
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
|
|
getUint8Memory().set(arg, ptr);
|
|
return [ptr, arg.length];
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_pass_array16_to_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("pass_array16_to_wasm") {
|
|
return;
|
|
}
|
|
self.required_internal_exports.insert("__wbindgen_malloc");
|
|
self.expose_uint16_memory();
|
|
self.globals.push_str(&format!("
|
|
function passArray16ToWasm(arg) {{
|
|
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
|
|
getUint16Memory().set(arg, ptr / 2);
|
|
return [ptr, arg.length];
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_pass_array32_to_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("pass_array32_to_wasm") {
|
|
return;
|
|
}
|
|
self.required_internal_exports.insert("__wbindgen_malloc");
|
|
self.expose_uint32_memory();
|
|
self.globals.push_str(&format!("
|
|
function passArray32ToWasm(arg) {{
|
|
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
|
|
getUint32Memory().set(arg, ptr / 4);
|
|
return [ptr, arg.length];
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_pass_array_f32_to_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("pass_array_f32_to_wasm") {
|
|
return;
|
|
}
|
|
self.required_internal_exports.insert("__wbindgen_malloc");
|
|
self.globals.push_str(&format!("
|
|
function passArrayF32ToWasm(arg) {{
|
|
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
|
|
new Float32Array(wasm.memory.buffer).set(arg, ptr / 4);
|
|
return [ptr, arg.length];
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_pass_array_f64_to_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("pass_array_f64_to_wasm") {
|
|
return;
|
|
}
|
|
self.required_internal_exports.insert("__wbindgen_malloc");
|
|
self.globals.push_str(&format!("
|
|
function passArrayF64ToWasm(arg) {{
|
|
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
|
|
new Float64Array(wasm.memory.buffer).set(arg, ptr / 8);
|
|
return [ptr, arg.length];
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_text_encoder(&mut self) {
|
|
if !self.exposed_globals.insert("text_encoder") {
|
|
return;
|
|
}
|
|
if self.config.nodejs {
|
|
self.globals.push_str(&format!("
|
|
const TextEncoder = require('util').TextEncoder;
|
|
"));
|
|
} else if !(self.config.browser || self.config.no_modules) {
|
|
self.globals.push_str(&format!("
|
|
const TextEncoder = typeof window === 'object' && window.TextEncoder
|
|
? window.TextEncoder
|
|
: require('util').TextEncoder;
|
|
"));
|
|
}
|
|
self.globals.push_str(&format!("
|
|
let cachedEncoder = new TextEncoder('utf-8');
|
|
"));
|
|
}
|
|
|
|
fn expose_text_decoder(&mut self) {
|
|
if !self.exposed_globals.insert("text_decoder") {
|
|
return;
|
|
}
|
|
if self.config.nodejs {
|
|
self.globals.push_str(&format!("
|
|
const TextDecoder = require('util').TextDecoder;
|
|
"));
|
|
} else if !(self.config.browser || self.config.no_modules) {
|
|
self.globals.push_str(&format!("
|
|
const TextDecoder = typeof window === 'object' && window.TextDecoder
|
|
? window.TextDecoder
|
|
: require('util').TextDecoder;
|
|
"));
|
|
}
|
|
self.globals.push_str(&format!("
|
|
let cachedDecoder = new TextDecoder('utf-8');
|
|
"));
|
|
}
|
|
|
|
fn expose_get_string_from_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("get_string_from_wasm") {
|
|
return;
|
|
}
|
|
self.expose_text_decoder();
|
|
self.expose_uint8_memory();
|
|
self.globals.push_str(&format!("
|
|
function getStringFromWasm(ptr, len) {{
|
|
return cachedDecoder.decode(getUint8Memory().slice(ptr, ptr + len));
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_get_array_js_value_from_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("get_array_js_value_from_wasm") {
|
|
return;
|
|
}
|
|
self.expose_get_array_u32_from_wasm();
|
|
self.expose_get_object();
|
|
self.globals.push_str(&format!("
|
|
function getArrayJsValueFromWasm(ptr, len) {{
|
|
const mem = getUint32Memory();
|
|
const slice = mem.slice(ptr / 4, ptr / 4 + len);
|
|
const result = []
|
|
for (ptr in slice) {{
|
|
result.push(getObject(ptr))
|
|
}}
|
|
return result;
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_get_array_i8_from_wasm(&mut self) {
|
|
self.expose_uint8_memory();
|
|
if !self.exposed_globals.insert("get_array_i8_from_wasm") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function getArrayI8FromWasm(ptr, len) {{
|
|
const mem = getUint8Memory();
|
|
const slice = mem.slice(ptr, ptr + len);
|
|
return new Int8Array(slice);
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_get_array_u8_from_wasm(&mut self) {
|
|
self.expose_uint8_memory();
|
|
if !self.exposed_globals.insert("get_array_u8_from_wasm") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function getArrayU8FromWasm(ptr, len) {{
|
|
const mem = getUint8Memory();
|
|
const slice = mem.slice(ptr, ptr + len);
|
|
return new Uint8Array(slice);
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_get_array_i16_from_wasm(&mut self) {
|
|
self.expose_uint16_memory();
|
|
if !self.exposed_globals.insert("get_array_i16_from_wasm") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function getArrayI16FromWasm(ptr, len) {{
|
|
const mem = getUint16Memory();
|
|
const slice = mem.slice(ptr / 2, ptr / 2 + len);
|
|
return new Int16Array(slice);
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_get_array_u16_from_wasm(&mut self) {
|
|
self.expose_uint16_memory();
|
|
if !self.exposed_globals.insert("get_array_u16_from_wasm") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function getArrayU16FromWasm(ptr, len) {{
|
|
const mem = getUint16Memory();
|
|
const slice = mem.slice(ptr / 2, ptr / 2 + len);
|
|
return new Uint16Array(slice);
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_get_array_i32_from_wasm(&mut self) {
|
|
self.expose_uint32_memory();
|
|
if !self.exposed_globals.insert("get_array_i32_from_wasm") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function getArrayI32FromWasm(ptr, len) {{
|
|
const mem = getUint32Memory();
|
|
const slice = mem.slice(ptr / 4, ptr / 4 + len);
|
|
return new Int32Array(slice);
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_get_array_u32_from_wasm(&mut self) {
|
|
self.expose_uint32_memory();
|
|
if !self.exposed_globals.insert("get_array_u32_from_wasm") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function getArrayU32FromWasm(ptr, len) {{
|
|
const mem = getUint32Memory();
|
|
const slice = mem.slice(ptr / 4, ptr / 4 + len);
|
|
return new Uint32Array(slice);
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_get_array_f32_from_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("get_array_f32_from_wasm") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function getArrayF32FromWasm(ptr, len) {{
|
|
const mem = new Float32Array(wasm.memory.buffer);
|
|
const slice = mem.slice(ptr / 4, ptr / 4 + len);
|
|
return new Float32Array(slice);
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_get_array_f64_from_wasm(&mut self) {
|
|
if !self.exposed_globals.insert("get_array_f64_from_wasm") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function getArrayF64FromWasm(ptr, len) {{
|
|
const mem = new Float64Array(wasm.memory.buffer);
|
|
const slice = mem.slice(ptr / 8, ptr / 8 + len);
|
|
return new Float64Array(slice);
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_uint8_memory(&mut self) {
|
|
if !self.exposed_globals.insert("uint8_memory") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
let cachedUint8Memory = null;
|
|
function getUint8Memory() {{
|
|
if (cachedUint8Memory === null ||
|
|
cachedUint8Memory.buffer !== wasm.memory.buffer)
|
|
cachedUint8Memory = new Uint8Array(wasm.memory.buffer);
|
|
return cachedUint8Memory;
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_uint16_memory(&mut self) {
|
|
if !self.exposed_globals.insert("uint16_memory") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
let cachedUint16Memory = null;
|
|
function getUint16Memory() {{
|
|
if (cachedUint16Memory === null ||
|
|
cachedUint16Memory.buffer !== wasm.memory.buffer)
|
|
cachedUint16Memory = new Uint16Array(wasm.memory.buffer);
|
|
return cachedUint16Memory;
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_uint32_memory(&mut self) {
|
|
if !self.exposed_globals.insert("uint32_memory") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
let cachedUint32Memory = null;
|
|
function getUint32Memory() {{
|
|
if (cachedUint32Memory === null ||
|
|
cachedUint32Memory.buffer !== wasm.memory.buffer)
|
|
cachedUint32Memory = new Uint32Array(wasm.memory.buffer);
|
|
return cachedUint32Memory;
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_assert_class(&mut self) {
|
|
if !self.exposed_globals.insert("assert_class") {
|
|
return;
|
|
}
|
|
self.globals.push_str(&format!("
|
|
function _assertClass(instance, klass) {{
|
|
if (!(instance instanceof klass))
|
|
throw new Error(`expected instance of ${{klass.name}}`);
|
|
return instance.ptr;
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_borrowed_objects(&mut self) {
|
|
if !self.exposed_globals.insert("borrowed_objects") {
|
|
return;
|
|
}
|
|
self.expose_global_stack();
|
|
self.globals.push_str(&format!("
|
|
function addBorrowedObject(obj) {{
|
|
stack.push(obj);
|
|
return ((stack.length - 1) << 1) | 1;
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_take_object(&mut self) {
|
|
if !self.exposed_globals.insert("take_object") {
|
|
return;
|
|
}
|
|
self.expose_get_object();
|
|
self.expose_drop_ref();
|
|
self.globals.push_str(&format!("
|
|
function takeObject(idx) {{
|
|
const ret = getObject(idx);
|
|
dropRef(idx);
|
|
return ret;
|
|
}}
|
|
"));
|
|
}
|
|
|
|
fn expose_add_heap_object(&mut self) {
|
|
if !self.exposed_globals.insert("add_heap_object") {
|
|
return;
|
|
}
|
|
self.expose_global_slab();
|
|
self.expose_global_slab_next();
|
|
let set_slab_next = if self.config.debug {
|
|
String::from("
|
|
if (typeof(next) !== 'number')
|
|
throw new Error('corrupt slab');
|
|
slab_next = next;
|
|
")
|
|
} else {
|
|
String::from("
|
|
slab_next = next;
|
|
")
|
|
};
|
|
self.globals.push_str(&format!("
|
|
function addHeapObject(obj) {{
|
|
if (slab_next === slab.length)
|
|
slab.push(slab.length + 1);
|
|
const idx = slab_next;
|
|
const next = slab[idx];
|
|
{}
|
|
slab[idx] = {{ obj, cnt: 1 }};
|
|
return idx << 1;
|
|
}}
|
|
", set_slab_next));
|
|
}
|
|
|
|
fn wasm_import_needed(&self, name: &str) -> bool {
|
|
let imports = match self.module.import_section() {
|
|
Some(s) => s,
|
|
None => return false,
|
|
};
|
|
|
|
imports.entries().iter().any(|i| {
|
|
i.module() == "__wbindgen_placeholder__" && i.field() == name
|
|
})
|
|
}
|
|
|
|
fn custom_type_name(&self, c: u32) -> &str {
|
|
let c = c & !shared::TYPE_CUSTOM_REF_FLAG;
|
|
&self.custom_type_names[&c]
|
|
}
|
|
|
|
fn pass_to_wasm_function(&mut self, ty: &VectorType) -> &'static str {
|
|
match ty.kind {
|
|
VectorKind::String => {
|
|
self.expose_pass_string_to_wasm();
|
|
"passStringToWasm"
|
|
}
|
|
VectorKind::I8 | VectorKind::U8 => {
|
|
self.expose_pass_array8_to_wasm();
|
|
"passArray8ToWasm"
|
|
}
|
|
VectorKind::I16 | VectorKind::U16 => {
|
|
self.expose_pass_array16_to_wasm();
|
|
"passArray16ToWasm"
|
|
}
|
|
VectorKind::I32 | VectorKind::U32 => {
|
|
self.expose_pass_array32_to_wasm();
|
|
"passArray32ToWasm"
|
|
}
|
|
VectorKind::F32 => {
|
|
self.expose_pass_array_f32_to_wasm();
|
|
"passArrayF32ToWasm"
|
|
}
|
|
VectorKind::F64 => {
|
|
self.expose_pass_array_f64_to_wasm();
|
|
"passArrayF64ToWasm"
|
|
}
|
|
VectorKind::JsValue => panic!("Cannot pass Vec<JsValue> to function")
|
|
}
|
|
}
|
|
|
|
fn expose_get_vector_from_wasm(&mut self, ty: &VectorType) -> &'static str {
|
|
match ty.kind {
|
|
VectorKind::String => {
|
|
self.expose_get_string_from_wasm();
|
|
"getStringFromWasm"
|
|
}
|
|
VectorKind::I8 => {
|
|
self.expose_get_array_i8_from_wasm();
|
|
"getArrayI8FromWasm"
|
|
}
|
|
VectorKind::U8 => {
|
|
self.expose_get_array_u8_from_wasm();
|
|
"getArrayU8FromWasm"
|
|
}
|
|
VectorKind::I16 => {
|
|
self.expose_get_array_i16_from_wasm();
|
|
"getArrayI16FromWasm"
|
|
}
|
|
VectorKind::U16 => {
|
|
self.expose_get_array_u16_from_wasm();
|
|
"getArrayU16FromWasm"
|
|
}
|
|
VectorKind::I32 => {
|
|
self.expose_get_array_i32_from_wasm();
|
|
"getArrayI32FromWasm"
|
|
}
|
|
VectorKind::U32 => {
|
|
self.expose_get_array_u32_from_wasm();
|
|
"getArrayU32FromWasm"
|
|
}
|
|
VectorKind::F32 => {
|
|
self.expose_get_array_f32_from_wasm();
|
|
"getArrayF32FromWasm"
|
|
}
|
|
VectorKind::F64 => {
|
|
self.expose_get_array_f64_from_wasm();
|
|
"getArrayF64FromWasm"
|
|
}
|
|
VectorKind::JsValue => {
|
|
self.expose_get_array_js_value_from_wasm();
|
|
"getArrayJsValueFromWasm"
|
|
}
|
|
}
|
|
}
|
|
|
|
fn expose_set_global_argument(&mut self) {
|
|
if !self.exposed_globals.insert("set_global_argument") {
|
|
return;
|
|
}
|
|
self.expose_uint32_memory();
|
|
self.expose_global_argument_ptr();
|
|
self.globals.push_str("
|
|
function setGlobalArgument(arg, i) {
|
|
const idx = globalArgumentPtr() / 4 + i;
|
|
getUint32Memory()[idx] = arg;
|
|
}
|
|
");
|
|
}
|
|
|
|
fn expose_get_global_argument(&mut self) {
|
|
if !self.exposed_globals.insert("get_global_argument") {
|
|
return;
|
|
}
|
|
self.expose_uint32_memory();
|
|
self.expose_global_argument_ptr();
|
|
self.globals.push_str("
|
|
function getGlobalArgument(arg) {
|
|
const idx = globalArgumentPtr() / 4 + arg;
|
|
return getUint32Memory()[idx];
|
|
}
|
|
");
|
|
}
|
|
|
|
fn expose_global_argument_ptr(&mut self) {
|
|
if !self.exposed_globals.insert("global_argument_ptr") {
|
|
return;
|
|
}
|
|
self.required_internal_exports.insert("__wbindgen_global_argument_ptr");
|
|
self.globals.push_str("
|
|
let cachedGlobalArgumentPtr = null;
|
|
function globalArgumentPtr() {
|
|
if (cachedGlobalArgumentPtr === null)
|
|
cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr();
|
|
return cachedGlobalArgumentPtr;
|
|
}
|
|
");
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b> SubContext<'a, 'b> {
|
|
pub fn generate(&mut self) {
|
|
for f in self.program.exports.iter() {
|
|
self.generate_export(f);
|
|
}
|
|
for f in self.program.imports.iter() {
|
|
self.generate_import(f);
|
|
}
|
|
for e in self.program.enums.iter() {
|
|
self.generate_enum(e);
|
|
}
|
|
}
|
|
|
|
pub fn generate_export(&mut self, export: &shared::Export) {
|
|
if let Some(ref class) = export.class {
|
|
return self.generate_export_for_class(class, export);
|
|
}
|
|
let (js, ts) = self.generate_function("function",
|
|
&export.function.name,
|
|
false,
|
|
&export.function);
|
|
self.cx.export(&export.function.name, &js);
|
|
self.cx.globals.push_str("\n");
|
|
self.cx.typescript.push_str("export ");
|
|
self.cx.typescript.push_str(&ts);
|
|
self.cx.typescript.push_str("\n");
|
|
}
|
|
|
|
pub fn generate_export_for_class(&mut self, class: &str, export: &shared::Export) {
|
|
let (js, ts) = self.generate_function(
|
|
"",
|
|
&shared::struct_function_export_name(class, &export.function.name),
|
|
export.method,
|
|
&export.function,
|
|
);
|
|
let class = self.cx.exported_classes.entry(class.to_string())
|
|
.or_insert(ExportedClass::default());
|
|
if !export.method {
|
|
class.contents.push_str("static ");
|
|
class.typescript.push_str("static ");
|
|
}
|
|
class.contents.push_str(&export.function.name);
|
|
class.contents.push_str(&js);
|
|
class.contents.push_str("\n");
|
|
class.typescript.push_str(&ts);
|
|
class.typescript.push_str("\n");
|
|
}
|
|
|
|
fn generate_function(&mut self,
|
|
prefix: &str,
|
|
wasm_name: &str,
|
|
is_method: bool,
|
|
function: &shared::Function) -> (String, String) {
|
|
let mut dst = String::from("(");
|
|
let mut dst_ts = format!("{}(", function.name);
|
|
let mut passed_args = String::new();
|
|
let mut arg_conversions = String::new();
|
|
let mut destructors = String::new();
|
|
|
|
if is_method {
|
|
passed_args.push_str("this.ptr");
|
|
}
|
|
|
|
let mut global_idx = 0;
|
|
for (i, arg) in function.arguments.iter().enumerate() {
|
|
let name = format!("arg{}", i);
|
|
if i > 0 {
|
|
dst.push_str(", ");
|
|
dst_ts.push_str(", ");
|
|
}
|
|
dst.push_str(&name);
|
|
dst_ts.push_str(&name);
|
|
|
|
let mut pass = |arg: &str| {
|
|
if passed_args.len() > 0 {
|
|
passed_args.push_str(", ");
|
|
}
|
|
passed_args.push_str(arg);
|
|
};
|
|
match *arg {
|
|
shared::TYPE_ENUM | shared::TYPE_NUMBER => {
|
|
dst_ts.push_str(": number");
|
|
if self.cx.config.debug {
|
|
self.cx.expose_assert_num();
|
|
arg_conversions.push_str(&format!("_assertNum({});\n", name));
|
|
}
|
|
pass(&name)
|
|
}
|
|
shared::TYPE_BOOLEAN => {
|
|
dst_ts.push_str(": boolean");
|
|
if self.cx.config.debug {
|
|
self.cx.expose_assert_bool();
|
|
arg_conversions.push_str(&format!("\
|
|
_assertBoolean({name});
|
|
", name = name));
|
|
} else {}
|
|
pass(&format!("arg{i} ? 1 : 0", i = i))
|
|
}
|
|
shared::TYPE_JS_OWNED => {
|
|
dst_ts.push_str(": any");
|
|
self.cx.expose_add_heap_object();
|
|
arg_conversions.push_str(&format!("\
|
|
const idx{i} = addHeapObject({arg});
|
|
", i = i, arg = name));
|
|
pass(&format!("idx{}", i));
|
|
}
|
|
shared::TYPE_JS_REF => {
|
|
dst_ts.push_str(": any");
|
|
self.cx.expose_borrowed_objects();
|
|
arg_conversions.push_str(&format!("\
|
|
const idx{i} = addBorrowedObject({arg});
|
|
", i = i, arg = name));
|
|
destructors.push_str("stack.pop();\n");
|
|
pass(&format!("idx{}", i));
|
|
}
|
|
other => {
|
|
match VectorType::from(other) {
|
|
Some(ty) => {
|
|
dst_ts.push_str(": ");
|
|
dst_ts.push_str(ty.js_ty());
|
|
let func = self.cx.pass_to_wasm_function(&ty);
|
|
self.cx.expose_set_global_argument();
|
|
arg_conversions.push_str(&format!("\
|
|
const [ptr{i}, len{i}] = {func}({arg});
|
|
setGlobalArgument(len{i}, {global_idx});
|
|
", i = i, func = func, arg = name, global_idx = global_idx));
|
|
global_idx += 1;
|
|
pass(&format!("ptr{}", i));
|
|
if !ty.owned {
|
|
destructors.push_str(&format!("\n\
|
|
wasm.__wbindgen_free(ptr{i}, len{i} * {size});\n\
|
|
", i = i, size = ty.size()));
|
|
self.cx.required_internal_exports.insert(
|
|
"__wbindgen_free",
|
|
);
|
|
}
|
|
}
|
|
None => {
|
|
let s = self.cx.custom_type_name(other).to_string();
|
|
dst_ts.push_str(&format!(": {}", s));
|
|
if self.cx.config.debug {
|
|
self.cx.expose_assert_class();
|
|
arg_conversions.push_str(&format!("\
|
|
_assertClass({arg}, {struct_});
|
|
", arg = name, struct_ = s));
|
|
}
|
|
|
|
if other & shared::TYPE_CUSTOM_REF_FLAG != 0 {
|
|
pass(&format!("{}.ptr", name));
|
|
} else {
|
|
arg_conversions.push_str(&format!("\
|
|
const ptr{i} = {arg}.ptr;
|
|
{arg}.ptr = 0;
|
|
", i = i, arg = name));
|
|
pass(&format!("ptr{}", i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
dst.push_str(")");
|
|
dst_ts.push_str(")");
|
|
let convert_ret = match function.ret {
|
|
None => {
|
|
dst_ts.push_str(": void");
|
|
format!("return ret;")
|
|
}
|
|
Some(shared::TYPE_ENUM) => {
|
|
dst_ts.push_str(": number");
|
|
format!("return ret;")
|
|
}
|
|
Some(shared::TYPE_NUMBER) => {
|
|
dst_ts.push_str(": number");
|
|
format!("return ret;")
|
|
}
|
|
Some(shared::TYPE_BOOLEAN) => {
|
|
dst_ts.push_str(": boolean");
|
|
format!("return ret !== 0;")
|
|
}
|
|
Some(shared::TYPE_JS_OWNED) => {
|
|
dst_ts.push_str(": any");
|
|
self.cx.expose_take_object();
|
|
format!("return takeObject(ret);")
|
|
}
|
|
Some(shared::TYPE_JS_REF) => {
|
|
dst_ts.push_str(": any");
|
|
self.cx.expose_get_object();
|
|
format!("return getObject(ret);")
|
|
}
|
|
Some(other) => {
|
|
match VectorType::from(other) {
|
|
Some(ty) => {
|
|
if !ty.owned {
|
|
panic!("cannot return slices yet");
|
|
}
|
|
dst_ts.push_str(": ");
|
|
dst_ts.push_str(ty.js_ty());
|
|
let f = self.cx.expose_get_vector_from_wasm(&ty);
|
|
self.cx.expose_get_global_argument();
|
|
self.cx.required_internal_exports.insert(
|
|
"__wbindgen_free",
|
|
);
|
|
format!("
|
|
const len = getGlobalArgument(0);
|
|
const realRet = {}(ret, len);
|
|
wasm.__wbindgen_free(ret, len * {});
|
|
return realRet;
|
|
", f, ty.size())
|
|
}
|
|
None => {
|
|
if other & shared::TYPE_CUSTOM_REF_FLAG != 0 {
|
|
panic!("cannot return references yet");
|
|
}
|
|
let name = self.cx.custom_type_name(other);
|
|
dst_ts.push_str(": ");
|
|
dst_ts.push_str(name);
|
|
if self.cx.config.debug {
|
|
format!("\
|
|
return new {name}(ret, token);
|
|
", name = name)
|
|
} else {
|
|
format!("\
|
|
return new {name}(ret);
|
|
", name = name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
dst_ts.push_str(";");
|
|
dst.push_str(" {\n ");
|
|
dst.push_str(&arg_conversions);
|
|
if destructors.len() == 0 {
|
|
dst.push_str(&format!("\
|
|
const ret = wasm.{}({passed});
|
|
{convert_ret}
|
|
",
|
|
f = wasm_name,
|
|
passed = passed_args,
|
|
convert_ret = convert_ret,
|
|
));
|
|
} else {
|
|
dst.push_str(&format!("\
|
|
try {{
|
|
const ret = wasm.{f}({passed});
|
|
{convert_ret}
|
|
}} finally {{
|
|
{destructors}
|
|
}}
|
|
",
|
|
f = wasm_name,
|
|
passed = passed_args,
|
|
destructors = destructors,
|
|
convert_ret = convert_ret,
|
|
));
|
|
}
|
|
dst.push_str("}");
|
|
(format!("{} {}", prefix, dst), format!("{} {}", prefix, dst_ts))
|
|
}
|
|
|
|
pub fn generate_import(&mut self, import: &shared::Import) {
|
|
match import.kind {
|
|
shared::ImportKind::Function(ref f) => {
|
|
self.generate_import_function(import, f)
|
|
}
|
|
shared::ImportKind::Static(ref s) => {
|
|
self.generate_import_static(import, s)
|
|
}
|
|
shared::ImportKind::Type(_) => {}
|
|
}
|
|
}
|
|
|
|
pub fn generate_import_static(&mut self,
|
|
info: &shared::Import,
|
|
import: &shared::ImportStatic) {
|
|
// TODO: should support more types to import here
|
|
let obj = self.import_name(info, &import.name);
|
|
self.cx.expose_add_heap_object();
|
|
self.cx.export(&import.shim, &format!("
|
|
function() {{
|
|
return addHeapObject({});
|
|
}}
|
|
", obj));
|
|
}
|
|
|
|
pub fn generate_import_function(&mut self,
|
|
info: &shared::Import,
|
|
import: &shared::ImportFunction) {
|
|
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 next_global = 0;
|
|
for (i, arg) in import.function.arguments.iter().enumerate() {
|
|
abi_args.push(format!("arg{}", i));
|
|
let invoc_arg = match *arg {
|
|
shared::TYPE_NUMBER => format!("arg{}", i),
|
|
shared::TYPE_BOOLEAN => format!("arg{} !== 0", i),
|
|
shared::TYPE_JS_OWNED => {
|
|
self.cx.expose_take_object();
|
|
format!("takeObject(arg{})", i)
|
|
}
|
|
shared::TYPE_JS_REF => {
|
|
self.cx.expose_get_object();
|
|
format!("getObject(arg{})", i)
|
|
}
|
|
other => {
|
|
match VectorType::from(other) {
|
|
Some(ty) => {
|
|
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 ty.owned {
|
|
extra.push_str(&format!("
|
|
wasm.__wbindgen_free(arg{0}, len{0} * {size});
|
|
", i, size = ty.size()));
|
|
self.cx.required_internal_exports.insert(
|
|
"__wbindgen_free"
|
|
);
|
|
}
|
|
format!("v{}", i)
|
|
}
|
|
None => {
|
|
if other & shared::TYPE_CUSTOM_REF_FLAG != 0 {
|
|
panic!("cannot import custom ref types yet")
|
|
}
|
|
let s = self.cx.custom_type_name(other).to_string();
|
|
let assign = if self.cx.config.debug {
|
|
format!("let c{0} = new {class}(arg{0}, token);", i, class = s)
|
|
} else {
|
|
format!("let c{0} = new {class}(arg{0});", i, class = s)
|
|
};
|
|
extra.push_str(&assign);
|
|
format!("c{}", i)
|
|
}
|
|
}
|
|
}
|
|
};
|
|
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 => {
|
|
format!("new {}", self.import_name(info, class))
|
|
}
|
|
Some(ref class) if import.method => {
|
|
let class = self.import_name(info, class);
|
|
let target = if let Some(ref g) = import.getter {
|
|
if import.structural {
|
|
format!("function() {{ return this.{}; }}", g)
|
|
} else {
|
|
format!(
|
|
"Object.getOwnPropertyDescriptor\
|
|
({}.prototype, '{}').get;",
|
|
class,
|
|
g,
|
|
)
|
|
}
|
|
} else if let Some(ref s) = import.setter {
|
|
if import.structural {
|
|
format!("function(y) {{ this.{} = y; }}", s)
|
|
} else {
|
|
format!(
|
|
"Object.getOwnPropertyDescriptor\
|
|
({}.prototype, '{}').set;",
|
|
class,
|
|
s,
|
|
)
|
|
}
|
|
} else {
|
|
if import.structural {
|
|
let mut s = format!("function(");
|
|
for i in 0..nargs - 1 {
|
|
if i > 0 {
|
|
drop(write!(s, ", "));
|
|
}
|
|
drop(write!(s, "x{}", i));
|
|
}
|
|
s.push_str(") { return this.");
|
|
s.push_str(function_name);
|
|
s.push_str("(");
|
|
for i in 0..nargs - 1 {
|
|
if i > 0 {
|
|
drop(write!(s, ", "));
|
|
}
|
|
drop(write!(s, "x{}", i));
|
|
}
|
|
s.push_str("); }");
|
|
s
|
|
} else {
|
|
format!("{}.prototype.{}", class, function_name)
|
|
}
|
|
};
|
|
self.cx.globals.push_str(&format!("
|
|
const {}_target = {};
|
|
", import.shim, target));
|
|
format!("{}_target.call", import.shim)
|
|
}
|
|
Some(ref class) => {
|
|
let class = self.import_name(info, class);
|
|
self.cx.globals.push_str(&format!("
|
|
const {}_target = {}.{};
|
|
", import.shim, class, function_name));
|
|
format!("{}_target", import.shim)
|
|
}
|
|
None => {
|
|
let name = self.import_name(info, function_name);
|
|
if name.contains(".") {
|
|
self.cx.globals.push_str(&format!("
|
|
const {}_target = {};
|
|
", import.shim, name));
|
|
format!("{}_target", import.shim)
|
|
} else {
|
|
name
|
|
}
|
|
}
|
|
};
|
|
let invoc = format!("{}({})", invoc, invoc_args);
|
|
let invoc = match import.function.ret {
|
|
Some(shared::TYPE_NUMBER) => format!("return {};", invoc),
|
|
Some(shared::TYPE_BOOLEAN) => format!("return {} ? 1 : 0;", invoc),
|
|
Some(shared::TYPE_JS_OWNED) => {
|
|
self.cx.expose_add_heap_object();
|
|
format!("return addHeapObject({});", invoc)
|
|
}
|
|
Some(other) => {
|
|
match VectorType::from(other) {
|
|
Some(ty) => {
|
|
if !ty.owned {
|
|
panic!("cannot return borrowed slices in imports");
|
|
}
|
|
let f = self.cx.pass_to_wasm_function(&ty);
|
|
self.cx.expose_uint32_memory();
|
|
self.cx.expose_set_global_argument();
|
|
format!("
|
|
const [retptr, retlen] = {}({});
|
|
setGlobalArgument(retlen, 0);
|
|
return retptr;
|
|
", f, invoc)
|
|
}
|
|
None => panic!("unimplemented return type in import"),
|
|
}
|
|
}
|
|
None => 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
|
|
};
|
|
|
|
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) {
|
|
let mut variants = String::new();
|
|
|
|
for variant in enum_.variants.iter() {
|
|
variants.push_str(&format!("{}:{},", variant.name, variant.value));
|
|
}
|
|
self.cx.export(&enum_.name, &format!("Object.freeze({{ {} }})", variants));
|
|
self.cx.typescript.push_str(&format!("export enum {} {{", enum_.name));
|
|
|
|
variants.clear();
|
|
for variant in enum_.variants.iter() {
|
|
variants.push_str(&format!("{},", variant.name));
|
|
}
|
|
self.cx.typescript.push_str(&variants);
|
|
self.cx.typescript.push_str("}\n");
|
|
}
|
|
|
|
fn import_name(&mut self, import: &shared::Import, item: &str) -> String {
|
|
if let Some(ref module) = import.module {
|
|
if self.cx.config.no_modules {
|
|
panic!("import from `{}` module not allowed in `--no-modules`. use `--nodejs` or `--browser` instead", module);
|
|
}
|
|
|
|
let name = import.js_namespace.as_ref().map(|s| &**s).unwrap_or(item);
|
|
|
|
if self.cx.imported_names.insert(name.to_string()) {
|
|
if self.cx.config.nodejs {
|
|
self.cx.imports.push_str(&format!("
|
|
const {} = require('{}').{};
|
|
", name, module, name));
|
|
} else {
|
|
self.cx.imports.push_str(&format!("
|
|
import {{ {} }} from '{}';
|
|
", name, module));
|
|
}
|
|
}
|
|
}
|
|
match import.js_namespace {
|
|
Some(ref s) => format!("{}.{}", s, item),
|
|
None => item.to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct VectorType {
|
|
owned: bool,
|
|
kind: VectorKind,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum VectorKind {
|
|
String,
|
|
I8,
|
|
U8,
|
|
I16,
|
|
U16,
|
|
I32,
|
|
U32,
|
|
F32,
|
|
F64,
|
|
JsValue,
|
|
}
|
|
|
|
impl VectorType {
|
|
fn from(desc: u32) -> Option<VectorType> {
|
|
let ty = match desc {
|
|
shared::TYPE_BORROWED_STR => {
|
|
VectorType { owned: false, kind: VectorKind::String }
|
|
}
|
|
shared::TYPE_STRING => {
|
|
VectorType { owned: true, kind: VectorKind::String }
|
|
}
|
|
shared::TYPE_VECTOR_U8 => {
|
|
VectorType { owned: true, kind: VectorKind::U8 }
|
|
}
|
|
shared::TYPE_VECTOR_I8 => {
|
|
VectorType { owned: true, kind: VectorKind::I8 }
|
|
}
|
|
shared::TYPE_SLICE_U8 => {
|
|
VectorType { owned: false, kind: VectorKind::U8 }
|
|
}
|
|
shared::TYPE_SLICE_I8 => {
|
|
VectorType { owned: false, kind: VectorKind::I8 }
|
|
}
|
|
shared::TYPE_VECTOR_U16 => {
|
|
VectorType { owned: true, kind: VectorKind::U16 }
|
|
}
|
|
shared::TYPE_VECTOR_I16 => {
|
|
VectorType { owned: true, kind: VectorKind::I16 }
|
|
}
|
|
shared::TYPE_SLICE_U16 => {
|
|
VectorType { owned: false, kind: VectorKind::U16 }
|
|
}
|
|
shared::TYPE_SLICE_I16 => {
|
|
VectorType { owned: false, kind: VectorKind::I16 }
|
|
}
|
|
shared::TYPE_VECTOR_U32 => {
|
|
VectorType { owned: true, kind: VectorKind::U32 }
|
|
}
|
|
shared::TYPE_VECTOR_I32 => {
|
|
VectorType { owned: true, kind: VectorKind::I32 }
|
|
}
|
|
shared::TYPE_SLICE_U32 => {
|
|
VectorType { owned: false, kind: VectorKind::U32 }
|
|
}
|
|
shared::TYPE_SLICE_I32 => {
|
|
VectorType { owned: false, kind: VectorKind::I32 }
|
|
}
|
|
shared::TYPE_VECTOR_F32 => {
|
|
VectorType { owned: true, kind: VectorKind::F32 }
|
|
}
|
|
shared::TYPE_VECTOR_F64 => {
|
|
VectorType { owned: true, kind: VectorKind::F64 }
|
|
}
|
|
shared::TYPE_SLICE_F32 => {
|
|
VectorType { owned: false, kind: VectorKind::F32 }
|
|
}
|
|
shared::TYPE_SLICE_F64 => {
|
|
VectorType { owned: false, kind: VectorKind::F64 }
|
|
}
|
|
shared::TYPE_VECTOR_JSVALUE => {
|
|
VectorType { owned: true, kind: VectorKind::JsValue }
|
|
}
|
|
_ => return None
|
|
};
|
|
Some(ty)
|
|
}
|
|
|
|
fn js_ty(&self) -> &str {
|
|
match self.kind {
|
|
VectorKind::String => "string",
|
|
VectorKind::I8 => "Int8Array",
|
|
VectorKind::U8 => "Uint8Array",
|
|
VectorKind::I16 => "Int16Array",
|
|
VectorKind::U16 => "Uint16Array",
|
|
VectorKind::I32 => "Int32Array",
|
|
VectorKind::U32 => "Uint32Array",
|
|
VectorKind::F32 => "Float32Array",
|
|
VectorKind::F64 => "Float64Array",
|
|
VectorKind::JsValue => "any[]",
|
|
}
|
|
}
|
|
|
|
fn size(&self) -> usize {
|
|
match self.kind {
|
|
VectorKind::String => 1,
|
|
VectorKind::I8 => 1,
|
|
VectorKind::U8 => 1,
|
|
VectorKind::I16 => 2,
|
|
VectorKind::U16 => 2,
|
|
VectorKind::I32 => 4,
|
|
VectorKind::U32 => 4,
|
|
VectorKind::F32 => 4,
|
|
VectorKind::F64 => 8,
|
|
VectorKind::JsValue => 4,
|
|
}
|
|
}
|
|
}
|