Merge pull request #713 from alexcrichton/weakref

Add experimental support for `WeakRef`
This commit is contained in:
Alex Crichton 2018-08-20 11:18:40 -07:00 committed by GitHub
commit 285a7bf7da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 73 additions and 6 deletions

View File

@ -490,6 +490,39 @@ impl<'a> Context<'a> {
let mut dst = format!("class {} {{\n", name);
let mut ts_dst = format!("export {}", dst);
let (mkweakref, freeref) = if self.config.weak_refs {
// When weak refs are enabled we use them to automatically free the
// contents of an exported rust class when it's gc'd. Note that a
// manual `free` function still exists for deterministic
// destruction.
//
// This is implemented by using a `WeakRefGroup` to run finalizers
// for all `WeakRef` objects that it creates. Upon construction of
// a new wasm object we use `makeRef` with "holdings" of a thunk to
// free the wasm instance. Once the `this` (the instance we're
// creating) is gc'd then the finalizer will run with the
// `WeakRef`, and we'll pull out the `holdings`, our pointer.
//
// Note, though, that if manual finalization happens we want to
// cancel the `WeakRef`-generated finalization, so we retain the
// `WeakRef` in a global map. This global map is then used to
// `drop()` the `WeakRef` (cancel finalization) whenever it is
// finalized.
self.expose_cleanup_groups();
let mk = format!("
const cleanup_ptr = this.ptr;
const ref = CLEANUPS.makeRef(this, () => free{}(cleanup_ptr));
CLEANUPS_MAP.set(this.ptr, ref);
", name);
let free = "
CLEANUPS_MAP.get(ptr).drop();
CLEANUPS_MAP.delete(ptr);
";
(mk, free)
} else {
(String::new(), "")
};
if self.config.debug || class.constructor.is_some() {
self.expose_constructor_token();
@ -516,9 +549,11 @@ impl<'a> Context<'a> {
// This invocation of new will call this constructor with a ConstructorToken
let instance = {class}.{constructor}(...args);
this.ptr = instance.ptr;
{mkweakref}
",
class = name,
constructor = constructor
constructor = constructor,
mkweakref = mkweakref,
));
} else {
dst.push_str(
@ -537,9 +572,11 @@ impl<'a> Context<'a> {
constructor(ptr) {{
this.ptr = ptr;
{}
}}
",
name
name,
mkweakref,
));
}
@ -599,16 +636,29 @@ impl<'a> Context<'a> {
}
}
dst.push_str(&format!(
self.global(&format!(
"
free() {{
const ptr = this.ptr;
this.ptr = 0;
function free{}(ptr) {{
{}
wasm.{}(ptr);
}}
",
name,
freeref,
shared::free_function(&name)
));
dst.push_str(&format!(
"
free() {{
if (this.ptr === 0)
return;
const ptr = this.ptr;
this.ptr = 0;
free{}(this.ptr);
}}
",
name,
));
ts_dst.push_str("free(): void;\n");
dst.push_str(&class.contents);
ts_dst.push_str(&class.typescript);
@ -1594,6 +1644,18 @@ impl<'a> Context<'a> {
");
}
fn expose_cleanup_groups(&mut self) {
if !self.exposed_globals.insert("cleanup_groups") {
return
}
self.global(
"
const CLEANUPS = new WeakRefGroup(x => x.holdings());
const CLEANUPS_MAP = new Map();
"
);
}
fn gc(&mut self) -> Result<(), Error> {
let module = mem::replace(self.module, Module::default());
let module = module.parse_names().unwrap_or_else(|p| p.1);

View File

@ -10,6 +10,7 @@ extern crate failure;
use std::any::Any;
use std::collections::BTreeSet;
use std::env;
use std::fmt;
use std::fs;
use std::mem;
@ -33,6 +34,9 @@ pub struct Bindgen {
typescript: bool,
demangle: bool,
keep_debug: bool,
// Experimental support for `WeakRefGroup`, an upcoming ECMAScript feature.
// Currently only enable-able through an env var.
weak_refs: bool,
}
enum Input {
@ -55,6 +59,7 @@ impl Bindgen {
typescript: false,
demangle: true,
keep_debug: false,
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
}
}