mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-22 09:11:35 +00:00
Generate TypeScript by default instead of JS
This is what's needed in the immediate future anyway, so let's do that!
This commit is contained in:
@ -1,486 +0,0 @@
|
||||
use shared;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Js {
|
||||
expose_global_memory: bool,
|
||||
expose_global_exports: bool,
|
||||
expose_get_string_from_wasm: bool,
|
||||
expose_pass_string_to_wasm: bool,
|
||||
expose_assert_num: bool,
|
||||
expose_assert_class: bool,
|
||||
expose_token: bool,
|
||||
expose_objects: bool,
|
||||
exports: Vec<(String, String)>,
|
||||
classes: Vec<String>,
|
||||
imports: Vec<String>,
|
||||
pub nodejs: bool,
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
impl Js {
|
||||
|
||||
pub fn generate_program(&mut self, program: &shared::Program) {
|
||||
for f in program.free_functions.iter() {
|
||||
self.generate_free_function(f);
|
||||
}
|
||||
for s in program.structs.iter() {
|
||||
self.generate_struct(s);
|
||||
}
|
||||
for s in program.imports.iter() {
|
||||
self.generate_import(s);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_free_function(&mut self, func: &shared::Function) {
|
||||
let ret = self.generate_function(&format!("function {}", func.name),
|
||||
&func.name,
|
||||
false,
|
||||
&func.arguments,
|
||||
func.ret.as_ref());
|
||||
|
||||
self.exports.push((func.name.clone(), ret));
|
||||
}
|
||||
|
||||
pub fn generate_struct(&mut self, s: &shared::Struct) {
|
||||
let mut dst = String::new();
|
||||
self.expose_token = true;
|
||||
self.expose_global_exports = true;
|
||||
dst.push_str(&format!("
|
||||
class {} {{
|
||||
constructor(ptr, sym) {{
|
||||
_checkToken(sym);
|
||||
this.__wasmPtr = ptr;
|
||||
}}
|
||||
|
||||
free() {{
|
||||
const ptr = this.__wasmPtr;
|
||||
this.__wasmPtr = 0;
|
||||
exports.{}(ptr);
|
||||
}}
|
||||
", s.name, s.free_function()));
|
||||
|
||||
for function in s.functions.iter() {
|
||||
let f = self.generate_function(
|
||||
&format!("static {}", function.name),
|
||||
&function.struct_function_export_name(&s.name),
|
||||
false,
|
||||
&function.arguments,
|
||||
function.ret.as_ref(),
|
||||
);
|
||||
dst.push_str(&f);
|
||||
dst.push_str("\n");
|
||||
}
|
||||
for method in s.methods.iter() {
|
||||
let f = self.generate_function(
|
||||
&format!("{}", method.function.name),
|
||||
&method.function.struct_function_export_name(&s.name),
|
||||
true,
|
||||
&method.function.arguments,
|
||||
method.function.ret.as_ref(),
|
||||
);
|
||||
dst.push_str(&f);
|
||||
dst.push_str("\n");
|
||||
}
|
||||
dst.push_str("}\n");
|
||||
self.classes.push(dst);
|
||||
self.exports.push((s.name.clone(), s.name.clone()));
|
||||
}
|
||||
|
||||
fn generate_function(&mut self,
|
||||
name: &str,
|
||||
wasm_name: &str,
|
||||
is_method: bool,
|
||||
arguments: &[shared::Type],
|
||||
ret: Option<&shared::Type>) -> String {
|
||||
let mut dst = format!("{}(", 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.__wasmPtr");
|
||||
}
|
||||
|
||||
for (i, arg) in arguments.iter().enumerate() {
|
||||
let name = format!("arg{}", i);
|
||||
if i > 0 {
|
||||
dst.push_str(", ");
|
||||
}
|
||||
dst.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::Number => {
|
||||
self.expose_assert_num = true;
|
||||
arg_conversions.push_str(&format!("_assertNum({});\n", name));
|
||||
pass(&name)
|
||||
}
|
||||
shared::Type::BorrowedStr |
|
||||
shared::Type::String => {
|
||||
self.expose_global_exports = true;
|
||||
self.expose_pass_string_to_wasm = true;
|
||||
arg_conversions.push_str(&format!("\
|
||||
const [ptr{i}, len{i}] = passStringToWasm({arg});
|
||||
", i = i, arg = name));
|
||||
pass(&format!("ptr{}", i));
|
||||
pass(&format!("len{}", i));
|
||||
if let shared::Type::BorrowedStr = *arg {
|
||||
destructors.push_str(&format!("\n\
|
||||
exports.__wbindgen_free(ptr{i}, len{i});\n\
|
||||
", i = i));
|
||||
}
|
||||
}
|
||||
shared::Type::ByRef(ref s) |
|
||||
shared::Type::ByMutRef(ref s) => {
|
||||
self.expose_assert_class = true;
|
||||
arg_conversions.push_str(&format!("\
|
||||
const ptr{i} = _assertClass({arg}, {struct_});
|
||||
", i = i, arg = name, struct_ = s));
|
||||
pass(&format!("ptr{}", i));
|
||||
}
|
||||
shared::Type::ByValue(ref s) => {
|
||||
self.expose_assert_class = true;
|
||||
arg_conversions.push_str(&format!("\
|
||||
const ptr{i} = _assertClass({arg}, {struct_});
|
||||
{arg}.__wasmPtr = 0;
|
||||
", i = i, arg = name, struct_ = s));
|
||||
pass(&format!("ptr{}", i));
|
||||
}
|
||||
shared::Type::JsObject => {
|
||||
self.expose_objects = true;
|
||||
arg_conversions.push_str(&format!("\
|
||||
const idx{i} = addHeapObject({arg});
|
||||
", i = i, arg = name));
|
||||
pass(&format!("idx{}", i));
|
||||
}
|
||||
shared::Type::JsObjectRef => {
|
||||
self.expose_objects = true;
|
||||
arg_conversions.push_str(&format!("\
|
||||
const idx{i} = addBorrowedObject({arg});
|
||||
", i = i, arg = name));
|
||||
destructors.push_str("popBorrowedObject();\n");
|
||||
pass(&format!("idx{}", i));
|
||||
}
|
||||
}
|
||||
}
|
||||
let convert_ret = match ret {
|
||||
None |
|
||||
Some(&shared::Type::Number) => format!("return ret;"),
|
||||
Some(&shared::Type::JsObject) => {
|
||||
self.expose_objects = true;
|
||||
format!("return takeObject(ret);")
|
||||
}
|
||||
Some(&shared::Type::JsObjectRef) |
|
||||
Some(&shared::Type::BorrowedStr) |
|
||||
Some(&shared::Type::ByMutRef(_)) |
|
||||
Some(&shared::Type::ByRef(_)) => panic!(),
|
||||
Some(&shared::Type::ByValue(ref name)) => {
|
||||
format!("\
|
||||
return new {name}(ret, token);
|
||||
", name = name)
|
||||
}
|
||||
Some(&shared::Type::String) => {
|
||||
self.expose_get_string_from_wasm = true;
|
||||
self.expose_global_exports = true;
|
||||
format!("
|
||||
const ptr = exports.__wbindgen_boxed_str_ptr(ret);
|
||||
const len = exports.__wbindgen_boxed_str_len(ret);
|
||||
const realRet = getStringFromWasm(ptr, len);
|
||||
exports.__wbindgen_boxed_str_free(ret);
|
||||
return realRet;
|
||||
")
|
||||
}
|
||||
};
|
||||
dst.push_str(") {\n ");
|
||||
dst.push_str(&arg_conversions);
|
||||
self.expose_global_exports = true;
|
||||
if destructors.len() == 0 {
|
||||
dst.push_str(&format!("\
|
||||
const ret = exports.{f}({passed});
|
||||
{convert_ret}
|
||||
", f = wasm_name, passed = passed_args, convert_ret = convert_ret));
|
||||
} else {
|
||||
dst.push_str(&format!("\
|
||||
try {{
|
||||
const ret = exports.{f}({passed});
|
||||
{convert_ret}
|
||||
}} finally {{
|
||||
{destructors}
|
||||
}}
|
||||
", f = wasm_name, passed = passed_args, destructors = destructors,
|
||||
convert_ret = convert_ret));
|
||||
}
|
||||
dst.push_str("}");
|
||||
return dst
|
||||
}
|
||||
|
||||
pub fn generate_import(&mut self, import: &shared::Function) {
|
||||
let mut dst = String::new();
|
||||
|
||||
dst.push_str(&format!("const {0} = imports.env.{0};\n", import.name));
|
||||
dst.push_str(&format!("imports.env.{0} = function {0}_shim(", import.name));
|
||||
|
||||
let mut invocation = String::new();
|
||||
for (i, arg) in import.arguments.iter().enumerate() {
|
||||
if invocation.len() > 0 {
|
||||
invocation.push_str(", ");
|
||||
}
|
||||
if i > 0 {
|
||||
dst.push_str(", ");
|
||||
}
|
||||
match *arg {
|
||||
shared::Type::Number => {
|
||||
invocation.push_str(&format!("arg{}", i));
|
||||
dst.push_str(&format!("arg{}", i));
|
||||
}
|
||||
shared::Type::BorrowedStr => {
|
||||
self.expose_get_string_from_wasm = true;
|
||||
invocation.push_str(&format!("getStringFromWasm(ptr{0}, len{0})", i));
|
||||
dst.push_str(&format!("ptr{0}, len{0}", i));
|
||||
}
|
||||
shared::Type::JsObject => {
|
||||
self.expose_objects = true;
|
||||
invocation.push_str(&format!("takeObject(arg{})", i));
|
||||
dst.push_str(&format!("arg{}", i));
|
||||
}
|
||||
shared::Type::JsObjectRef => {
|
||||
self.expose_objects = true;
|
||||
invocation.push_str(&format!("getObject(arg{})", i));
|
||||
dst.push_str(&format!("arg{}", i));
|
||||
}
|
||||
shared::Type::String |
|
||||
shared::Type::ByRef(_) |
|
||||
shared::Type::ByMutRef(_) |
|
||||
shared::Type::ByValue(_) => {
|
||||
panic!("unsupported type in import");
|
||||
}
|
||||
}
|
||||
}
|
||||
dst.push_str(") {\n");
|
||||
dst.push_str(&format!("return {}({});\n}}", import.name, invocation));
|
||||
|
||||
self.imports.push(dst);
|
||||
}
|
||||
|
||||
pub fn to_string(&mut self) -> String {
|
||||
let mut globals = String::new();
|
||||
let mut real_globals = String::new();
|
||||
if self.expose_global_memory ||
|
||||
self.expose_pass_string_to_wasm ||
|
||||
self.expose_get_string_from_wasm
|
||||
{
|
||||
globals.push_str("const memory = obj.instance.exports.memory;\n");
|
||||
}
|
||||
if self.expose_global_exports ||
|
||||
self.expose_pass_string_to_wasm ||
|
||||
self.expose_get_string_from_wasm
|
||||
{
|
||||
globals.push_str("const exports = obj.instance.exports;\n");
|
||||
}
|
||||
if self.expose_token {
|
||||
globals.push_str("\
|
||||
const token = Symbol('foo');
|
||||
function _checkToken(sym) {
|
||||
if (token !== sym)
|
||||
throw new Error('cannot invoke `new` directly');
|
||||
}
|
||||
");
|
||||
}
|
||||
if self.expose_assert_num {
|
||||
globals.push_str("\
|
||||
function _assertNum(n) {
|
||||
if (typeof(n) !== 'number')
|
||||
throw new Error('expected a number argument');
|
||||
}
|
||||
");
|
||||
}
|
||||
if self.expose_pass_string_to_wasm {
|
||||
if self.nodejs {
|
||||
globals.push_str("
|
||||
function passStringToWasm(arg) {
|
||||
if (typeof(arg) !== 'string')
|
||||
throw new Error('expected a string argument');
|
||||
const buf = Buffer.from(arg);
|
||||
const len = buf.length;
|
||||
const ptr = exports.__wbindgen_malloc(len);
|
||||
let array = new Uint8Array(memory.buffer);
|
||||
buf.copy(array, ptr);
|
||||
return [ptr, len];
|
||||
}
|
||||
");
|
||||
} else {
|
||||
globals.push_str("
|
||||
function passStringToWasm(arg) {
|
||||
if (typeof(arg) !== 'string')
|
||||
throw new Error('expected a string argument');
|
||||
const buf = new TextEncoder('utf-8').encode(arg);
|
||||
const len = buf.length;
|
||||
const ptr = exports.__wbindgen_malloc(len);
|
||||
let array = new Uint8Array(memory.buffer);
|
||||
array.set(buf, ptr);
|
||||
return [ptr, len];
|
||||
}
|
||||
");
|
||||
}
|
||||
}
|
||||
if self.expose_get_string_from_wasm {
|
||||
real_globals.push_str("let getStringFromWasm = null;\n");
|
||||
if self.nodejs {
|
||||
globals.push_str("
|
||||
getStringFromWasm = function getStringFromWasm(ptr, len) {
|
||||
const mem = new Uint8Array(memory.buffer);
|
||||
const buf = Buffer.from(mem.slice(ptr, ptr + len));
|
||||
const ret = buf.toString();
|
||||
return ret;
|
||||
}
|
||||
");
|
||||
} else {
|
||||
globals.push_str("
|
||||
getStringFromWasm = function getStringFromWasm(ptr, len) {
|
||||
const mem = new Uint8Array(memory.buffer);
|
||||
const slice = mem.slice(ptr, ptr + len);
|
||||
const ret = new TextDecoder('utf-8').decode(slice);
|
||||
return ret;
|
||||
}
|
||||
");
|
||||
}
|
||||
}
|
||||
if self.expose_assert_class {
|
||||
globals.push_str("
|
||||
function _assertClass(instance, klass) {
|
||||
if (!(instance instanceof klass))
|
||||
throw new Error(`expected instance of ${klass.name}`);
|
||||
return instance.__wasmPtr;
|
||||
}
|
||||
");
|
||||
}
|
||||
|
||||
|
||||
if self.expose_objects {
|
||||
real_globals.push_str("
|
||||
let stack = [];
|
||||
let slab = [];
|
||||
let slab_next = 0;
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (slab_next == slab.length) {
|
||||
slab.push(slab.length + 1);
|
||||
}
|
||||
const idx = slab_next;
|
||||
slab_next = slab[idx];
|
||||
slab[idx] = { obj, cnt: 1 };
|
||||
return idx << 1;
|
||||
}
|
||||
|
||||
function addBorrowedObject(obj) {
|
||||
stack.push(obj);
|
||||
return ((stack.length - 1) << 1) | 1;
|
||||
}
|
||||
|
||||
function popBorrowedObject() {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
function getObject(idx) {
|
||||
if (idx & 1 == 1) {
|
||||
return stack[idx >> 1];
|
||||
} else {
|
||||
return slab[idx >> 1].obj;
|
||||
}
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropRef(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function cloneRef(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
|
||||
slab[idx >> 1].cnt += 1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
function dropRef(idx) {
|
||||
if (idx & 1 == 1)
|
||||
throw new Error('cannot drop ref of stack objects');
|
||||
|
||||
// Decrement our refcount, but if it's still larger than one
|
||||
// keep going
|
||||
let obj = slab[idx >> 1];
|
||||
obj.cnt -= 1;
|
||||
if (obj.cnt > 0)
|
||||
return;
|
||||
|
||||
// If we hit 0 then free up our space in the slab
|
||||
slab[idx >> 1] = slab_next;
|
||||
slab_next = idx >> 1;
|
||||
}
|
||||
");
|
||||
|
||||
if self.debug {
|
||||
self.exports.push(
|
||||
(
|
||||
"assertHeapAndStackEmpty".to_string(),
|
||||
"function() {
|
||||
if (stack.length > 0)
|
||||
throw new Error('stack is not empty');
|
||||
for (let i = 0; i < slab.length; i++) {
|
||||
if (typeof(slab[i]) !== 'number')
|
||||
throw new Error('slab is not empty');
|
||||
}
|
||||
}".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut exports = String::new();
|
||||
for class in self.classes.iter() {
|
||||
exports.push_str(class);
|
||||
exports.push_str("\n");
|
||||
}
|
||||
for &(ref name, ref body) in self.exports.iter() {
|
||||
exports.push_str("obj.");
|
||||
exports.push_str(name);
|
||||
exports.push_str(" = ");
|
||||
exports.push_str(body);
|
||||
exports.push_str(";\n");
|
||||
}
|
||||
let mut imports = String::new();
|
||||
for import in self.imports.iter() {
|
||||
imports.push_str(import);
|
||||
imports.push_str("\n");
|
||||
}
|
||||
|
||||
if self.expose_objects {
|
||||
imports.push_str("
|
||||
imports.env.__wasm_bindgen_object_clone_ref = cloneRef;
|
||||
imports.env.__wasm_bindgen_object_drop_ref = dropRef;
|
||||
");
|
||||
}
|
||||
format!("
|
||||
{}
|
||||
function xform(obj) {{
|
||||
{}
|
||||
{}
|
||||
return obj;
|
||||
}}
|
||||
export function instantiate(bytes, imports) {{
|
||||
{}
|
||||
return WebAssembly.instantiate(bytes, imports).then(xform);
|
||||
}}
|
||||
", real_globals, globals, exports, imports)
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ use std::io::Write;
|
||||
use failure::{Error, ResultExt};
|
||||
use parity_wasm::elements::*;
|
||||
|
||||
mod js;
|
||||
mod ts;
|
||||
|
||||
pub struct Bindgen {
|
||||
path: Option<PathBuf>,
|
||||
@ -69,16 +69,16 @@ impl Bindgen {
|
||||
}
|
||||
|
||||
impl Object {
|
||||
pub fn write_js_to<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
|
||||
self._write_js_to(path.as_ref())
|
||||
pub fn write_ts_to<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
|
||||
self._write_ts_to(path.as_ref())
|
||||
}
|
||||
|
||||
fn _write_js_to(&self, path: &Path) -> Result<(), Error> {
|
||||
let js = self.generate_js();
|
||||
fn _write_ts_to(&self, path: &Path) -> Result<(), Error> {
|
||||
let ts = self.generate_ts();
|
||||
let mut f = File::create(path).with_context(|_| {
|
||||
format!("failed to create file at {:?}", path)
|
||||
})?;
|
||||
f.write_all(js.as_bytes()).with_context(|_| {
|
||||
f.write_all(ts.as_bytes()).with_context(|_| {
|
||||
format!("failed to write file at {:?}", path)
|
||||
})?;
|
||||
Ok(())
|
||||
@ -95,12 +95,12 @@ impl Object {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_js(&self) -> String {
|
||||
let mut js = js::Js::default();
|
||||
js.nodejs = self.nodejs;
|
||||
js.debug = self.debug;
|
||||
js.generate_program(&self.program);
|
||||
js.to_string()
|
||||
pub fn generate_ts(&self) -> String {
|
||||
let mut ts = ts::Js::default();
|
||||
ts.nodejs = self.nodejs;
|
||||
ts.debug = self.debug;
|
||||
ts.generate_program(&self.program, &self.module);
|
||||
ts.to_string(&self.module)
|
||||
}
|
||||
}
|
||||
|
||||
|
795
crates/wasm-bindgen-cli-support/src/ts.rs
Normal file
795
crates/wasm-bindgen-cli-support/src/ts.rs
Normal file
@ -0,0 +1,795 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use shared;
|
||||
use parity_wasm::elements::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Js {
|
||||
globals: String,
|
||||
exposed_globals: HashSet<&'static str>,
|
||||
exports: Vec<(String, String, String)>,
|
||||
classes: Vec<String>,
|
||||
imports: Vec<(String, String, String)>,
|
||||
pub nodejs: bool,
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
impl Js {
|
||||
|
||||
pub fn generate_program(&mut self,
|
||||
program: &shared::Program,
|
||||
_wasm: &Module) {
|
||||
for f in program.free_functions.iter() {
|
||||
self.generate_free_function(f);
|
||||
}
|
||||
for s in program.structs.iter() {
|
||||
self.generate_struct(s);
|
||||
}
|
||||
for s in program.imports.iter() {
|
||||
self.generate_import(s);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_free_function(&mut self, func: &shared::Function) {
|
||||
let (js, ts) = self.generate_function("function",
|
||||
&func.name,
|
||||
&func.name,
|
||||
false,
|
||||
&func.arguments,
|
||||
func.ret.as_ref());
|
||||
|
||||
self.exports.push((func.name.clone(), js, ts));
|
||||
}
|
||||
|
||||
pub fn generate_struct(&mut self, s: &shared::Struct) {
|
||||
let mut dst = String::new();
|
||||
self.expose_check_token();
|
||||
self.expose_wasm_exports();
|
||||
dst.push_str(&format!("
|
||||
export class {} {{
|
||||
constructor(public __wasmPtr: number, sym: Symbol) {{
|
||||
_checkToken(sym);
|
||||
}}
|
||||
|
||||
free(): void {{
|
||||
const ptr = this.__wasmPtr;
|
||||
this.__wasmPtr = 0;
|
||||
wasm_exports.{}(ptr);
|
||||
}}
|
||||
", s.name, s.free_function()));
|
||||
|
||||
for function in s.functions.iter() {
|
||||
let (js, _ts) = self.generate_function(
|
||||
"static",
|
||||
&function.name,
|
||||
&function.struct_function_export_name(&s.name),
|
||||
false,
|
||||
&function.arguments,
|
||||
function.ret.as_ref(),
|
||||
);
|
||||
dst.push_str(&js);
|
||||
dst.push_str("\n");
|
||||
}
|
||||
for method in s.methods.iter() {
|
||||
let (js, _ts) = self.generate_function(
|
||||
"",
|
||||
&method.function.name,
|
||||
&method.function.struct_function_export_name(&s.name),
|
||||
true,
|
||||
&method.function.arguments,
|
||||
method.function.ret.as_ref(),
|
||||
);
|
||||
dst.push_str(&js);
|
||||
dst.push_str("\n");
|
||||
}
|
||||
dst.push_str("}\n");
|
||||
self.classes.push(dst);
|
||||
|
||||
let ts_export = format!("{0}: typeof {0};", s.name);
|
||||
self.exports.push((s.name.clone(), s.name.clone(), ts_export));
|
||||
}
|
||||
|
||||
fn generate_function(&mut self,
|
||||
prefix: &str,
|
||||
name: &str,
|
||||
wasm_name: &str,
|
||||
is_method: bool,
|
||||
arguments: &[shared::Type],
|
||||
ret: Option<&shared::Type>) -> (String, String) {
|
||||
let mut dst = format!("{}(", 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.__wasmPtr");
|
||||
}
|
||||
|
||||
for (i, arg) in arguments.iter().enumerate() {
|
||||
let name = format!("arg{}", i);
|
||||
if i > 0 {
|
||||
dst.push_str(", ");
|
||||
}
|
||||
dst.push_str(&name);
|
||||
dst.push_str(": ");
|
||||
|
||||
let mut pass = |arg: &str| {
|
||||
if passed_args.len() > 0 {
|
||||
passed_args.push_str(", ");
|
||||
}
|
||||
passed_args.push_str(arg);
|
||||
};
|
||||
match *arg {
|
||||
shared::Type::Number => {
|
||||
dst.push_str("number");
|
||||
self.expose_assert_num();
|
||||
arg_conversions.push_str(&format!("_assertNum({});\n", name));
|
||||
pass(&name)
|
||||
}
|
||||
shared::Type::BorrowedStr |
|
||||
shared::Type::String => {
|
||||
dst.push_str("string");
|
||||
self.expose_pass_string_to_wasm();
|
||||
arg_conversions.push_str(&format!("\
|
||||
const [ptr{i}, len{i}] = passStringToWasm({arg});
|
||||
", i = i, arg = name));
|
||||
pass(&format!("ptr{}", i));
|
||||
pass(&format!("len{}", i));
|
||||
if let shared::Type::BorrowedStr = *arg {
|
||||
self.expose_wasm_exports();
|
||||
destructors.push_str(&format!("\n\
|
||||
wasm_exports.__wbindgen_free(ptr{i}, len{i});\n\
|
||||
", i = i));
|
||||
}
|
||||
}
|
||||
shared::Type::ByRef(ref s) |
|
||||
shared::Type::ByMutRef(ref s) => {
|
||||
dst.push_str(s);
|
||||
self.expose_assert_class();
|
||||
arg_conversions.push_str(&format!("\
|
||||
const ptr{i} = _assertClass({arg}, {struct_});
|
||||
", i = i, arg = name, struct_ = s));
|
||||
pass(&format!("ptr{}", i));
|
||||
}
|
||||
shared::Type::ByValue(ref s) => {
|
||||
dst.push_str(s);
|
||||
self.expose_assert_class();
|
||||
arg_conversions.push_str(&format!("\
|
||||
const ptr{i} = _assertClass({arg}, {struct_});
|
||||
{arg}.__wasmPtr = 0;
|
||||
", i = i, arg = name, struct_ = s));
|
||||
pass(&format!("ptr{}", i));
|
||||
}
|
||||
shared::Type::JsObject => {
|
||||
dst.push_str("any");
|
||||
self.expose_add_heap_object();
|
||||
arg_conversions.push_str(&format!("\
|
||||
const idx{i} = addHeapObject({arg});
|
||||
", i = i, arg = name));
|
||||
pass(&format!("idx{}", i));
|
||||
}
|
||||
shared::Type::JsObjectRef => {
|
||||
dst.push_str("any");
|
||||
self.expose_borrowed_objects();
|
||||
arg_conversions.push_str(&format!("\
|
||||
const idx{i} = addBorrowedObject({arg});
|
||||
", i = i, arg = name));
|
||||
destructors.push_str("popBorrowedObject();\n");
|
||||
pass(&format!("idx{}", i));
|
||||
}
|
||||
}
|
||||
}
|
||||
dst.push_str("): ");
|
||||
let convert_ret = match ret {
|
||||
None => {
|
||||
dst.push_str("void");
|
||||
format!("return ret;")
|
||||
}
|
||||
Some(&shared::Type::Number) => {
|
||||
dst.push_str("number");
|
||||
format!("return ret;")
|
||||
}
|
||||
Some(&shared::Type::JsObject) => {
|
||||
dst.push_str("any");
|
||||
self.expose_take_object();
|
||||
format!("return takeObject(ret);")
|
||||
}
|
||||
Some(&shared::Type::JsObjectRef) |
|
||||
Some(&shared::Type::BorrowedStr) |
|
||||
Some(&shared::Type::ByMutRef(_)) |
|
||||
Some(&shared::Type::ByRef(_)) => panic!(),
|
||||
Some(&shared::Type::ByValue(ref name)) => {
|
||||
dst.push_str(name);
|
||||
format!("\
|
||||
return new {name}(ret, token);
|
||||
", name = name)
|
||||
}
|
||||
Some(&shared::Type::String) => {
|
||||
dst.push_str("string");
|
||||
self.expose_get_string_from_wasm();
|
||||
self.expose_wasm_exports();
|
||||
format!("
|
||||
const ptr = wasm_exports.__wbindgen_boxed_str_ptr(ret);
|
||||
const len = wasm_exports.__wbindgen_boxed_str_len(ret);
|
||||
const realRet = getStringFromWasm(ptr, len);
|
||||
wasm_exports.__wbindgen_boxed_str_free(ret);
|
||||
return realRet;
|
||||
")
|
||||
}
|
||||
};
|
||||
let mut dst_ts = dst.clone();
|
||||
dst_ts.push_str(";");
|
||||
dst.push_str(" {\n ");
|
||||
dst.push_str(&arg_conversions);
|
||||
self.expose_wasm_exports();
|
||||
if destructors.len() == 0 {
|
||||
dst.push_str(&format!("\
|
||||
const ret = wasm_exports.{f}({passed});
|
||||
{convert_ret}
|
||||
", f = wasm_name, passed = passed_args, convert_ret = convert_ret));
|
||||
} else {
|
||||
dst.push_str(&format!("\
|
||||
try {{
|
||||
const ret = wasm_exports.{f}({passed});
|
||||
{convert_ret}
|
||||
}} finally {{
|
||||
{destructors}
|
||||
}}
|
||||
", f = wasm_name, passed = passed_args, destructors = destructors,
|
||||
convert_ret = convert_ret));
|
||||
}
|
||||
dst.push_str("}");
|
||||
(format!("{} {}", prefix, dst), dst_ts)
|
||||
}
|
||||
|
||||
pub fn generate_import(&mut self, import: &shared::Function) {
|
||||
let mut dst = String::new();
|
||||
let mut ts_dst = String::new();
|
||||
|
||||
dst.push_str(&format!("function {0}_shim(", import.name));
|
||||
|
||||
ts_dst.push_str(&import.name);
|
||||
ts_dst.push_str("(");
|
||||
|
||||
let mut invocation = String::new();
|
||||
for (i, arg) in import.arguments.iter().enumerate() {
|
||||
if invocation.len() > 0 {
|
||||
invocation.push_str(", ");
|
||||
}
|
||||
if i > 0 {
|
||||
dst.push_str(", ");
|
||||
ts_dst.push_str(", ");
|
||||
}
|
||||
ts_dst.push_str(&format!("arg{}: ", i));
|
||||
match *arg {
|
||||
shared::Type::Number => {
|
||||
ts_dst.push_str("number");
|
||||
invocation.push_str(&format!("arg{}", i));
|
||||
dst.push_str(&format!("arg{}: number", i));
|
||||
}
|
||||
shared::Type::BorrowedStr => {
|
||||
ts_dst.push_str("string");
|
||||
self.expose_get_string_from_wasm();
|
||||
invocation.push_str(&format!("getStringFromWasm(ptr{0}, len{0})", i));
|
||||
dst.push_str(&format!("ptr{0}: number, len{0}: number", i));
|
||||
}
|
||||
shared::Type::JsObject => {
|
||||
ts_dst.push_str("any");
|
||||
self.expose_take_object();
|
||||
invocation.push_str(&format!("takeObject(arg{})", i));
|
||||
dst.push_str(&format!("arg{}: number", i));
|
||||
}
|
||||
shared::Type::JsObjectRef => {
|
||||
ts_dst.push_str("any");
|
||||
self.expose_get_object();
|
||||
invocation.push_str(&format!("getObject(arg{})", i));
|
||||
dst.push_str(&format!("arg{}: number", i));
|
||||
}
|
||||
shared::Type::String |
|
||||
shared::Type::ByRef(_) |
|
||||
shared::Type::ByMutRef(_) |
|
||||
shared::Type::ByValue(_) => {
|
||||
panic!("unsupported type in import");
|
||||
}
|
||||
}
|
||||
}
|
||||
ts_dst.push_str("): ");
|
||||
dst.push_str("): ");
|
||||
match import.ret {
|
||||
Some(shared::Type::Number) => {
|
||||
ts_dst.push_str("number");
|
||||
dst.push_str("number");
|
||||
}
|
||||
None => {
|
||||
ts_dst.push_str("void");
|
||||
dst.push_str("void");
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
ts_dst.push_str("\n");
|
||||
dst.push_str(" {\n");
|
||||
dst.push_str(&format!("return imports.{}({});\n}}", import.name, invocation));
|
||||
|
||||
self.imports.push((import.name.clone(), dst, ts_dst));
|
||||
}
|
||||
|
||||
pub fn to_string(&mut self, m: &Module) -> String {
|
||||
if self.debug {
|
||||
self.expose_global_slab();
|
||||
self.expose_global_stack();
|
||||
self.exports.push(
|
||||
(
|
||||
"assertHeapAndStackEmpty".to_string(),
|
||||
"function(): void {
|
||||
if (stack.length > 0)
|
||||
throw new Error('stack is not empty');
|
||||
for (let i = 0; i < slab.length; i++) {
|
||||
if (typeof(slab[i]) !== 'number')
|
||||
throw new Error('slab is not empty');
|
||||
}
|
||||
}".to_string(),
|
||||
"assertHeapAndStackEmpty(): void;\n".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
for class in self.classes.iter() {
|
||||
self.globals.push_str(class);
|
||||
self.globals.push_str("\n");
|
||||
}
|
||||
let mut typescript_exports = String::new();
|
||||
let mut exports = format!("\
|
||||
{{
|
||||
module,
|
||||
instance,
|
||||
");
|
||||
for &(ref name, ref body, ref ts_export) in self.exports.iter() {
|
||||
exports.push_str(name);
|
||||
exports.push_str(": ");
|
||||
exports.push_str(body);
|
||||
exports.push_str(",\n");
|
||||
typescript_exports.push_str(ts_export);
|
||||
typescript_exports.push_str("\n");
|
||||
}
|
||||
exports.push_str("}");
|
||||
let mut imports = String::new();
|
||||
let mut typescript_imports = String::new();
|
||||
for &(ref import, ref val, ref ts_import) in self.imports.iter() {
|
||||
imports.push_str(import);
|
||||
imports.push_str(":");
|
||||
imports.push_str(val);
|
||||
imports.push_str(",\n");
|
||||
typescript_imports.push_str(ts_import);
|
||||
typescript_imports.push_str("\n");
|
||||
}
|
||||
|
||||
if self.wasm_import_needed("__wasm_bindgen_object_clone_ref", m) {
|
||||
self.expose_add_heap_object();
|
||||
self.expose_get_object();
|
||||
imports.push_str("
|
||||
__wasm_bindgen_object_clone_ref: function(idx: number): number {
|
||||
// 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];
|
||||
if (typeof(val) === 'number')
|
||||
throw new Error('corrupt slab');
|
||||
val.cnt += 1;
|
||||
return idx;
|
||||
},
|
||||
");
|
||||
}
|
||||
|
||||
if self.wasm_import_needed("__wasm_bindgen_object_drop_ref", m) {
|
||||
self.expose_drop_ref();
|
||||
imports.push_str("__wasm_bindgen_object_drop_ref: dropRef,\n");
|
||||
}
|
||||
|
||||
let mut writes = String::new();
|
||||
if self.exposed_globals.contains(&"memory") {
|
||||
writes.push_str("memory = exports.memory;\n");
|
||||
}
|
||||
if self.exposed_globals.contains(&"wasm_exports") {
|
||||
writes.push_str("wasm_exports = exports;\n");
|
||||
}
|
||||
format!("
|
||||
{globals}
|
||||
|
||||
interface WasmImportsTop {{
|
||||
env: WasmImports,
|
||||
}}
|
||||
|
||||
interface WasmImports {{
|
||||
{wasm_imports}
|
||||
}}
|
||||
|
||||
interface WasmExports {{
|
||||
{wasm_exports}
|
||||
}}
|
||||
|
||||
export interface Imports {{
|
||||
{typescript_imports}
|
||||
}}
|
||||
|
||||
export interface Exports {{
|
||||
module: WebAssembly.Module;
|
||||
instance: WebAssembly.Module;
|
||||
{typescript_exports}
|
||||
}}
|
||||
|
||||
function xform(obj: WebAssembly.ResultObject): Exports {{
|
||||
let {{ module, instance }} = obj;
|
||||
let {{ exports }} = instance;
|
||||
{writes}
|
||||
return {exports};
|
||||
}}
|
||||
export function instantiate(bytes: any, imports: Imports): Promise<Exports> {{
|
||||
let wasm_imports: WasmImportsTop = {{
|
||||
env: {{
|
||||
{imports}
|
||||
}},
|
||||
}};
|
||||
return WebAssembly.instantiate(bytes, wasm_imports).then(xform);
|
||||
}}
|
||||
",
|
||||
globals = self.globals,
|
||||
exports = exports,
|
||||
imports = imports,
|
||||
writes = writes,
|
||||
typescript_imports = typescript_imports,
|
||||
typescript_exports = typescript_exports,
|
||||
wasm_imports = self.typescript_wasm_imports(m),
|
||||
wasm_exports = self.typescript_wasm_exports(m),
|
||||
)
|
||||
}
|
||||
|
||||
fn wasm_import_needed(&self, name: &str, m: &Module) -> bool {
|
||||
let imports = match m.import_section() {
|
||||
Some(s) => s,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
imports.entries().iter().any(|i| {
|
||||
i.module() == "env" && i.field() == name
|
||||
})
|
||||
}
|
||||
|
||||
fn typescript_wasm_imports(&self, m: &Module) -> String {
|
||||
let imports = match m.import_section() {
|
||||
Some(s) => s,
|
||||
None => return String::new(),
|
||||
};
|
||||
let types = match m.type_section() {
|
||||
Some(s) => s,
|
||||
None => return String::new(),
|
||||
};
|
||||
|
||||
let mut ts = String::new();
|
||||
for import in imports.entries() {
|
||||
assert_eq!(import.module(), "env");
|
||||
|
||||
let ty = match *import.external() {
|
||||
External::Function(i) => {
|
||||
match types.types()[i as usize] {
|
||||
Type::Function(ref t) => t,
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
ts.push_str(import.field());
|
||||
ts.push_str("(");
|
||||
// TODO: probably match `arg` to catch exhaustive errors in the
|
||||
// future
|
||||
for (i, _arg) in ty.params().iter().enumerate() {
|
||||
if i > 0 {
|
||||
ts.push_str(", ");
|
||||
}
|
||||
ts.push_str(&format!("arg{}: number", i));
|
||||
}
|
||||
ts.push_str("): ");
|
||||
if ty.return_type().is_none() {
|
||||
ts.push_str("void");
|
||||
} else {
|
||||
ts.push_str("number");
|
||||
}
|
||||
ts.push_str(";\n");
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
||||
fn typescript_wasm_exports(&self, m: &Module) -> String {
|
||||
let imported_functions = match m.import_section() {
|
||||
Some(s) => s.functions(),
|
||||
None => 0,
|
||||
};
|
||||
let functions = match m.function_section() {
|
||||
Some(s) => s,
|
||||
None => return String::new(),
|
||||
};
|
||||
let types = match m.type_section() {
|
||||
Some(s) => s,
|
||||
None => return String::new(),
|
||||
};
|
||||
let exports = match m.export_section() {
|
||||
Some(s) => s,
|
||||
None => return String::new(),
|
||||
};
|
||||
|
||||
let mut ts = String::new();
|
||||
for export in exports.entries() {
|
||||
let fn_idx = match *export.internal() {
|
||||
Internal::Function(i) => i as usize,
|
||||
_ => continue,
|
||||
};
|
||||
assert!(fn_idx >= imported_functions);
|
||||
let function = &functions.entries()[fn_idx - imported_functions];
|
||||
let ty = match types.types()[function.type_ref() as usize] {
|
||||
Type::Function(ref t) => t,
|
||||
};
|
||||
|
||||
ts.push_str(export.field());
|
||||
ts.push_str("(");
|
||||
// TODO: probably match `arg` to catch exhaustive errors in the
|
||||
// future
|
||||
for (i, _arg) in ty.params().iter().enumerate() {
|
||||
if i > 0 {
|
||||
ts.push_str(", ");
|
||||
}
|
||||
ts.push_str(&format!("arg{}: number", i));
|
||||
}
|
||||
ts.push_str("): ");
|
||||
if ty.return_type().is_none() {
|
||||
ts.push_str("void");
|
||||
} else {
|
||||
ts.push_str("number");
|
||||
}
|
||||
ts.push_str(";\n");
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
||||
fn expose_drop_ref(&mut self) {
|
||||
if !self.exposed_globals.insert("drop_ref") {
|
||||
return
|
||||
}
|
||||
self.expose_global_slab();
|
||||
self.expose_global_slab_next();
|
||||
self.globals.push_str("
|
||||
function dropRef(idx: number): void {
|
||||
if ((idx & 1) == 1)
|
||||
throw new Error('cannot drop ref of stack objects');
|
||||
|
||||
// Decrement our refcount, but if it's still larger than one
|
||||
// keep going
|
||||
let obj = slab[idx >> 1];
|
||||
if (typeof(obj) === 'number')
|
||||
throw new Error('corrupt slab');
|
||||
obj.cnt -= 1;
|
||||
if (obj.cnt > 0)
|
||||
return;
|
||||
|
||||
// If we hit 0 then free up our space in the slab
|
||||
slab[idx >> 1] = slab_next;
|
||||
slab_next = idx >> 1;
|
||||
}
|
||||
");
|
||||
}
|
||||
|
||||
fn expose_global_stack(&mut self) {
|
||||
if !self.exposed_globals.insert("stack") {
|
||||
return
|
||||
}
|
||||
self.globals.push_str("
|
||||
let stack: any[] = [];
|
||||
");
|
||||
}
|
||||
|
||||
fn expose_global_slab(&mut self) {
|
||||
if !self.exposed_globals.insert("slab") {
|
||||
return
|
||||
}
|
||||
self.globals.push_str("
|
||||
let slab: ({ obj: any, cnt: number } | number)[] = [];
|
||||
");
|
||||
}
|
||||
|
||||
fn expose_global_slab_next(&mut self) {
|
||||
if !self.exposed_globals.insert("slab_next") {
|
||||
return
|
||||
}
|
||||
self.globals.push_str("
|
||||
let slab_next: number = 0;
|
||||
");
|
||||
}
|
||||
|
||||
fn expose_get_object(&mut self) {
|
||||
if !self.exposed_globals.insert("get_object") {
|
||||
return
|
||||
}
|
||||
self.expose_global_stack();
|
||||
self.expose_global_slab();
|
||||
self.globals.push_str("
|
||||
function getObject(idx: number): any {
|
||||
if ((idx & 1) === 1) {
|
||||
return stack[idx >> 1];
|
||||
} else {
|
||||
const val = slab[idx >> 1];
|
||||
if (typeof(val) === 'number')
|
||||
throw new Error('corrupt slab');
|
||||
return val.obj;
|
||||
}
|
||||
}
|
||||
");
|
||||
}
|
||||
|
||||
fn expose_global_memory(&mut self) {
|
||||
if !self.exposed_globals.insert("memory") {
|
||||
return
|
||||
}
|
||||
self.globals.push_str("let memory: WebAssembly.Memory;\n");
|
||||
}
|
||||
|
||||
fn expose_wasm_exports(&mut self) {
|
||||
if !self.exposed_globals.insert("wasm_exports") {
|
||||
return
|
||||
}
|
||||
self.globals.push_str("let wasm_exports: WasmExports;\n");
|
||||
}
|
||||
|
||||
fn expose_check_token(&mut self) {
|
||||
if !self.exposed_globals.insert("check_token") {
|
||||
return
|
||||
}
|
||||
self.globals.push_str("\
|
||||
const token = Symbol('foo');
|
||||
function _checkToken(sym: Symbol): void {
|
||||
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("\
|
||||
function _assertNum(n: number): void {
|
||||
if (typeof(n) !== 'number')
|
||||
throw new Error('expected a number argument');
|
||||
}
|
||||
");
|
||||
}
|
||||
|
||||
fn expose_pass_string_to_wasm(&mut self) {
|
||||
if !self.exposed_globals.insert("pass_string_to_wasm") {
|
||||
return
|
||||
}
|
||||
self.expose_wasm_exports();
|
||||
self.expose_global_memory();
|
||||
if self.nodejs {
|
||||
self.globals.push_str("
|
||||
function passStringToWasm(arg: string): [number, number] {
|
||||
if (typeof(arg) !== 'string')
|
||||
throw new Error('expected a string argument');
|
||||
const buf = Buffer.from(arg);
|
||||
const len = buf.length;
|
||||
const ptr = wasm_exports.__wbindgen_malloc(len);
|
||||
buf.copy(Buffer.from(memory.buffer), ptr);
|
||||
return [ptr, len];
|
||||
}
|
||||
");
|
||||
} else {
|
||||
self.globals.push_str("
|
||||
function passStringToWasm(arg: string): [number, number] {
|
||||
if (typeof(arg) !== 'string')
|
||||
throw new Error('expected a string argument');
|
||||
const buf = new TextEncoder('utf-8').encode(arg);
|
||||
const len = buf.length;
|
||||
const ptr = wasm_exports.__wbindgen_malloc(len);
|
||||
let array = new Uint8Array(memory.buffer);
|
||||
array.set(buf, ptr);
|
||||
return [ptr, len];
|
||||
}
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
fn expose_get_string_from_wasm(&mut self) {
|
||||
if !self.exposed_globals.insert("get_string_from_wasm") {
|
||||
return
|
||||
}
|
||||
if self.nodejs {
|
||||
self.expose_global_memory();
|
||||
self.globals.push_str("
|
||||
function getStringFromWasm(ptr: number, len: number): string {
|
||||
const buf = Buffer.from(memory.buffer).slice(ptr, ptr + len);
|
||||
const ret = buf.toString();
|
||||
return ret;
|
||||
}
|
||||
");
|
||||
} else {
|
||||
self.expose_global_memory();
|
||||
self.globals.push_str("
|
||||
function getStringFromWasm(ptr: number, len: number): string {
|
||||
const mem = new Uint8Array(memory.buffer);
|
||||
const slice = mem.slice(ptr, ptr + len);
|
||||
const ret = new TextDecoder('utf-8').decode(slice);
|
||||
return ret;
|
||||
}
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
fn expose_assert_class(&mut self) {
|
||||
if !self.exposed_globals.insert("assert_class") {
|
||||
return
|
||||
}
|
||||
self.globals.push_str("
|
||||
function _assertClass(instance: any, klass: any) {
|
||||
if (!(instance instanceof klass))
|
||||
throw new Error(`expected instance of ${klass.name}`);
|
||||
return instance.__wasmPtr;
|
||||
}
|
||||
");
|
||||
}
|
||||
|
||||
fn expose_borrowed_objects(&mut self) {
|
||||
if !self.exposed_globals.insert("borrowed_objects") {
|
||||
return
|
||||
}
|
||||
self.expose_global_stack();
|
||||
self.globals.push_str("
|
||||
function addBorrowedObject(obj: any): number {
|
||||
stack.push(obj);
|
||||
return ((stack.length - 1) << 1) | 1;
|
||||
}
|
||||
|
||||
function popBorrowedObject(): void {
|
||||
stack.pop();
|
||||
}
|
||||
");
|
||||
}
|
||||
|
||||
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("
|
||||
function takeObject(idx: number): any {
|
||||
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();
|
||||
self.globals.push_str("
|
||||
function addHeapObject(obj: any): number {
|
||||
if (slab_next == slab.length) {
|
||||
slab.push(slab.length + 1);
|
||||
}
|
||||
const idx = slab_next;
|
||||
const next = slab[idx];
|
||||
if (typeof(next) !== 'number')
|
||||
throw new Error('corrupt slab');
|
||||
slab_next = next;
|
||||
slab[idx] = { obj, cnt: 1 };
|
||||
return idx << 1;
|
||||
}
|
||||
");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user