Fix some class import methods and auto gc

The runtime functions are now moved to the `wasm-bindgen` crate and are
auto-gc'd if they don't end up actually being required.
This commit is contained in:
Alex Crichton 2018-02-06 08:58:15 -08:00
parent 28966d9853
commit 56b7fa453a
7 changed files with 95 additions and 102 deletions

View File

@ -4,8 +4,9 @@ version = "0.1.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
[dependencies]
parity-wasm = "0.23"
failure = "0.1"
wasm-bindgen-shared = { path = "../wasm-bindgen-shared" }
serde_json = "1.0"
base64 = "0.9"
failure = "0.1"
parity-wasm = "0.23"
serde_json = "1.0"
wasm-bindgen-shared = { path = "../wasm-bindgen-shared" }
wasm-gc-api = "0.1"

View File

@ -10,6 +10,7 @@ pub struct Js<'a> {
pub imports: 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 program: &'a shared::Program,
@ -188,6 +189,7 @@ impl<'a> Js<'a> {
);
self.rewrite_imports(module_name);
self.unexport_unused_internal_exports();
(js, self.typescript.clone())
}
@ -343,6 +345,7 @@ impl<'a> Js<'a> {
destructors.push_str(&format!("\n\
wasm.__wbindgen_free(ptr{i}, len{i});\n\
", i = i));
self.required_internal_exports.insert("__wbindgen_free");
}
}
shared::TYPE_JS_OWNED => {
@ -416,6 +419,9 @@ impl<'a> Js<'a> {
Some(&shared::TYPE_STRING) => {
dst_ts.push_str(": string");
self.expose_get_string_from_wasm();
self.required_internal_exports.insert("__wbindgen_boxed_str_ptr");
self.required_internal_exports.insert("__wbindgen_boxed_str_len");
self.required_internal_exports.insert("__wbindgen_boxed_str_free");
format!("
const ptr = wasm.__wbindgen_boxed_str_ptr(ret);
const len = wasm.__wbindgen_boxed_str_len(ret);
@ -567,6 +573,7 @@ impl<'a> Js<'a> {
wasm.__wbindgen_free(ptr{0}, len{0});
", i));
invocation.push_str(&format!("arg{}", i));
self.required_internal_exports.insert("__wbindgen_free");
}
shared::TYPE_JS_OWNED => {
self.expose_take_object();
@ -666,6 +673,20 @@ impl<'a> Js<'a> {
}
}
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
@ -805,6 +826,7 @@ impl<'a> Js<'a> {
if !self.exposed_globals.insert("pass_string_to_wasm") {
return
}
self.required_internal_exports.insert("__wbindgen_malloc");
if self.config.nodejs {
self.globals.push_str(&format!("
function passStringToWasm(arg) {{

View File

@ -3,6 +3,7 @@ extern crate failure;
extern crate parity_wasm;
extern crate wasm_bindgen_shared as shared;
extern crate serde_json;
extern crate wasm_gc;
use std::fs::File;
use std::io::Write;
@ -71,6 +72,7 @@ impl Bindgen {
imports: String::new(),
typescript: format!("/* tslint:disable */\n"),
exposed_globals: Default::default(),
required_internal_exports: Default::default(),
config: &self,
module: &mut module,
program: &program,
@ -87,9 +89,13 @@ impl Bindgen {
}
let wasm_path = out_dir.join(format!("{}_wasm", stem)).with_extension("wasm");
parity_wasm::serialize_to_file(wasm_path, module).map_err(|e| {
let wasm_bytes = parity_wasm::serialize(module).map_err(|e| {
format_err!("{:?}", e)
})?;
let bytes = wasm_gc::Config::new()
.demangle(false)
.gc(&wasm_bytes)?;
File::create(&wasm_path)?.write_all(&bytes)?;
Ok(())
}
}

View File

@ -23,9 +23,6 @@ macro_rules! my_quote {
mod ast;
static MALLOC_GENERATED: AtomicBool = ATOMIC_BOOL_INIT;
static BOXED_STR_GENERATED: AtomicBool = ATOMIC_BOOL_INIT;
#[proc_macro]
pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
// Parse the input as a list of Rust items, reusing the `syn::File` parser.
@ -214,9 +211,6 @@ fn bindgen(export_name: &syn::LitStr,
let mut converted_arguments = vec![];
let ret = syn::Ident::from("_ret");
let mut malloc = false;
let mut boxed_str = false;
let mut offset = 0;
if let Receiver::StructMethod(class, _, _) = receiver {
args.push(my_quote! { me: *mut ::wasm_bindgen::__rt::WasmRefCell<#class> });
@ -232,7 +226,6 @@ fn bindgen(export_name: &syn::LitStr,
let ident = syn::Ident::from(format!("arg{}", i));
match *ty {
ast::Type::BorrowedStr => {
malloc = malloc || !MALLOC_GENERATED.swap(true, Ordering::SeqCst);
let ptr = syn::Ident::from(format!("arg{}_ptr", i));
let len = syn::Ident::from(format!("arg{}_len", i));
args.push(my_quote! { #ptr: *const u8 });
@ -245,7 +238,6 @@ fn bindgen(export_name: &syn::LitStr,
});
}
ast::Type::String => {
malloc = malloc || !MALLOC_GENERATED.swap(true, Ordering::SeqCst);
let ptr = syn::Ident::from(format!("arg{}_ptr", i));
let len = syn::Ident::from(format!("arg{}_len", i));
args.push(my_quote! { #ptr: *mut u8 });
@ -299,7 +291,6 @@ fn bindgen(export_name: &syn::LitStr,
let convert_ret;
match ret_type {
Some(&ast::Type::String) => {
boxed_str = !BOXED_STR_GENERATED.swap(true, Ordering::SeqCst);
ret_ty = my_quote! { -> *mut String };
convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) };
}
@ -322,65 +313,7 @@ fn bindgen(export_name: &syn::LitStr,
}
}
// TODO: move this function into wasm-bindgen-the-crate and then gc it out
// if it's not used.
let malloc = if malloc {
my_quote! {
#[no_mangle]
pub extern fn __wbindgen_malloc(size: usize) -> *mut u8 {
// Any malloc request this big is bogus anyway. If this actually
// goes down to `Vec` we trigger a whole bunch of panicking
// machinery to get pulled in from libstd anyway as it'll verify
// the size passed in below.
//
// Head this all off by just aborting on too-big sizes. This
// avoids panicking (code bloat) and gives a better error
// message too hopefully.
if size >= usize::max_value() / 2 {
::wasm_bindgen::throw("invalid malloc request");
}
let mut ret = Vec::with_capacity(size);
let ptr = ret.as_mut_ptr();
::std::mem::forget(ret);
return ptr
}
#[no_mangle]
pub unsafe extern fn __wbindgen_free(ptr: *mut u8, size: usize) {
drop(Vec::<u8>::from_raw_parts(ptr, 0, size));
}
}
} else {
my_quote! {
}
};
let boxed_str = if boxed_str {
my_quote! {
#[no_mangle]
pub unsafe extern fn __wbindgen_boxed_str_len(ptr: *mut String) -> usize {
(*ptr).len()
}
#[no_mangle]
pub unsafe extern fn __wbindgen_boxed_str_ptr(ptr: *mut String) -> *const u8 {
(*ptr).as_ptr()
}
#[no_mangle]
pub unsafe extern fn __wbindgen_boxed_str_free(ptr: *mut String) {
drop(Box::from_raw(ptr));
}
}
} else {
my_quote! {
}
};
let tokens = my_quote! {
#malloc
#boxed_str
#[export_name = #export_name]
#[allow(non_snake_case)]
pub extern fn #generated_name(#(#args),*) #ret_ty {

View File

@ -90,25 +90,3 @@ pub const TYPE_JS_REF: char = '\u{63}';
pub const TYPE_CUSTOM_START: u32 = 0x64;
pub const TYPE_CUSTOM_REF_FLAG: u32 = 1;
// #[derive(Serialize, Deserialize)]
// pub enum Type {
// Number,
// BorrowedStr,
// String,
// ByValue(String), // wrapper class
// ByRef(String), // wrapper class
// ByMutRef(String), // wrapper class
// JsObject,
// JsObjectRef,
// Boolean,
// }
// impl Type {
// pub fn is_number(&self) -> bool {
// match *self {
// Type::Number => true,
// _ => false,
// }
// }
// }

View File

@ -261,6 +261,7 @@ pub fn throw(s: &str) -> ! {
#[doc(hidden)]
pub mod __rt {
use std::cell::{Cell, UnsafeCell};
use std::mem;
use std::ops::{Deref, DerefMut};
#[inline]
@ -394,4 +395,43 @@ pub mod __rt {
super::throw("recursive use of an object detected which would lead to \
unsafe aliasing in rust");
}
#[no_mangle]
pub extern fn __wbindgen_malloc(size: usize) -> *mut u8 {
// Any malloc request this big is bogus anyway. If this actually
// goes down to `Vec` we trigger a whole bunch of panicking
// machinery to get pulled in from libstd anyway as it'll verify
// the size passed in below.
//
// Head this all off by just aborting on too-big sizes. This
// avoids panicking (code bloat) and gives a better error
// message too hopefully.
if size >= usize::max_value() / 2 {
super::throw("invalid malloc request");
}
let mut ret = Vec::with_capacity(size);
let ptr = ret.as_mut_ptr();
mem::forget(ret);
return ptr
}
#[no_mangle]
pub unsafe extern fn __wbindgen_free(ptr: *mut u8, size: usize) {
drop(Vec::<u8>::from_raw_parts(ptr, 0, size));
}
#[no_mangle]
pub unsafe extern fn __wbindgen_boxed_str_len(ptr: *mut String) -> usize {
(*ptr).len()
}
#[no_mangle]
pub unsafe extern fn __wbindgen_boxed_str_ptr(ptr: *mut String) -> *const u8 {
(*ptr).as_ptr()
}
#[no_mangle]
pub unsafe extern fn __wbindgen_boxed_str_free(ptr: *mut String) {
drop(Box::from_raw(ptr));
}
}

View File

@ -92,11 +92,16 @@ fn construct() {
#[wasm_module = "./test"]
extern struct Foo {
fn create() -> Foo;
fn doit(&self);
fn get_internal_string(&self) -> String;
fn append_to_internal_string(&self, s: &str);
fn assert_internal_string(&self, s: &str);
}
pub fn bar() {
Foo::create().doit();
pub fn run() {
let f = Foo::create();
assert_eq!(f.get_internal_string(), "this");
f.append_to_internal_string(" foo");
f.assert_internal_string("this foo");
}
}
"#)
@ -107,22 +112,30 @@ fn construct() {
let called = false;
export class Foo {
private random_property: string = '';
private internal_string: string = '';
static create() {
const ret = new Foo();
ret.random_property = 'this';
ret.internal_string = 'this';
return ret;
}
doit() {
assert.strictEqual(this.random_property, 'this');
get_internal_string() {
return this.internal_string;
}
append_to_internal_string(s: string) {
this.internal_string += s;
}
assert_internal_string(s: string) {
assert.strictEqual(this.internal_string, s);
called = true;
}
}
export function test() {
wasm.bar();
wasm.run();
assert.strictEqual(called, true);
}
"#)