mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-05-05 18:42:16 +00:00
Merge pull request #1002 from alexcrichton/reference-types
Add experimental support for the `anyref` type
This commit is contained in:
commit
de85d99acd
@ -65,6 +65,8 @@ matrix:
|
|||||||
- cargo test
|
- cargo test
|
||||||
# Run the main body of the test suite
|
# Run the main body of the test suite
|
||||||
- cargo test --target wasm32-unknown-unknown
|
- cargo test --target wasm32-unknown-unknown
|
||||||
|
# Make sure the anyref pass at least compiles even if it doesn't run
|
||||||
|
- NODE_ARGS=/dev/null WASM_BINDGEN_ANYREF=1 cargo test --target wasm32-unknown-unknown --test wasm
|
||||||
# Rerun the test suite but disable `--debug` in generated JS
|
# Rerun the test suite but disable `--debug` in generated JS
|
||||||
- WASM_BINDGEN_NO_DEBUG=1 cargo test --target wasm32-unknown-unknown
|
- WASM_BINDGEN_NO_DEBUG=1 cargo test --target wasm32-unknown-unknown
|
||||||
# Make sure our serde tests work
|
# Make sure our serde tests work
|
||||||
|
16
crates/anyref-xform/Cargo.toml
Normal file
16
crates/anyref-xform/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasm-bindgen-anyref-xform"
|
||||||
|
version = "0.2.37"
|
||||||
|
authors = ["The wasm-bindgen Developers"]
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/anyref-xform"
|
||||||
|
homepage = "https://rustwasm.github.io/wasm-bindgen/"
|
||||||
|
documentation = "https://docs.rs/wasm-bindgen-anyref-xform"
|
||||||
|
description = """
|
||||||
|
Internal anyref transformations for wasm-bindgen
|
||||||
|
"""
|
||||||
|
edition = '2018'
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
failure = "0.1"
|
||||||
|
walrus = "0.4"
|
730
crates/anyref-xform/src/lib.rs
Normal file
730
crates/anyref-xform/src/lib.rs
Normal file
@ -0,0 +1,730 @@
|
|||||||
|
//! Transformation for wasm-bindgen to enable usage of `anyref` in a wasm
|
||||||
|
//! module.
|
||||||
|
//!
|
||||||
|
//! This crate is in charge of enabling code using `wasm-bindgen` to use the
|
||||||
|
//! `anyref` type inside of the wasm module. This transformation pass primarily
|
||||||
|
//! wraps exports and imports in shims which use `anyref`, but quickly turn them
|
||||||
|
//! into `i32` value types. This is all largely a stopgap until Rust has
|
||||||
|
//! first-class support for the `anyref` type, but that's thought to be in the
|
||||||
|
//! far future and will take quite some time to implement. In the meantime, we
|
||||||
|
//! have this!
|
||||||
|
//!
|
||||||
|
//! The pass here works by collecting information during binding generation
|
||||||
|
//! about imports and exports. Afterwards this pass runs in one go against a
|
||||||
|
//! wasm module, updating exports, imports, calls to these functions, etc. The
|
||||||
|
//! goal at least is to have valid wasm modules coming in that don't use
|
||||||
|
//! `anyref` and valid wasm modules going out which use `anyref` at the fringes.
|
||||||
|
|
||||||
|
use failure::{bail, format_err, Error};
|
||||||
|
use std::cmp;
|
||||||
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
use std::mem;
|
||||||
|
use walrus::ir::*;
|
||||||
|
use walrus::{FunctionId, GlobalId, InitExpr, Module, TableId, ValType};
|
||||||
|
|
||||||
|
// must be kept in sync with src/lib.rs and ANYREF_HEAP_START
|
||||||
|
const DEFAULT_MIN: u32 = 32;
|
||||||
|
|
||||||
|
/// State of the anyref pass, used to collect information while bindings are
|
||||||
|
/// generated and used eventually to actually execute the entire pass.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Context {
|
||||||
|
// Functions within the module that we're gonna be wrapping, organized by
|
||||||
|
// type. The `Function` contains information about what arguments/return
|
||||||
|
// values in the function signature should turn into anyref.
|
||||||
|
imports: HashMap<String, HashMap<String, Function>>,
|
||||||
|
exports: HashMap<String, Function>,
|
||||||
|
elements: BTreeMap<u32, (u32, Function)>,
|
||||||
|
|
||||||
|
// When wrapping closures with new shims, this is the index of the next
|
||||||
|
// table entry that we'll be handing out.
|
||||||
|
next_element: u32,
|
||||||
|
|
||||||
|
// The anyref table we'll be using, injected after construction
|
||||||
|
table: Option<TableId>,
|
||||||
|
|
||||||
|
// Whether or not the transformation will actually be run at the end
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Transform<'a> {
|
||||||
|
cx: &'a mut Context,
|
||||||
|
|
||||||
|
// A map of functions to intrinsics that they represent
|
||||||
|
intrinsic_map: HashMap<FunctionId, Intrinsic>,
|
||||||
|
// A map of old import functions to the new internally-defined shims which
|
||||||
|
// call the correct new import functions
|
||||||
|
import_map: HashMap<FunctionId, FunctionId>,
|
||||||
|
// A set of all shims we've created
|
||||||
|
shims: HashSet<FunctionId>,
|
||||||
|
|
||||||
|
// Indices of items that we have injected or found. This state is maintained
|
||||||
|
// during the pass execution.
|
||||||
|
table: TableId,
|
||||||
|
clone_ref: FunctionId,
|
||||||
|
heap_alloc: FunctionId,
|
||||||
|
heap_dealloc: FunctionId,
|
||||||
|
stack_pointer: GlobalId,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Function {
|
||||||
|
name: String,
|
||||||
|
// A map of argument index to whether it's an owned or borrowed anyref
|
||||||
|
// (owned = true)
|
||||||
|
args: HashMap<usize, bool>,
|
||||||
|
ret_anyref: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Intrinsic {
|
||||||
|
TableGrow,
|
||||||
|
TableSetNull,
|
||||||
|
DropRef,
|
||||||
|
CloneRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
/// Executed first very early over a wasm module, used to learn about how
|
||||||
|
/// large the function table is so we know what indexes to hand out when
|
||||||
|
/// we're appending entries.
|
||||||
|
pub fn prepare(&mut self, module: &mut Module) -> Result<(), Error> {
|
||||||
|
if !self.enabled {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out what the maximum index of functions pointers are. We'll
|
||||||
|
// be adding new entries to the function table later (maybe) so
|
||||||
|
// precalculate this ahead of time.
|
||||||
|
let mut tables = module.tables.iter().filter_map(|t| match &t.kind {
|
||||||
|
walrus::TableKind::Function(f) => Some(f),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
if let Some(t) = tables.next() {
|
||||||
|
if tables.next().is_some() {
|
||||||
|
bail!("more than one function table present")
|
||||||
|
}
|
||||||
|
self.next_element = t.elements.len() as u32;
|
||||||
|
}
|
||||||
|
drop(tables);
|
||||||
|
|
||||||
|
// Add in an anyref table to the module, which we'll be using for
|
||||||
|
// our transform below.
|
||||||
|
let kind = walrus::TableKind::Anyref(Default::default());
|
||||||
|
self.table = Some(module.tables.add_local(DEFAULT_MIN, None, kind));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store information about an imported function that needs to be
|
||||||
|
/// transformed. The actual transformation happens later during `run`.
|
||||||
|
pub fn import_xform(
|
||||||
|
&mut self,
|
||||||
|
module: &str,
|
||||||
|
name: &str,
|
||||||
|
anyref: &[(usize, bool)],
|
||||||
|
ret_anyref: bool,
|
||||||
|
) -> &mut Self {
|
||||||
|
if !self.enabled {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
let f = self.function(name, anyref, ret_anyref);
|
||||||
|
self.imports
|
||||||
|
.entry(module.to_string())
|
||||||
|
.or_insert_with(Default::default)
|
||||||
|
.insert(name.to_string(), f);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store information about an exported function that needs to be
|
||||||
|
/// transformed. The actual transformation happens later during `run`.
|
||||||
|
pub fn export_xform(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
anyref: &[(usize, bool)],
|
||||||
|
ret_anyref: bool,
|
||||||
|
) -> &mut Self {
|
||||||
|
if !self.enabled {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
let f = self.function(name, anyref, ret_anyref);
|
||||||
|
self.exports.insert(name.to_string(), f);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store information about a function pointer that needs to be transformed.
|
||||||
|
/// The actual transformation happens later during `run`. Returns an index
|
||||||
|
/// that the new wrapped function pointer will be injected at.
|
||||||
|
pub fn table_element_xform(
|
||||||
|
&mut self,
|
||||||
|
idx: u32,
|
||||||
|
anyref: &[(usize, bool)],
|
||||||
|
ret_anyref: bool,
|
||||||
|
) -> u32 {
|
||||||
|
if !self.enabled {
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
let name = format!("closure{}", idx);
|
||||||
|
let f = self.function(&name, anyref, ret_anyref);
|
||||||
|
let ret = self.next_element;
|
||||||
|
self.next_element += 1;
|
||||||
|
self.elements.insert(ret, (idx, f));
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn function(&self, name: &str, anyref: &[(usize, bool)], ret_anyref: bool) -> Function {
|
||||||
|
Function {
|
||||||
|
name: name.to_string(),
|
||||||
|
args: anyref.iter().cloned().collect(),
|
||||||
|
ret_anyref,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn anyref_table_id(&self) -> TableId {
|
||||||
|
self.table.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self, module: &mut Module) -> Result<(), Error> {
|
||||||
|
if !self.enabled {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let table = self.table.unwrap();
|
||||||
|
|
||||||
|
// Inject a stack pointer global which will be used for managing the
|
||||||
|
// stack on the anyref table.
|
||||||
|
let init = InitExpr::Value(Value::I32(DEFAULT_MIN as i32));
|
||||||
|
let stack_pointer = module.globals.add_local(ValType::I32, true, init);
|
||||||
|
|
||||||
|
let mut heap_alloc = None;
|
||||||
|
let mut heap_dealloc = None;
|
||||||
|
|
||||||
|
// Find exports of some intrinsics which we only need for a runtime
|
||||||
|
// implementation.
|
||||||
|
for export in module.exports.iter() {
|
||||||
|
let f = match export.item {
|
||||||
|
walrus::ExportItem::Function(f) => f,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
match export.name.as_str() {
|
||||||
|
"__wbindgen_anyref_table_alloc" => heap_alloc = Some(f),
|
||||||
|
"__wbindgen_anyref_table_dealloc" => heap_dealloc = Some(f),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let heap_alloc = heap_alloc.ok_or_else(|| format_err!("failed to find heap alloc"))?;
|
||||||
|
let heap_dealloc =
|
||||||
|
heap_dealloc.ok_or_else(|| format_err!("failed to find heap dealloc"))?;
|
||||||
|
|
||||||
|
// Create a shim function that looks like:
|
||||||
|
//
|
||||||
|
// (func __wbindgen_object_clone_ref (param i32) (result i32)
|
||||||
|
// (local i32)
|
||||||
|
// (table.set
|
||||||
|
// (tee_local 1 (call $heap_alloc))
|
||||||
|
// (table.get (local.get 0)))
|
||||||
|
// (local.get 1))
|
||||||
|
let mut builder = walrus::FunctionBuilder::new();
|
||||||
|
let arg = module.locals.add(ValType::I32);
|
||||||
|
let local = module.locals.add(ValType::I32);
|
||||||
|
|
||||||
|
let alloc = builder.call(heap_alloc, Box::new([]));
|
||||||
|
let tee = builder.local_tee(local, alloc);
|
||||||
|
let get_arg = builder.local_get(arg);
|
||||||
|
let get_table = builder.table_get(table, get_arg);
|
||||||
|
let set_table = builder.table_set(table, tee, get_table);
|
||||||
|
let get_local = builder.local_get(local);
|
||||||
|
|
||||||
|
let ty = module.types.add(&[ValType::I32], &[ValType::I32]);
|
||||||
|
let clone_ref = builder.finish(ty, vec![arg], vec![set_table, get_local], module);
|
||||||
|
let name = "__wbindgen_object_clone_ref".to_string();
|
||||||
|
module.funcs.get_mut(clone_ref).name = Some(name);
|
||||||
|
|
||||||
|
// And run the transformation!
|
||||||
|
Transform {
|
||||||
|
cx: self,
|
||||||
|
intrinsic_map: HashMap::new(),
|
||||||
|
import_map: HashMap::new(),
|
||||||
|
shims: HashSet::new(),
|
||||||
|
table,
|
||||||
|
clone_ref,
|
||||||
|
heap_alloc,
|
||||||
|
heap_dealloc,
|
||||||
|
stack_pointer,
|
||||||
|
}
|
||||||
|
.run(module)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transform<'_> {
|
||||||
|
fn run(&mut self, module: &mut Module) -> Result<(), Error> {
|
||||||
|
// Detect all the various intrinsics and such. This will also along the
|
||||||
|
// way inject an intrinsic for cloning an anyref.
|
||||||
|
self.find_intrinsics(module)?;
|
||||||
|
|
||||||
|
// Perform transformations of imports, exports, and function pointers.
|
||||||
|
self.process_imports(module);
|
||||||
|
for m in self.cx.imports.values() {
|
||||||
|
assert!(m.is_empty());
|
||||||
|
}
|
||||||
|
self.process_exports(module);
|
||||||
|
assert!(self.cx.exports.is_empty());
|
||||||
|
self.process_elements(module)?;
|
||||||
|
assert!(self.cx.elements.is_empty());
|
||||||
|
|
||||||
|
// If we didn't actually transform anything, no need to inject or
|
||||||
|
// rewrite anything from below.
|
||||||
|
if self.shims.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform all instruction transformations to rewrite calls between
|
||||||
|
// functions and make sure everything is still hooked up right.
|
||||||
|
self.rewrite_calls(module);
|
||||||
|
|
||||||
|
// Inject initialization routine to set up default slots in the table
|
||||||
|
// (things like null/undefined/true/false)
|
||||||
|
self.inject_initialization(module);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_intrinsics(&mut self, module: &mut Module) -> Result<(), Error> {
|
||||||
|
// Build up a map of various imported intrinsics to wire them up to
|
||||||
|
// different implementations or different functions.
|
||||||
|
for import in module.imports.iter_mut() {
|
||||||
|
let f = match import.kind {
|
||||||
|
walrus::ImportKind::Function(f) => f,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
if import.module == "__wbindgen_anyref_xform__" {
|
||||||
|
match import.name.as_str() {
|
||||||
|
"__wbindgen_anyref_table_grow" => {
|
||||||
|
self.intrinsic_map.insert(f, Intrinsic::TableGrow);
|
||||||
|
}
|
||||||
|
"__wbindgen_anyref_table_set_null" => {
|
||||||
|
self.intrinsic_map.insert(f, Intrinsic::TableSetNull);
|
||||||
|
}
|
||||||
|
n => bail!("unknown intrinsic: {}", n),
|
||||||
|
}
|
||||||
|
} else if import.module == "__wbindgen_placeholder__" {
|
||||||
|
match import.name.as_str() {
|
||||||
|
"__wbindgen_object_drop_ref" => {
|
||||||
|
self.intrinsic_map.insert(f, Intrinsic::DropRef);
|
||||||
|
}
|
||||||
|
"__wbindgen_object_clone_ref" => {
|
||||||
|
self.intrinsic_map.insert(f, Intrinsic::CloneRef);
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we don't actually end up using the original import
|
||||||
|
// because any invocation of them should be remapped to something
|
||||||
|
// else.
|
||||||
|
import.name = format!("{}_unused", import.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_imports(&mut self, module: &mut Module) {
|
||||||
|
for import in module.imports.iter_mut() {
|
||||||
|
let f = match import.kind {
|
||||||
|
walrus::ImportKind::Function(f) => f,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
let import = {
|
||||||
|
let entries = match self.cx.imports.get_mut(&import.module) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
match entries.remove(&import.name) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => continue,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let shim = self.append_shim(
|
||||||
|
f,
|
||||||
|
import,
|
||||||
|
&mut module.types,
|
||||||
|
&mut module.funcs,
|
||||||
|
&mut module.locals,
|
||||||
|
);
|
||||||
|
self.import_map.insert(f, shim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_exports(&mut self, module: &mut Module) {
|
||||||
|
let mut new_exports = Vec::new();
|
||||||
|
for export in module.exports.iter() {
|
||||||
|
let f = match export.item {
|
||||||
|
walrus::ExportItem::Function(f) => f,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
let function = match self.cx.exports.remove(&export.name) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let shim = self.append_shim(
|
||||||
|
f,
|
||||||
|
function,
|
||||||
|
&mut module.types,
|
||||||
|
&mut module.funcs,
|
||||||
|
&mut module.locals,
|
||||||
|
);
|
||||||
|
new_exports.push((export.name.to_string(), shim, export.id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (name, shim, old_id) in new_exports {
|
||||||
|
module.exports.add(&name, shim);
|
||||||
|
module.exports.delete(old_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_elements(&mut self, module: &mut Module) -> Result<(), Error> {
|
||||||
|
let table = match module.tables.main_function_table()? {
|
||||||
|
Some(t) => t,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
let table = module.tables.get_mut(table);
|
||||||
|
let kind = match &mut table.kind {
|
||||||
|
walrus::TableKind::Function(f) => f,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
if kind.relative_elements.len() > 0 {
|
||||||
|
bail!("not compatible with relative element initializers yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create shims for all our functions and append them all to the segment
|
||||||
|
// which places elements at the end.
|
||||||
|
while let Some((idx, function)) = self.cx.elements.remove(&(kind.elements.len() as u32)) {
|
||||||
|
let target = kind.elements[idx as usize].unwrap();
|
||||||
|
let shim = self.append_shim(
|
||||||
|
target,
|
||||||
|
function,
|
||||||
|
&mut module.types,
|
||||||
|
&mut module.funcs,
|
||||||
|
&mut module.locals,
|
||||||
|
);
|
||||||
|
kind.elements.push(Some(shim));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and next update the limits of the table in case any are listed.
|
||||||
|
table.initial = cmp::max(table.initial, kind.elements.len() as u32);
|
||||||
|
if let Some(max) = table.maximum {
|
||||||
|
table.maximum = Some(cmp::max(max, kind.elements.len() as u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_shim(
|
||||||
|
&mut self,
|
||||||
|
shim_target: FunctionId,
|
||||||
|
mut func: Function,
|
||||||
|
types: &mut walrus::ModuleTypes,
|
||||||
|
funcs: &mut walrus::ModuleFunctions,
|
||||||
|
locals: &mut walrus::ModuleLocals,
|
||||||
|
) -> FunctionId {
|
||||||
|
let target = funcs.get_mut(shim_target);
|
||||||
|
let (is_export, ty) = match &mut target.kind {
|
||||||
|
walrus::FunctionKind::Import(f) => (false, &mut f.ty),
|
||||||
|
walrus::FunctionKind::Local(f) => (true, &mut f.ty),
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let target_ty = types.get(*ty);
|
||||||
|
|
||||||
|
// Learn about the various operations we're doing up front. Afterwards
|
||||||
|
// we'll have a better idea bout what sort of code we're gonna be
|
||||||
|
// generating.
|
||||||
|
enum Convert {
|
||||||
|
None,
|
||||||
|
Store { owned: bool },
|
||||||
|
Load { owned: bool },
|
||||||
|
}
|
||||||
|
let mut param_tys = Vec::new();
|
||||||
|
let mut param_convert = Vec::new();
|
||||||
|
let mut anyref_stack = 0;
|
||||||
|
|
||||||
|
for (i, old_ty) in target_ty.params().iter().enumerate() {
|
||||||
|
let is_owned = func.args.remove(&i);
|
||||||
|
let new_ty = is_owned
|
||||||
|
.map(|_which| ValType::Anyref)
|
||||||
|
.unwrap_or(old_ty.clone());
|
||||||
|
param_tys.push(new_ty.clone());
|
||||||
|
if new_ty == *old_ty {
|
||||||
|
param_convert.push(Convert::None);
|
||||||
|
} else if is_export {
|
||||||
|
// We're calling an export, so we need to push this anyref into
|
||||||
|
// a table somehow.
|
||||||
|
param_convert.push(Convert::Store {
|
||||||
|
owned: is_owned.unwrap(),
|
||||||
|
});
|
||||||
|
if is_owned == Some(false) {
|
||||||
|
anyref_stack += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We're calling an import, so we just need to fetch our table
|
||||||
|
// value.
|
||||||
|
param_convert.push(Convert::Load {
|
||||||
|
owned: is_owned.unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_ret = if func.ret_anyref {
|
||||||
|
assert_eq!(target_ty.results(), &[ValType::I32]);
|
||||||
|
vec![ValType::Anyref]
|
||||||
|
} else {
|
||||||
|
target_ty.results().to_vec()
|
||||||
|
};
|
||||||
|
let anyref_ty = types.add(¶m_tys, &new_ret);
|
||||||
|
|
||||||
|
// If we're an export then our shim is what's actually going to get
|
||||||
|
// exported, and it's going to have the anyref signature.
|
||||||
|
//
|
||||||
|
// If we're an import, then our shim is what the Rust code calls, which
|
||||||
|
// means it'll have the original signature. The existing import's
|
||||||
|
// signature, however, is transformed to be an anyref signature.
|
||||||
|
let shim_ty = if is_export {
|
||||||
|
anyref_ty
|
||||||
|
} else {
|
||||||
|
mem::replace(ty, anyref_ty)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut builder = walrus::FunctionBuilder::new();
|
||||||
|
let mut before = Vec::new();
|
||||||
|
let params = types.get(shim_ty)
|
||||||
|
.params()
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|ty| locals.add(ty))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Unconditionally allocate some locals which get cleaned up in later
|
||||||
|
// gc passes if we don't actually end up using them.
|
||||||
|
let fp = locals.add(ValType::I32);
|
||||||
|
let scratch_i32 = locals.add(ValType::I32);
|
||||||
|
let scratch_anyref = locals.add(ValType::Anyref);
|
||||||
|
|
||||||
|
// Update our stack pointer if there's any borrowed anyref objects.
|
||||||
|
if anyref_stack > 0 {
|
||||||
|
let sp = builder.global_get(self.stack_pointer);
|
||||||
|
let size = builder.const_(Value::I32(anyref_stack));
|
||||||
|
let new_sp = builder.binop(BinaryOp::I32Sub, sp, size);
|
||||||
|
let tee = builder.local_tee(fp, new_sp);
|
||||||
|
before.push(builder.global_set(self.stack_pointer, tee));
|
||||||
|
}
|
||||||
|
let mut next_stack_offset = 0;
|
||||||
|
|
||||||
|
let mut args = Vec::new();
|
||||||
|
for (i, convert) in param_convert.iter().enumerate() {
|
||||||
|
let local = builder.local_get(params[i]);
|
||||||
|
args.push(match *convert {
|
||||||
|
Convert::None => local,
|
||||||
|
Convert::Load { owned: true } => {
|
||||||
|
// load the anyref onto the stack, then afterwards
|
||||||
|
// deallocate our index, leaving the anyref on the stack.
|
||||||
|
let get = builder.table_get(self.table, local);
|
||||||
|
let free = builder.call(self.heap_dealloc, Box::new([local]));
|
||||||
|
builder.with_side_effects(Vec::new(), get, vec![free])
|
||||||
|
}
|
||||||
|
Convert::Load { owned: false } => builder.table_get(self.table, local),
|
||||||
|
Convert::Store { owned: true } => {
|
||||||
|
// Allocate space for the anyref, store it, and then leave
|
||||||
|
// the index of the allocated anyref on the stack.
|
||||||
|
let alloc = builder.call(self.heap_alloc, Box::new([]));
|
||||||
|
let tee = builder.local_tee(scratch_i32, alloc);
|
||||||
|
let store = builder.table_set(self.table, tee, local);
|
||||||
|
let get = builder.local_get(scratch_i32);
|
||||||
|
builder.with_side_effects(vec![store], get, Vec::new())
|
||||||
|
}
|
||||||
|
Convert::Store { owned: false } => {
|
||||||
|
// Store an anyref at an offset from our function's stack
|
||||||
|
// pointer frame.
|
||||||
|
let get_fp = builder.local_get(fp);
|
||||||
|
next_stack_offset += 1;
|
||||||
|
let (index, idx_local) = if next_stack_offset == 1 {
|
||||||
|
(get_fp, fp)
|
||||||
|
} else {
|
||||||
|
let rhs = builder.i32_const(next_stack_offset);
|
||||||
|
let add = builder.binop(BinaryOp::I32Add, get_fp, rhs);
|
||||||
|
(builder.local_tee(scratch_i32, add), scratch_i32)
|
||||||
|
};
|
||||||
|
let store = builder.table_set(self.table, index, local);
|
||||||
|
let get = builder.local_get(idx_local);
|
||||||
|
builder.with_side_effects(vec![store], get, Vec::new())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we've converted all the arguments, call the original
|
||||||
|
// function. This may be either an import or an export which we're
|
||||||
|
// wrapping.
|
||||||
|
let mut result = builder.call(shim_target, args.into_boxed_slice());
|
||||||
|
let mut after = Vec::new();
|
||||||
|
|
||||||
|
// If an anyref value is returned, then we need to be sure to apply
|
||||||
|
// special treatment to convert it to an i32 as well. Note that only
|
||||||
|
// owned anyref values can be returned, so that's all that's handled
|
||||||
|
// here.
|
||||||
|
if func.ret_anyref {
|
||||||
|
if is_export {
|
||||||
|
// We're an export so we have an i32 on the stack and need to
|
||||||
|
// convert it to an anyref, basically by doing the same as an
|
||||||
|
// owned load above: get the value then deallocate our slot.
|
||||||
|
let tee = builder.local_tee(scratch_i32, result);
|
||||||
|
result = builder.table_get(self.table, tee);
|
||||||
|
let get_local = builder.local_get(scratch_i32);
|
||||||
|
after.push(builder.call(self.heap_dealloc, Box::new([get_local])));
|
||||||
|
} else {
|
||||||
|
// Imports are the opposite, we have any anyref on the stack
|
||||||
|
// and convert it to an i32 by allocating space for it and
|
||||||
|
// storing it there.
|
||||||
|
before.push(builder.local_set(scratch_anyref, result));
|
||||||
|
let alloc = builder.call(self.heap_alloc, Box::new([]));
|
||||||
|
let tee = builder.local_tee(scratch_i32, alloc);
|
||||||
|
let get = builder.local_get(scratch_anyref);
|
||||||
|
before.push(builder.table_set(self.table, tee, get));
|
||||||
|
result = builder.local_get(scratch_i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On function exit restore our anyref stack pointer if we decremented
|
||||||
|
// it to start off.
|
||||||
|
//
|
||||||
|
// Note that we pave over all our stack slots with `ref.null` to ensure
|
||||||
|
// that the table doesn't accidentally hold a strong reference to items
|
||||||
|
// no longer in use by our wasm instance.
|
||||||
|
//
|
||||||
|
// TODO: use `table.fill` once that's spec'd
|
||||||
|
if anyref_stack > 0 {
|
||||||
|
for i in 0..anyref_stack {
|
||||||
|
let get_fp = builder.local_get(fp);
|
||||||
|
let index = if i > 0 {
|
||||||
|
let offset = builder.i32_const(i);
|
||||||
|
builder.binop(BinaryOp::I32Add, get_fp, offset)
|
||||||
|
} else {
|
||||||
|
get_fp
|
||||||
|
};
|
||||||
|
let null = builder.ref_null();
|
||||||
|
after.push(builder.table_set(self.table, index, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
let get_fp = builder.local_get(fp);
|
||||||
|
let size = builder.i32_const(anyref_stack);
|
||||||
|
let new_sp = builder.binop(BinaryOp::I32Add, get_fp, size);
|
||||||
|
after.push(builder.global_set(self.stack_pointer, new_sp));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the final expression node and then finish the function builder
|
||||||
|
// with a fresh type we've been calculating so far. Give the function a
|
||||||
|
// nice name for debugging and then we're good to go!
|
||||||
|
let expr = builder.with_side_effects(before, result, after);
|
||||||
|
let id = builder.finish_parts(shim_ty, params, vec![expr], types, funcs);
|
||||||
|
let name = format!("{}_anyref_shim", func.name);
|
||||||
|
funcs.get_mut(id).name = Some(name);
|
||||||
|
self.shims.insert(id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rewrite_calls(&mut self, module: &mut Module) {
|
||||||
|
for (id, func) in module.funcs.iter_local_mut() {
|
||||||
|
if self.shims.contains(&id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut entry = func.entry_block();
|
||||||
|
Rewrite {
|
||||||
|
func,
|
||||||
|
xform: self,
|
||||||
|
replace: None,
|
||||||
|
}
|
||||||
|
.visit_block_id_mut(&mut entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Rewrite<'a, 'b> {
|
||||||
|
func: &'a mut walrus::LocalFunction,
|
||||||
|
xform: &'a Transform<'b>,
|
||||||
|
replace: Option<ExprId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitorMut for Rewrite<'_, '_> {
|
||||||
|
fn local_function_mut(&mut self) -> &mut walrus::LocalFunction {
|
||||||
|
self.func
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr_id_mut(&mut self, expr: &mut ExprId) {
|
||||||
|
expr.visit_mut(self);
|
||||||
|
if let Some(id) = self.replace.take() {
|
||||||
|
*expr = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_call_mut(&mut self, e: &mut Call) {
|
||||||
|
e.visit_mut(self);
|
||||||
|
let intrinsic = match self.xform.intrinsic_map.get(&e.func) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => {
|
||||||
|
// If this wasn't a call of an intrinsic, but it was a
|
||||||
|
// call of one of our old import functions then we
|
||||||
|
// switch the functions we're calling here.
|
||||||
|
if let Some(f) = self.xform.import_map.get(&e.func) {
|
||||||
|
e.func = *f;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let builder = self.func.builder_mut();
|
||||||
|
|
||||||
|
match intrinsic {
|
||||||
|
Intrinsic::TableGrow => {
|
||||||
|
assert_eq!(e.args.len(), 1);
|
||||||
|
let delta = e.args[0];
|
||||||
|
let null = builder.ref_null();
|
||||||
|
let grow = builder.table_grow(self.xform.table, delta, null);
|
||||||
|
self.replace = Some(grow);
|
||||||
|
}
|
||||||
|
Intrinsic::TableSetNull => {
|
||||||
|
assert_eq!(e.args.len(), 1);
|
||||||
|
let index = e.args[0];
|
||||||
|
let null = builder.ref_null();
|
||||||
|
let set = builder.table_set(self.xform.table, index, null);
|
||||||
|
self.replace = Some(set);
|
||||||
|
}
|
||||||
|
Intrinsic::DropRef => e.func = self.xform.heap_dealloc,
|
||||||
|
Intrinsic::CloneRef => e.func = self.xform.clone_ref,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the `start` function for this module calls the
|
||||||
|
// `__wbindgen_init_anyref_table` function. This'll ensure that all
|
||||||
|
// instances of this module have the initial slots of the anyref table
|
||||||
|
// initialized correctly.
|
||||||
|
fn inject_initialization(&mut self, module: &mut Module) {
|
||||||
|
let ty = module.types.add(&[], &[]);
|
||||||
|
let import = module.add_import_func(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_init_anyref_table",
|
||||||
|
ty,
|
||||||
|
);
|
||||||
|
|
||||||
|
let prev_start = match module.start {
|
||||||
|
Some(f) => f,
|
||||||
|
None => {
|
||||||
|
module.start = Some(import);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut builder = walrus::FunctionBuilder::new();
|
||||||
|
let call_init = builder.call(import, Box::new([]));
|
||||||
|
let call_prev = builder.call(prev_start, Box::new([]));
|
||||||
|
let new_start = builder.finish(ty, Vec::new(), vec![call_init, call_prev], module);
|
||||||
|
module.start = Some(new_start);
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,9 @@ use std::env;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::atomic::Ordering::SeqCst;
|
use std::sync::atomic::Ordering::SeqCst;
|
||||||
use std::sync::atomic::{AtomicBool};
|
|
||||||
use std::sync::atomic::{AtomicUsize};
|
|
||||||
|
|
||||||
use ast;
|
use ast;
|
||||||
use proc_macro2::{self, Ident};
|
use proc_macro2::{self, Ident};
|
||||||
|
@ -18,6 +18,7 @@ log = "0.4"
|
|||||||
rustc-demangle = "0.1.13"
|
rustc-demangle = "0.1.13"
|
||||||
tempfile = "3.0"
|
tempfile = "3.0"
|
||||||
walrus = "0.4.0"
|
walrus = "0.4.0"
|
||||||
|
wasm-bindgen-anyref-xform = { path = '../anyref-xform', version = '=0.2.37' }
|
||||||
wasm-bindgen-shared = { path = "../shared", version = '=0.2.37' }
|
wasm-bindgen-shared = { path = "../shared", version = '=0.2.37' }
|
||||||
wasm-bindgen-threads-xform = { path = '../threads-xform', version = '=0.2.37' }
|
wasm-bindgen-threads-xform = { path = '../threads-xform', version = '=0.2.37' }
|
||||||
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.37' }
|
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.37' }
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
//! this works can be found in the code below.
|
//! this works can be found in the code below.
|
||||||
|
|
||||||
use crate::descriptor::Descriptor;
|
use crate::descriptor::Descriptor;
|
||||||
use crate::js::js2rust::Js2Rust;
|
use crate::js::js2rust::{ExportedShim, Js2Rust};
|
||||||
use crate::js::Context;
|
use crate::js::Context;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
@ -142,7 +142,7 @@ impl ClosureDescriptors {
|
|||||||
let table = input.module.tables.get_mut(table_id);
|
let table = input.module.tables.get_mut(table_id);
|
||||||
let table = match &mut table.kind {
|
let table = match &mut table.kind {
|
||||||
walrus::TableKind::Function(f) => f,
|
walrus::TableKind::Function(f) => f,
|
||||||
walrus::TableKind::Anyref(_) => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
for idx in self.element_removal_list.iter().cloned() {
|
for idx in self.element_removal_list.iter().cloned() {
|
||||||
log::trace!("delete element {}", idx);
|
log::trace!("delete element {}", idx);
|
||||||
@ -178,6 +178,7 @@ impl ClosureDescriptors {
|
|||||||
|
|
||||||
let closure = instr.descriptor.closure().unwrap();
|
let closure = instr.descriptor.closure().unwrap();
|
||||||
|
|
||||||
|
let mut shim = closure.shim_idx;
|
||||||
let (js, _ts, _js_doc) = {
|
let (js, _ts, _js_doc) = {
|
||||||
let mut builder = Js2Rust::new("", input);
|
let mut builder = Js2Rust::new("", input);
|
||||||
builder.prelude("this.cnt++;");
|
builder.prelude("this.cnt++;");
|
||||||
@ -192,9 +193,12 @@ impl ClosureDescriptors {
|
|||||||
builder.rust_argument("this.a").rust_argument("b");
|
builder.rust_argument("this.a").rust_argument("b");
|
||||||
}
|
}
|
||||||
builder.finally("if (this.cnt-- == 1) d(this.a, b);");
|
builder.finally("if (this.cnt-- == 1) d(this.a, b);");
|
||||||
builder.process(&closure.function)?.finish("function", "f")
|
builder.process(&closure.function)?.finish(
|
||||||
|
"function",
|
||||||
|
"f",
|
||||||
|
ExportedShim::TableElement(&mut shim),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
input.expose_add_heap_object();
|
|
||||||
input.function_table_needed = true;
|
input.function_table_needed = true;
|
||||||
let body = format!(
|
let body = format!(
|
||||||
"function(a, b, _ignored) {{
|
"function(a, b, _ignored) {{
|
||||||
@ -205,15 +209,19 @@ impl ClosureDescriptors {
|
|||||||
cb.cnt = 1;
|
cb.cnt = 1;
|
||||||
let real = cb.bind(cb);
|
let real = cb.bind(cb);
|
||||||
real.original = cb;
|
real.original = cb;
|
||||||
return addHeapObject(real);
|
return {};
|
||||||
}}",
|
}}",
|
||||||
closure.shim_idx, closure.dtor_idx, js,
|
shim,
|
||||||
|
closure.dtor_idx,
|
||||||
|
js,
|
||||||
|
input.add_heap_object("real"),
|
||||||
);
|
);
|
||||||
input.export(&import_name, &body, None);
|
input.export(&import_name, &body, None);
|
||||||
|
|
||||||
let id = input
|
let module = "__wbindgen_placeholder__";
|
||||||
.module
|
let id = input.module.add_import_func(module, &import_name, ty);
|
||||||
.add_import_func("__wbindgen_placeholder__", &import_name, ty);
|
input.anyref.import_xform(module, &import_name, &[], true);
|
||||||
|
input.module.funcs.get_mut(id).name = Some(import_name);
|
||||||
|
|
||||||
let local = match &mut input.module.funcs.get_mut(*func).kind {
|
let local = match &mut input.module.funcs.get_mut(*func).kind {
|
||||||
walrus::FunctionKind::Local(l) => l,
|
walrus::FunctionKind::Local(l) => l,
|
||||||
|
@ -44,6 +44,15 @@ pub struct Js2Rust<'a, 'b: 'a> {
|
|||||||
/// The string value here is the class that this should be a constructor
|
/// The string value here is the class that this should be a constructor
|
||||||
/// for.
|
/// for.
|
||||||
constructor: Option<String>,
|
constructor: Option<String>,
|
||||||
|
|
||||||
|
/// metadata for anyref transformations
|
||||||
|
anyref_args: Vec<(usize, bool)>,
|
||||||
|
ret_anyref: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ExportedShim<'a> {
|
||||||
|
Named(&'a str),
|
||||||
|
TableElement(&'a mut u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Js2Rust<'a, 'b> {
|
impl<'a, 'b> Js2Rust<'a, 'b> {
|
||||||
@ -59,6 +68,8 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
ret_ty: String::new(),
|
ret_ty: String::new(),
|
||||||
ret_expr: String::new(),
|
ret_expr: String::new(),
|
||||||
constructor: None,
|
constructor: None,
|
||||||
|
anyref_args: Vec::new(),
|
||||||
|
ret_anyref: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,14 +204,26 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
|
|
||||||
if arg.is_anyref() {
|
if arg.is_anyref() {
|
||||||
self.js_arguments.push((name.clone(), "any".to_string()));
|
self.js_arguments.push((name.clone(), "any".to_string()));
|
||||||
|
if self.cx.config.anyref {
|
||||||
|
if optional {
|
||||||
|
self.cx.expose_add_to_anyref_table()?;
|
||||||
|
self.cx.expose_is_like_none();
|
||||||
|
self.rust_arguments
|
||||||
|
.push(format!("isLikeNone({0}) ? 0 : addToAnyrefTable({0})", name));
|
||||||
|
} else {
|
||||||
|
self.anyref_args.push((self.rust_arguments.len(), true));
|
||||||
|
self.rust_arguments.push(name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
self.cx.expose_add_heap_object();
|
self.cx.expose_add_heap_object();
|
||||||
if optional {
|
if optional {
|
||||||
self.cx.expose_is_like_none();
|
self.cx.expose_is_like_none();
|
||||||
self.rust_arguments
|
self.rust_arguments
|
||||||
.push(format!("isLikeNone({0}) ? 0 : addHeapObject({0})", name,));
|
.push(format!("isLikeNone({0}) ? 0 : addHeapObject({0})", name));
|
||||||
} else {
|
} else {
|
||||||
self.rust_arguments.push(format!("addHeapObject({})", name));
|
self.rust_arguments.push(format!("addHeapObject({})", name));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,14 +406,19 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
|
|
||||||
if arg.is_ref_anyref() {
|
if arg.is_ref_anyref() {
|
||||||
self.js_arguments.push((name.clone(), "any".to_string()));
|
self.js_arguments.push((name.clone(), "any".to_string()));
|
||||||
self.cx.expose_borrowed_objects();
|
if self.cx.config.anyref {
|
||||||
self.cx.expose_global_stack_pointer();
|
self.anyref_args.push((self.rust_arguments.len(), false));
|
||||||
|
self.rust_arguments.push(name);
|
||||||
|
} else {
|
||||||
// the "stack-ful" nature means that we're always popping from the
|
// the "stack-ful" nature means that we're always popping from the
|
||||||
// stack, and make sure that we actually clear our reference to
|
// stack, and make sure that we actually clear our reference to
|
||||||
// allow stale values to get GC'd
|
// allow stale values to get GC'd
|
||||||
|
self.cx.expose_borrowed_objects();
|
||||||
|
self.cx.expose_global_stack_pointer();
|
||||||
self.finally("heap[stack_pointer++] = undefined;");
|
self.finally("heap[stack_pointer++] = undefined;");
|
||||||
self.rust_arguments
|
self.rust_arguments
|
||||||
.push(format!("addBorrowedObject({})", name));
|
.push(format!("addBorrowedObject({})", name));
|
||||||
|
}
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,7 +490,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
|
|
||||||
if let Some(ty) = ty.vector_kind() {
|
if let Some(ty) = ty.vector_kind() {
|
||||||
self.ret_ty = ty.js_ty().to_string();
|
self.ret_ty = ty.js_ty().to_string();
|
||||||
let f = self.cx.expose_get_vector_from_wasm(ty);
|
let f = self.cx.expose_get_vector_from_wasm(ty)?;
|
||||||
self.cx.expose_global_argument_ptr()?;
|
self.cx.expose_global_argument_ptr()?;
|
||||||
self.cx.expose_uint32_memory();
|
self.cx.expose_uint32_memory();
|
||||||
self.cx.require_internal_export("__wbindgen_free")?;
|
self.cx.require_internal_export("__wbindgen_free")?;
|
||||||
@ -494,8 +522,8 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
// that `takeObject` will naturally pluck out `undefined`.
|
// that `takeObject` will naturally pluck out `undefined`.
|
||||||
if ty.is_anyref() {
|
if ty.is_anyref() {
|
||||||
self.ret_ty = "any".to_string();
|
self.ret_ty = "any".to_string();
|
||||||
self.cx.expose_take_object();
|
self.ret_expr = format!("return {};", self.cx.take_object("RET"));
|
||||||
self.ret_expr = format!("return takeObject(RET);");
|
self.ret_anyref = true;
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,7 +736,12 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
/// Returns two strings, the first of which is the JS expression for the
|
/// Returns two strings, the first of which is the JS expression for the
|
||||||
/// generated function shim and the second is a TypeScript signature of the
|
/// generated function shim and the second is a TypeScript signature of the
|
||||||
/// JS expression.
|
/// JS expression.
|
||||||
pub fn finish(&self, prefix: &str, invoc: &str) -> (String, String, String) {
|
pub fn finish(
|
||||||
|
&mut self,
|
||||||
|
prefix: &str,
|
||||||
|
invoc: &str,
|
||||||
|
exported_shim: ExportedShim,
|
||||||
|
) -> (String, String, String) {
|
||||||
let js_args = self
|
let js_args = self
|
||||||
.js_arguments
|
.js_arguments
|
||||||
.iter()
|
.iter()
|
||||||
@ -754,6 +787,24 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
ts.push_str(&self.ret_ty);
|
ts.push_str(&self.ret_ty);
|
||||||
}
|
}
|
||||||
ts.push(';');
|
ts.push(';');
|
||||||
|
|
||||||
|
if self.ret_anyref || self.anyref_args.len() > 0 {
|
||||||
|
match exported_shim {
|
||||||
|
ExportedShim::Named(name) => {
|
||||||
|
self.cx
|
||||||
|
.anyref
|
||||||
|
.export_xform(name, &self.anyref_args, self.ret_anyref);
|
||||||
|
}
|
||||||
|
ExportedShim::TableElement(idx) => {
|
||||||
|
*idx = self.cx.anyref.table_element_xform(
|
||||||
|
*idx,
|
||||||
|
&self.anyref_args,
|
||||||
|
self.ret_anyref,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(js, ts, self.js_doc_comments())
|
(js, ts, self.js_doc_comments())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use walrus::{MemoryId, Module};
|
|||||||
use wasm_bindgen_wasm_interpreter::Interpreter;
|
use wasm_bindgen_wasm_interpreter::Interpreter;
|
||||||
|
|
||||||
mod js2rust;
|
mod js2rust;
|
||||||
use self::js2rust::Js2Rust;
|
use self::js2rust::{ExportedShim, Js2Rust};
|
||||||
mod rust2js;
|
mod rust2js;
|
||||||
use self::rust2js::Rust2Js;
|
use self::rust2js::Rust2Js;
|
||||||
mod closures;
|
mod closures;
|
||||||
@ -52,6 +52,8 @@ pub struct Context<'a> {
|
|||||||
pub function_table_needed: bool,
|
pub function_table_needed: bool,
|
||||||
pub interpreter: &'a mut Interpreter,
|
pub interpreter: &'a mut Interpreter,
|
||||||
pub memory: MemoryId,
|
pub memory: MemoryId,
|
||||||
|
|
||||||
|
pub anyref: wasm_bindgen_anyref_xform::Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -163,182 +165,198 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize(&mut self, module_name: &str) -> Result<(String, String), Error> {
|
pub fn finalize(&mut self, module_name: &str) -> Result<(String, String), Error> {
|
||||||
self.bind("__wbindgen_object_clone_ref", &|me| {
|
|
||||||
me.expose_get_object();
|
|
||||||
me.expose_add_heap_object();
|
|
||||||
Ok(String::from(
|
|
||||||
"
|
|
||||||
function(idx) {
|
|
||||||
return addHeapObject(getObject(idx));
|
|
||||||
}
|
|
||||||
",
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.bind("__wbindgen_object_drop_ref", &|me| {
|
|
||||||
me.expose_drop_ref();
|
|
||||||
Ok(String::from("function(i) { dropObject(i); }"))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.bind("__wbindgen_string_new", &|me| {
|
self.bind("__wbindgen_string_new", &|me| {
|
||||||
me.expose_add_heap_object();
|
me.anyref.import_xform(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_string_new",
|
||||||
|
&[],
|
||||||
|
true,
|
||||||
|
);
|
||||||
me.expose_get_string_from_wasm();
|
me.expose_get_string_from_wasm();
|
||||||
Ok(String::from(
|
Ok(format!(
|
||||||
"
|
"function(p, l) {{ return {}; }}",
|
||||||
function(p, l) {
|
me.add_heap_object("getStringFromWasm(p, l)")
|
||||||
return addHeapObject(getStringFromWasm(p, l));
|
|
||||||
}
|
|
||||||
",
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_number_new", &|me| {
|
self.bind("__wbindgen_number_new", &|me| {
|
||||||
me.expose_add_heap_object();
|
me.anyref.import_xform(
|
||||||
Ok(String::from("function(i) { return addHeapObject(i); }"))
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_number_new",
|
||||||
|
&[],
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
Ok(format!(
|
||||||
|
"function(i) {{ return {}; }}",
|
||||||
|
me.add_heap_object("i")
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_number_get", &|me| {
|
self.bind("__wbindgen_number_get", &|me| {
|
||||||
me.expose_get_object();
|
me.anyref.import_xform(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_number_get",
|
||||||
|
&[(0, false)],
|
||||||
|
false,
|
||||||
|
);
|
||||||
me.expose_uint8_memory();
|
me.expose_uint8_memory();
|
||||||
Ok(String::from(
|
Ok(format!(
|
||||||
"
|
"
|
||||||
function(n, invalid) {
|
function(n, invalid) {{
|
||||||
let obj = getObject(n);
|
let obj = {};
|
||||||
if (typeof(obj) === 'number') return obj;
|
if (typeof(obj) === 'number') return obj;
|
||||||
getUint8Memory()[invalid] = 1;
|
getUint8Memory()[invalid] = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}}
|
||||||
",
|
",
|
||||||
|
me.get_object("n"),
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_is_null", &|me| {
|
self.bind("__wbindgen_is_null", &|me| {
|
||||||
me.expose_get_object();
|
me.anyref.import_xform(
|
||||||
Ok(String::from(
|
"__wbindgen_placeholder__",
|
||||||
"
|
"__wbindgen_is_null",
|
||||||
function(idx) {
|
&[(0, false)],
|
||||||
return getObject(idx) === null ? 1 : 0;
|
false,
|
||||||
}
|
);
|
||||||
",
|
Ok(format!(
|
||||||
|
"function(i) {{ return {} === null ? 1 : 0; }}",
|
||||||
|
me.get_object("i")
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_is_undefined", &|me| {
|
self.bind("__wbindgen_is_undefined", &|me| {
|
||||||
me.expose_get_object();
|
me.anyref.import_xform(
|
||||||
Ok(String::from(
|
"__wbindgen_placeholder__",
|
||||||
"
|
"__wbindgen_is_undefined",
|
||||||
function(idx) {
|
&[(0, false)],
|
||||||
return getObject(idx) === undefined ? 1 : 0;
|
false,
|
||||||
}
|
);
|
||||||
",
|
Ok(format!(
|
||||||
|
"function(i) {{ return {} === undefined ? 1 : 0; }}",
|
||||||
|
me.get_object("i")
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_boolean_get", &|me| {
|
self.bind("__wbindgen_boolean_get", &|me| {
|
||||||
me.expose_get_object();
|
me.anyref.import_xform(
|
||||||
Ok(String::from(
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_boolean_get",
|
||||||
|
&[(0, false)],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
Ok(format!(
|
||||||
"
|
"
|
||||||
function(i) {
|
function(i) {{
|
||||||
let v = getObject(i);
|
let v = {};
|
||||||
if (typeof(v) === 'boolean') {
|
return typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
|
||||||
return v ? 1 : 0;
|
}}
|
||||||
} else {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
",
|
",
|
||||||
|
me.get_object("i"),
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_symbol_new", &|me| {
|
self.bind("__wbindgen_symbol_new", &|me| {
|
||||||
|
me.anyref.import_xform(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_symbol_new",
|
||||||
|
&[],
|
||||||
|
true,
|
||||||
|
);
|
||||||
me.expose_get_string_from_wasm();
|
me.expose_get_string_from_wasm();
|
||||||
me.expose_add_heap_object();
|
let expr = "ptr === 0 ? Symbol() : Symbol(getStringFromWasm(ptr, len))";
|
||||||
Ok(String::from(
|
Ok(format!(
|
||||||
"
|
"function(ptr, len) {{ return {}; }}",
|
||||||
function(ptr, len) {
|
me.add_heap_object(expr)
|
||||||
let a;
|
|
||||||
if (ptr === 0) {
|
|
||||||
a = Symbol();
|
|
||||||
} else {
|
|
||||||
a = Symbol(getStringFromWasm(ptr, len));
|
|
||||||
}
|
|
||||||
return addHeapObject(a);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_is_symbol", &|me| {
|
self.bind("__wbindgen_is_symbol", &|me| {
|
||||||
me.expose_get_object();
|
me.anyref.import_xform(
|
||||||
Ok(String::from(
|
"__wbindgen_placeholder__",
|
||||||
"
|
"__wbindgen_is_symbol",
|
||||||
function(i) {
|
&[(0, false)],
|
||||||
return typeof(getObject(i)) === 'symbol' ? 1 : 0;
|
false,
|
||||||
}
|
);
|
||||||
",
|
Ok(format!(
|
||||||
|
"function(i) {{ return typeof({}) === 'symbol' ? 1 : 0; }}",
|
||||||
|
me.get_object("i")
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_is_object", &|me| {
|
self.bind("__wbindgen_is_object", &|me| {
|
||||||
me.expose_get_object();
|
me.anyref.import_xform(
|
||||||
Ok(String::from(
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_is_object",
|
||||||
|
&[(0, false)],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
Ok(format!(
|
||||||
"
|
"
|
||||||
function(i) {
|
function(i) {{
|
||||||
const val = getObject(i);
|
const val = {};
|
||||||
return typeof(val) === 'object' && val !== null ? 1 : 0;
|
return typeof(val) === 'object' && val !== null ? 1 : 0;
|
||||||
}
|
}}",
|
||||||
",
|
me.get_object("i"),
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_is_function", &|me| {
|
self.bind("__wbindgen_is_function", &|me| {
|
||||||
me.expose_get_object();
|
me.anyref.import_xform(
|
||||||
Ok(String::from(
|
"__wbindgen_placeholder__",
|
||||||
"
|
"__wbindgen_is_function",
|
||||||
function(i) {
|
&[(0, false)],
|
||||||
return typeof(getObject(i)) === 'function' ? 1 : 0;
|
false,
|
||||||
}
|
);
|
||||||
",
|
Ok(format!(
|
||||||
|
"function(i) {{ return typeof({}) === 'function' ? 1 : 0; }}",
|
||||||
|
me.get_object("i")
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_is_string", &|me| {
|
self.bind("__wbindgen_is_string", &|me| {
|
||||||
me.expose_get_object();
|
me.anyref.import_xform(
|
||||||
Ok(String::from(
|
"__wbindgen_placeholder__",
|
||||||
"
|
"__wbindgen_is_string",
|
||||||
function(i) {
|
&[(0, false)],
|
||||||
return typeof(getObject(i)) === 'string' ? 1 : 0;
|
false,
|
||||||
}
|
);
|
||||||
",
|
Ok(format!(
|
||||||
|
"function(i) {{ return typeof({}) === 'string' ? 1 : 0; }}",
|
||||||
|
me.get_object("i")
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_string_get", &|me| {
|
self.bind("__wbindgen_string_get", &|me| {
|
||||||
me.expose_pass_string_to_wasm()?;
|
me.expose_pass_string_to_wasm()?;
|
||||||
me.expose_get_object();
|
|
||||||
me.expose_uint32_memory();
|
me.expose_uint32_memory();
|
||||||
Ok(String::from(
|
me.anyref.import_xform(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_string_get",
|
||||||
|
&[(0, false)],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
Ok(format!(
|
||||||
"
|
"
|
||||||
function(i, len_ptr) {
|
function(i, len_ptr) {{
|
||||||
let obj = getObject(i);
|
let obj = {};
|
||||||
if (typeof(obj) !== 'string') return 0;
|
if (typeof(obj) !== 'string') return 0;
|
||||||
const ptr = passStringToWasm(obj);
|
const ptr = passStringToWasm(obj);
|
||||||
getUint32Memory()[len_ptr / 4] = WASM_VECTOR_LEN;
|
getUint32Memory()[len_ptr / 4] = WASM_VECTOR_LEN;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}}
|
||||||
",
|
",
|
||||||
|
me.get_object("i"),
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_debug_string", &|me| {
|
self.bind("__wbindgen_debug_string", &|me| {
|
||||||
me.expose_pass_string_to_wasm()?;
|
me.expose_pass_string_to_wasm()?;
|
||||||
me.expose_get_object();
|
|
||||||
me.expose_uint32_memory();
|
me.expose_uint32_memory();
|
||||||
Ok(String::from(
|
|
||||||
"
|
let debug_str = "
|
||||||
function(i, len_ptr) {
|
val => {
|
||||||
const toString = Object.prototype.toString;
|
|
||||||
const debug_str = val => {
|
|
||||||
// primitive types
|
// primitive types
|
||||||
const type = typeof val;
|
const type = typeof val;
|
||||||
if (type == 'number' || type == 'boolean' || val == null) {
|
if (type == 'number' || type == 'boolean' || val == null) {
|
||||||
@ -401,81 +419,112 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
// TODO we could test for more things here, like `Set`s and `Map`s.
|
// TODO we could test for more things here, like `Set`s and `Map`s.
|
||||||
return className;
|
return className;
|
||||||
};
|
}
|
||||||
const val = getObject(i);
|
";
|
||||||
|
Ok(format!(
|
||||||
|
"
|
||||||
|
function(i, len_ptr) {{
|
||||||
|
const debug_str = {};
|
||||||
|
const toString = Object.prototype.toString;
|
||||||
|
const val = {};
|
||||||
const debug = debug_str(val);
|
const debug = debug_str(val);
|
||||||
const ptr = passStringToWasm(debug);
|
const ptr = passStringToWasm(debug);
|
||||||
getUint32Memory()[len_ptr / 4] = WASM_VECTOR_LEN;
|
getUint32Memory()[len_ptr / 4] = WASM_VECTOR_LEN;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}}
|
||||||
",
|
",
|
||||||
|
debug_str,
|
||||||
|
me.get_object("i"),
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_cb_drop", &|me| {
|
self.bind("__wbindgen_cb_drop", &|me| {
|
||||||
me.expose_drop_ref();
|
me.anyref.import_xform(
|
||||||
Ok(String::from(
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_cb_drop",
|
||||||
|
&[(0, true)],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
Ok(format!(
|
||||||
"
|
"
|
||||||
function(i) {
|
function(i) {{
|
||||||
const obj = getObject(i).original;
|
const obj = {}.original;
|
||||||
dropObject(i);
|
if (obj.cnt-- == 1) {{
|
||||||
if (obj.cnt-- == 1) {
|
|
||||||
obj.a = 0;
|
obj.a = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}}
|
||||||
",
|
",
|
||||||
|
me.take_object("i"),
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_cb_forget", &|me| {
|
self.bind("__wbindgen_cb_forget", &|me| {
|
||||||
|
Ok(if me.config.anyref {
|
||||||
|
// TODO: we should rewrite this in the anyref xform to not even
|
||||||
|
// call into JS
|
||||||
|
me.anyref.import_xform(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_cb_drop",
|
||||||
|
&[(0, true)],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
String::from("function(obj) {}")
|
||||||
|
} else {
|
||||||
me.expose_drop_ref();
|
me.expose_drop_ref();
|
||||||
Ok("dropObject".to_string())
|
"dropObject".to_string()
|
||||||
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_json_parse", &|me| {
|
self.bind("__wbindgen_json_parse", &|me| {
|
||||||
me.expose_add_heap_object();
|
|
||||||
me.expose_get_string_from_wasm();
|
me.expose_get_string_from_wasm();
|
||||||
Ok(String::from(
|
me.anyref.import_xform(
|
||||||
"
|
"__wbindgen_placeholder__",
|
||||||
function(ptr, len) {
|
"__wbindgen_json_parse",
|
||||||
return addHeapObject(JSON.parse(getStringFromWasm(ptr, len)));
|
&[],
|
||||||
}
|
true,
|
||||||
",
|
);
|
||||||
))
|
let expr = "JSON.parse(getStringFromWasm(ptr, len))";
|
||||||
|
let expr = me.add_heap_object(expr);
|
||||||
|
Ok(format!("function(ptr, len) {{ return {}; }}", expr))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_json_serialize", &|me| {
|
self.bind("__wbindgen_json_serialize", &|me| {
|
||||||
me.expose_get_object();
|
me.anyref.import_xform(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
"__wbindgen_json_serialize",
|
||||||
|
&[(0, false)],
|
||||||
|
false,
|
||||||
|
);
|
||||||
me.expose_pass_string_to_wasm()?;
|
me.expose_pass_string_to_wasm()?;
|
||||||
me.expose_uint32_memory();
|
me.expose_uint32_memory();
|
||||||
Ok(String::from(
|
Ok(format!(
|
||||||
"
|
"
|
||||||
function(idx, ptrptr) {
|
function(idx, ptrptr) {{
|
||||||
const ptr = passStringToWasm(JSON.stringify(getObject(idx)));
|
const ptr = passStringToWasm(JSON.stringify({}));
|
||||||
getUint32Memory()[ptrptr / 4] = ptr;
|
getUint32Memory()[ptrptr / 4] = ptr;
|
||||||
return WASM_VECTOR_LEN;
|
return WASM_VECTOR_LEN;
|
||||||
}
|
}}
|
||||||
",
|
",
|
||||||
|
me.get_object("idx"),
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_jsval_eq", &|me| {
|
self.bind("__wbindgen_jsval_eq", &|me| {
|
||||||
me.expose_get_object();
|
Ok(format!(
|
||||||
Ok(String::from(
|
"function(a, b) {{ return {} === {} ? 1 : 0; }}",
|
||||||
"
|
me.get_object("a"),
|
||||||
function(a, b) {
|
me.get_object("b")
|
||||||
return getObject(a) === getObject(b) ? 1 : 0;
|
|
||||||
}
|
|
||||||
",
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_memory", &|me| {
|
self.bind("__wbindgen_memory", &|me| {
|
||||||
me.expose_add_heap_object();
|
|
||||||
let mem = me.memory();
|
let mem = me.memory();
|
||||||
Ok(format!("function() {{ return addHeapObject({}); }}", mem))
|
Ok(format!(
|
||||||
|
"function() {{ return {}; }}",
|
||||||
|
me.add_heap_object(mem)
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_module", &|me| {
|
self.bind("__wbindgen_module", &|me| {
|
||||||
@ -485,23 +534,60 @@ impl<'a> Context<'a> {
|
|||||||
--no-modules"
|
--no-modules"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
me.expose_add_heap_object();
|
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"
|
"function() {{ return {}; }}",
|
||||||
function() {{
|
me.add_heap_object("init.__wbindgen_wasm_module")
|
||||||
return addHeapObject(init.__wbindgen_wasm_module);
|
|
||||||
}}
|
|
||||||
",
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_rethrow", &|me| {
|
self.bind("__wbindgen_rethrow", &|me| {
|
||||||
me.expose_take_object();
|
Ok(format!(
|
||||||
Ok(String::from("function(idx) { throw takeObject(idx); }"))
|
"function(idx) {{ throw {}; }}",
|
||||||
|
me.take_object("idx")
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
closures::rewrite(self).with_context(|_| "failed to generate internal closure shims")?;
|
closures::rewrite(self).with_context(|_| "failed to generate internal closure shims")?;
|
||||||
self.write_classes()?;
|
self.write_classes()?;
|
||||||
|
self.anyref.run(self.module)?;
|
||||||
|
|
||||||
|
// After the anyref pass has executed, if this intrinsic is needed then
|
||||||
|
// we expose a function which initializes it
|
||||||
|
self.bind("__wbindgen_init_anyref_table", &|me| {
|
||||||
|
me.expose_anyref_table();
|
||||||
|
Ok(String::from(
|
||||||
|
"function() {
|
||||||
|
const table = wasm.__wbg_anyref_table;
|
||||||
|
const offset = table.grow(4);
|
||||||
|
table.set(offset + 0, undefined);
|
||||||
|
table.set(offset + 1, null);
|
||||||
|
table.set(offset + 2, true);
|
||||||
|
table.set(offset + 3, false);
|
||||||
|
}",
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// make sure that the anyref pass runs before binding this as anyref may
|
||||||
|
// remove calls to this import and then gc would remove it
|
||||||
|
self.bind("__wbindgen_object_clone_ref", &|me| {
|
||||||
|
me.expose_get_object();
|
||||||
|
me.expose_add_heap_object();
|
||||||
|
Ok(String::from(
|
||||||
|
"
|
||||||
|
function(idx) {
|
||||||
|
return addHeapObject(getObject(idx));
|
||||||
|
}
|
||||||
|
",
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// like above, make sure anyref runs first and the anyref pass may
|
||||||
|
// remove usages of this.
|
||||||
|
self.bind("__wbindgen_object_drop_ref", &|me| {
|
||||||
|
me.expose_drop_ref();
|
||||||
|
Ok(String::from("function(i) { dropObject(i); }"))
|
||||||
|
})?;
|
||||||
|
|
||||||
self.unexport_unused_internal_exports();
|
self.unexport_unused_internal_exports();
|
||||||
|
|
||||||
// Handle the `start` function, if one was specified. If we're in a
|
// Handle the `start` function, if one was specified. If we're in a
|
||||||
@ -529,9 +615,6 @@ impl<'a> Context<'a> {
|
|||||||
if self.config.emit_start {
|
if self.config.emit_start {
|
||||||
self.add_start_function()?;
|
self.add_start_function()?;
|
||||||
has_start_function = self.unstart_start_function();
|
has_start_function = self.unstart_start_function();
|
||||||
if has_start_function && !self.config.no_modules {
|
|
||||||
self.inject_start_shim();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.export_table()?;
|
self.export_table()?;
|
||||||
@ -703,6 +786,14 @@ impl<'a> Context<'a> {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
// In the "we're pretending to be an ES module use case if we've got
|
||||||
|
// a start function then we use an injected shim to actually execute
|
||||||
|
// the real start function on the next tick of the microtask queue
|
||||||
|
// (explained above)
|
||||||
|
if has_start_function {
|
||||||
|
self.inject_start_shim();
|
||||||
|
}
|
||||||
|
|
||||||
let import_wasm = if self.globals.len() == 0 {
|
let import_wasm = if self.globals.len() == 0 {
|
||||||
String::new()
|
String::new()
|
||||||
} else if self.use_node_require() {
|
} else if self.use_node_require() {
|
||||||
@ -804,21 +895,13 @@ impl<'a> Context<'a> {
|
|||||||
let mut wrap_needed = class.wrap_needed;
|
let mut wrap_needed = class.wrap_needed;
|
||||||
let new_name = wasm_bindgen_shared::new_function(&name);
|
let new_name = wasm_bindgen_shared::new_function(&name);
|
||||||
if self.wasm_import_needed(&new_name) {
|
if self.wasm_import_needed(&new_name) {
|
||||||
self.expose_add_heap_object();
|
|
||||||
wrap_needed = true;
|
wrap_needed = true;
|
||||||
|
self.anyref
|
||||||
self.export(
|
.import_xform("__wbindgen_placeholder__", &new_name, &[], true);
|
||||||
&new_name,
|
let expr = format!("{}.__wrap(ptr)", name);
|
||||||
&format!(
|
let expr = self.add_heap_object(&expr);
|
||||||
"
|
let body = format!("function(ptr) {{ return {}; }}", expr);
|
||||||
function(ptr) {{
|
self.export(&new_name, &body, None);
|
||||||
return addHeapObject({}.__wrap(ptr));
|
|
||||||
}}
|
|
||||||
",
|
|
||||||
name
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if wrap_needed {
|
if wrap_needed {
|
||||||
@ -924,7 +1007,10 @@ impl<'a> Context<'a> {
|
|||||||
math_imports.push((renamed_import.clone(), format!("function{}", expr)));
|
math_imports.push((renamed_import.clone(), format!("function{}", expr)));
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME(#32): try to not use function shims
|
// Note that since Rust 1.32.0 this is no longer necessary. Imports
|
||||||
|
// of these functions were fixed in rust-lang/rust#54257 and we're
|
||||||
|
// just waiting until pre-1.32.0 compilers are basically no longer
|
||||||
|
// in use to remove this.
|
||||||
match import.name.as_str() {
|
match import.name.as_str() {
|
||||||
"Math_acos" => bind_math("(x) { return Math.acos(x); }"),
|
"Math_acos" => bind_math("(x) { return Math.acos(x); }"),
|
||||||
"Math_asin" => bind_math("(x) { return Math.asin(x); }"),
|
"Math_asin" => bind_math("(x) { return Math.asin(x); }"),
|
||||||
@ -1007,6 +1093,7 @@ impl<'a> Context<'a> {
|
|||||||
if !self.should_write_global("heap") {
|
if !self.should_write_global("heap") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
assert!(!self.config.anyref);
|
||||||
self.global(&format!("const heap = new Array({});", INITIAL_HEAP_OFFSET));
|
self.global(&format!("const heap = new Array({});", INITIAL_HEAP_OFFSET));
|
||||||
self.global("heap.fill(undefined);");
|
self.global("heap.fill(undefined);");
|
||||||
self.global(&format!("heap.push({});", INITIAL_HEAP_VALUES.join(", ")));
|
self.global(&format!("heap.push({});", INITIAL_HEAP_VALUES.join(", ")));
|
||||||
@ -1133,6 +1220,24 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
self.require_internal_export("__wbindgen_malloc")?;
|
self.require_internal_export("__wbindgen_malloc")?;
|
||||||
self.expose_uint32_memory();
|
self.expose_uint32_memory();
|
||||||
|
if self.config.anyref {
|
||||||
|
// TODO: using `addToAnyrefTable` goes back and forth between wasm
|
||||||
|
// and JS a lot, we should have a bulk operation for this.
|
||||||
|
self.expose_add_to_anyref_table()?;
|
||||||
|
self.global(
|
||||||
|
"
|
||||||
|
function passArrayJsValueToWasm(array) {
|
||||||
|
const ptr = wasm.__wbindgen_malloc(array.length * 4);
|
||||||
|
const mem = getUint32Memory();
|
||||||
|
for (let i = 0; i < array.length; i++) {
|
||||||
|
mem[ptr / 4 + i] = addToAnyrefTable(array[i]);
|
||||||
|
}
|
||||||
|
WASM_VECTOR_LEN = array.length;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
self.expose_add_heap_object();
|
self.expose_add_heap_object();
|
||||||
self.global(
|
self.global(
|
||||||
"
|
"
|
||||||
@ -1148,6 +1253,7 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1242,11 +1348,29 @@ impl<'a> Context<'a> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expose_get_array_js_value_from_wasm(&mut self) {
|
fn expose_get_array_js_value_from_wasm(&mut self) -> Result<(), Error> {
|
||||||
if !self.should_write_global("get_array_js_value_from_wasm") {
|
if !self.should_write_global("get_array_js_value_from_wasm") {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
self.expose_uint32_memory();
|
self.expose_uint32_memory();
|
||||||
|
if self.config.anyref {
|
||||||
|
self.expose_anyref_table();
|
||||||
|
self.global(
|
||||||
|
"
|
||||||
|
function getArrayJsValueFromWasm(ptr, len) {
|
||||||
|
const mem = getUint32Memory();
|
||||||
|
const slice = mem.subarray(ptr / 4, ptr / 4 + len);
|
||||||
|
const result = [];
|
||||||
|
for (let i = 0; i < slice.length; i++) {
|
||||||
|
result.push(wasm.__wbg_anyref_table.get(slice[i]));
|
||||||
|
}
|
||||||
|
wasm.__wbindgen_drop_anyref_slice(ptr, len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
self.require_internal_export("__wbindgen_drop_anyref_slice")?;
|
||||||
|
} else {
|
||||||
self.expose_take_object();
|
self.expose_take_object();
|
||||||
self.global(
|
self.global(
|
||||||
"
|
"
|
||||||
@ -1262,6 +1386,8 @@ impl<'a> Context<'a> {
|
|||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn expose_get_array_i8_from_wasm(&mut self) {
|
fn expose_get_array_i8_from_wasm(&mut self) {
|
||||||
self.expose_int8_memory();
|
self.expose_int8_memory();
|
||||||
@ -1553,11 +1679,24 @@ impl<'a> Context<'a> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expose_handle_error(&mut self) {
|
fn expose_handle_error(&mut self) -> Result<(), Error> {
|
||||||
if !self.should_write_global("handle_error") {
|
if !self.should_write_global("handle_error") {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
self.expose_uint32_memory();
|
self.expose_uint32_memory();
|
||||||
|
if self.config.anyref {
|
||||||
|
self.expose_add_to_anyref_table()?;
|
||||||
|
self.global(
|
||||||
|
"
|
||||||
|
function handleError(exnptr, e) {
|
||||||
|
const idx = addToAnyrefTable(e);
|
||||||
|
const view = getUint32Memory();
|
||||||
|
view[exnptr / 4] = 1;
|
||||||
|
view[exnptr / 4 + 1] = idx;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
self.expose_add_heap_object();
|
self.expose_add_heap_object();
|
||||||
self.global(
|
self.global(
|
||||||
"
|
"
|
||||||
@ -1569,6 +1708,8 @@ impl<'a> Context<'a> {
|
|||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn wasm_import_needed(&self, name: &str) -> bool {
|
fn wasm_import_needed(&self, name: &str) -> bool {
|
||||||
self.module
|
self.module
|
||||||
@ -1615,8 +1756,8 @@ impl<'a> Context<'a> {
|
|||||||
Ok(s)
|
Ok(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expose_get_vector_from_wasm(&mut self, ty: VectorKind) -> &'static str {
|
fn expose_get_vector_from_wasm(&mut self, ty: VectorKind) -> Result<&'static str, Error> {
|
||||||
match ty {
|
Ok(match ty {
|
||||||
VectorKind::String => {
|
VectorKind::String => {
|
||||||
self.expose_get_string_from_wasm();
|
self.expose_get_string_from_wasm();
|
||||||
"getStringFromWasm"
|
"getStringFromWasm"
|
||||||
@ -1666,10 +1807,10 @@ impl<'a> Context<'a> {
|
|||||||
"getArrayF64FromWasm"
|
"getArrayF64FromWasm"
|
||||||
}
|
}
|
||||||
VectorKind::Anyref => {
|
VectorKind::Anyref => {
|
||||||
self.expose_get_array_js_value_from_wasm();
|
self.expose_get_array_js_value_from_wasm()?;
|
||||||
"getArrayJsValueFromWasm"
|
"getArrayJsValueFromWasm"
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expose_global_argument_ptr(&mut self) -> Result<(), Error> {
|
fn expose_global_argument_ptr(&mut self) -> Result<(), Error> {
|
||||||
@ -2051,24 +2192,24 @@ impl<'a> Context<'a> {
|
|||||||
_ => bail!("export `{}` wasn't a function", start),
|
_ => bail!("export `{}` wasn't a function", start),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(prev) = self.module.start {
|
let prev_start = match self.module.start {
|
||||||
let prev = self.module.funcs.get(prev);
|
Some(f) => f,
|
||||||
if let Some(prev) = &prev.name {
|
None => {
|
||||||
bail!(
|
|
||||||
"cannot flag `{}` as start function as `{}` is \
|
|
||||||
already the start function",
|
|
||||||
start,
|
|
||||||
prev
|
|
||||||
);
|
|
||||||
}
|
|
||||||
bail!(
|
|
||||||
"cannot flag `{}` as start function as another \
|
|
||||||
function is already the start function",
|
|
||||||
start
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.module.start = Some(id);
|
self.module.start = Some(id);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note that we call the previous start function, if any, first. This is
|
||||||
|
// because the start function currently only shows up when it's injected
|
||||||
|
// through thread/anyref transforms. These injected start functions need
|
||||||
|
// to happen before user code, so we always schedule them first.
|
||||||
|
let mut builder = walrus::FunctionBuilder::new();
|
||||||
|
let call1 = builder.call(prev_start, Box::new([]));
|
||||||
|
let call2 = builder.call(id, Box::new([]));
|
||||||
|
let ty = self.module.funcs.get(id).ty();
|
||||||
|
let new_start = builder.finish(ty, Vec::new(), vec![call1, call2], self.module);
|
||||||
|
self.module.start = Some(new_start);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2098,8 +2239,66 @@ impl<'a> Context<'a> {
|
|||||||
let id =
|
let id =
|
||||||
self.module
|
self.module
|
||||||
.add_import_func("__wbindgen_placeholder__", "__wbindgen_defer_start", ty);
|
.add_import_func("__wbindgen_placeholder__", "__wbindgen_defer_start", ty);
|
||||||
|
assert!(self.module.start.is_none());
|
||||||
self.module.start = Some(id);
|
self.module.start = Some(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expose_anyref_table(&mut self) {
|
||||||
|
assert!(self.config.anyref);
|
||||||
|
if !self.should_write_global("anyref_table") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.module
|
||||||
|
.exports
|
||||||
|
.add("__wbg_anyref_table", self.anyref.anyref_table_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expose_add_to_anyref_table(&mut self) -> Result<(), Error> {
|
||||||
|
assert!(self.config.anyref);
|
||||||
|
if !self.should_write_global("add_to_anyref_table") {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.expose_anyref_table();
|
||||||
|
self.require_internal_export("__wbindgen_anyref_table_alloc")?;
|
||||||
|
self.global(
|
||||||
|
"
|
||||||
|
function addToAnyrefTable(obj) {
|
||||||
|
const idx = wasm.__wbindgen_anyref_table_alloc();
|
||||||
|
wasm.__wbg_anyref_table.set(idx, obj);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_heap_object(&mut self, expr: &str) -> String {
|
||||||
|
if self.config.anyref {
|
||||||
|
expr.to_string()
|
||||||
|
} else {
|
||||||
|
self.expose_add_heap_object();
|
||||||
|
format!("addHeapObject({})", expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_object(&mut self, expr: &str) -> String {
|
||||||
|
if self.config.anyref {
|
||||||
|
expr.to_string()
|
||||||
|
} else {
|
||||||
|
self.expose_take_object();
|
||||||
|
format!("takeObject({})", expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_object(&mut self, expr: &str) -> String {
|
||||||
|
if self.config.anyref {
|
||||||
|
expr.to_string()
|
||||||
|
} else {
|
||||||
|
self.expose_get_object();
|
||||||
|
format!("getObject({})", expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> SubContext<'a, 'b> {
|
impl<'a, 'b> SubContext<'a, 'b> {
|
||||||
@ -2153,7 +2352,11 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
|
|
||||||
let (js, ts, js_doc) = Js2Rust::new(&export.function.name, self.cx)
|
let (js, ts, js_doc) = Js2Rust::new(&export.function.name, self.cx)
|
||||||
.process(descriptor.unwrap_function())?
|
.process(descriptor.unwrap_function())?
|
||||||
.finish("function", &format!("wasm.{}", export.function.name));
|
.finish(
|
||||||
|
"function",
|
||||||
|
&format!("wasm.{}", export.function.name),
|
||||||
|
ExportedShim::Named(&export.function.name),
|
||||||
|
);
|
||||||
self.cx.export(
|
self.cx.export(
|
||||||
&export.function.name,
|
&export.function.name,
|
||||||
&js,
|
&js,
|
||||||
@ -2205,7 +2408,11 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
None
|
None
|
||||||
})
|
})
|
||||||
.process(descriptor.unwrap_function())?
|
.process(descriptor.unwrap_function())?
|
||||||
.finish("", &format!("wasm.{}", wasm_name));
|
.finish(
|
||||||
|
"",
|
||||||
|
&format!("wasm.{}", wasm_name),
|
||||||
|
ExportedShim::Named(&wasm_name),
|
||||||
|
);
|
||||||
|
|
||||||
let class = self
|
let class = self
|
||||||
.cx
|
.cx
|
||||||
@ -2276,19 +2483,11 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
|
|
||||||
// TODO: should support more types to import here
|
// TODO: should support more types to import here
|
||||||
let obj = self.import_name(info, &import.name)?;
|
let obj = self.import_name(info, &import.name)?;
|
||||||
self.cx.expose_add_heap_object();
|
self.cx
|
||||||
self.cx.export(
|
.anyref
|
||||||
&import.shim,
|
.import_xform("__wbindgen_placeholder__", &import.shim, &[], true);
|
||||||
&format!(
|
let body = format!("function() {{ return {}; }}", self.cx.add_heap_object(&obj));
|
||||||
"
|
self.cx.export(&import.shim, &body, None);
|
||||||
function() {{
|
|
||||||
return addHeapObject({});
|
|
||||||
}}
|
|
||||||
",
|
|
||||||
obj
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2342,6 +2541,15 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
} = name
|
} = name
|
||||||
{
|
{
|
||||||
shim.cx.direct_imports.insert(import.shim, (module, name));
|
shim.cx.direct_imports.insert(import.shim, (module, name));
|
||||||
|
|
||||||
|
if shim.ret_anyref || shim.anyref_args.len() > 0 {
|
||||||
|
shim.cx.anyref.import_xform(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
&import.shim,
|
||||||
|
&shim.anyref_args,
|
||||||
|
shim.ret_anyref,
|
||||||
|
);
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2350,7 +2558,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
// here (possibly emitting some glue in our JS module) and then emit the
|
// here (possibly emitting some glue in our JS module) and then emit the
|
||||||
// shim as the wasm will be importing the shim.
|
// shim as the wasm will be importing the shim.
|
||||||
let target = shim.cx.generated_import_target(name, import)?;
|
let target = shim.cx.generated_import_target(name, import)?;
|
||||||
let js = shim.finish(&target)?;
|
let js = shim.finish(&target, &import.shim)?;
|
||||||
shim.cx.export(&import.shim, &js, None);
|
shim.cx.export(&import.shim, &js, None);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -2364,14 +2572,16 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let name = self.import_name(info, &import.name)?;
|
let name = self.import_name(info, &import.name)?;
|
||||||
self.cx.expose_get_object();
|
self.cx.anyref.import_xform(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
&import.instanceof_shim,
|
||||||
|
&[(0, false)],
|
||||||
|
false,
|
||||||
|
);
|
||||||
let body = format!(
|
let body = format!(
|
||||||
"
|
"function(idx) {{ return {} instanceof {} ? 1 : 0; }}",
|
||||||
function(idx) {{
|
self.cx.get_object("idx"),
|
||||||
return getObject(idx) instanceof {} ? 1 : 0;
|
name
|
||||||
}}
|
|
||||||
",
|
|
||||||
name,
|
|
||||||
);
|
);
|
||||||
self.cx.export(&import.instanceof_shim, &body, None);
|
self.cx.export(&import.instanceof_shim, &body, None);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -2412,6 +2622,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let set = {
|
let set = {
|
||||||
|
let setter = ExportedShim::Named(&wasm_setter);
|
||||||
let mut cx = Js2Rust::new(&field.name, self.cx);
|
let mut cx = Js2Rust::new(&field.name, self.cx);
|
||||||
cx.method(true, false)
|
cx.method(true, false)
|
||||||
.argument(&descriptor)?
|
.argument(&descriptor)?
|
||||||
@ -2422,12 +2633,13 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
field.name,
|
field.name,
|
||||||
&cx.js_arguments[0].1
|
&cx.js_arguments[0].1
|
||||||
));
|
));
|
||||||
cx.finish("", &format!("wasm.{}", wasm_setter)).0
|
cx.finish("", &format!("wasm.{}", wasm_setter), setter).0
|
||||||
};
|
};
|
||||||
|
let getter = ExportedShim::Named(&wasm_getter);
|
||||||
let (get, _ts, js_doc) = Js2Rust::new(&field.name, self.cx)
|
let (get, _ts, js_doc) = Js2Rust::new(&field.name, self.cx)
|
||||||
.method(true, false)
|
.method(true, false)
|
||||||
.ret(&descriptor)?
|
.ret(&descriptor)?
|
||||||
.finish("", &format!("wasm.{}", wasm_getter));
|
.finish("", &format!("wasm.{}", wasm_getter), getter);
|
||||||
if !dst.ends_with("\n") {
|
if !dst.ends_with("\n") {
|
||||||
dst.push_str("\n");
|
dst.push_str("\n");
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::descriptor::{Descriptor, Function};
|
use crate::descriptor::{Descriptor, Function};
|
||||||
|
use crate::js::js2rust::ExportedShim;
|
||||||
use crate::js::{Context, ImportTarget, Js2Rust};
|
use crate::js::{Context, ImportTarget, Js2Rust};
|
||||||
use failure::{bail, Error};
|
use failure::{bail, Error};
|
||||||
|
|
||||||
@ -39,6 +40,11 @@ pub struct Rust2Js<'a, 'b: 'a> {
|
|||||||
|
|
||||||
/// Whether or not the last argument is a slice representing variadic arguments.
|
/// Whether or not the last argument is a slice representing variadic arguments.
|
||||||
variadic: bool,
|
variadic: bool,
|
||||||
|
|
||||||
|
/// list of arguments that are anyref, and whether they're an owned anyref
|
||||||
|
/// or not.
|
||||||
|
pub anyref_args: Vec<(usize, bool)>,
|
||||||
|
pub ret_anyref: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Rust2Js<'a, 'b> {
|
impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||||
@ -55,6 +61,8 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
catch: false,
|
catch: false,
|
||||||
catch_and_rethrow: false,
|
catch_and_rethrow: false,
|
||||||
variadic: false,
|
variadic: false,
|
||||||
|
anyref_args: Vec::new(),
|
||||||
|
ret_anyref: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +109,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
|
|
||||||
if let Some(ty) = arg.vector_kind() {
|
if let Some(ty) = arg.vector_kind() {
|
||||||
let abi2 = self.shim_argument();
|
let abi2 = self.shim_argument();
|
||||||
let f = self.cx.expose_get_vector_from_wasm(ty);
|
let f = self.cx.expose_get_vector_from_wasm(ty)?;
|
||||||
self.prelude(&format!(
|
self.prelude(&format!(
|
||||||
"let v{0} = {prefix}{func}({0}, {1});",
|
"let v{0} = {prefix}{func}({0}, {1});",
|
||||||
abi,
|
abi,
|
||||||
@ -141,12 +149,14 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
// No need to special case `optional` here because `takeObject` will
|
// No need to special case `optional` here because `takeObject` will
|
||||||
// naturally work.
|
// naturally work.
|
||||||
if arg.is_anyref() {
|
if arg.is_anyref() {
|
||||||
self.cx.expose_take_object();
|
let arg = self.cx.take_object(&abi);
|
||||||
self.js_arguments.push(format!("takeObject({})", abi));
|
self.js_arguments.push(arg);
|
||||||
|
self.anyref_args.push((self.arg_idx - 1, true));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else if arg.is_ref_anyref() {
|
} else if arg.is_ref_anyref() {
|
||||||
self.cx.expose_get_object();
|
let arg = self.cx.get_object(&abi);
|
||||||
self.js_arguments.push(format!("getObject({})", abi));
|
self.js_arguments.push(arg);
|
||||||
|
self.anyref_args.push((self.arg_idx - 1, false));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,6 +273,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
|
|
||||||
if let Some((f, mutable)) = arg.stack_closure() {
|
if let Some((f, mutable)) = arg.stack_closure() {
|
||||||
let arg2 = self.shim_argument();
|
let arg2 = self.shim_argument();
|
||||||
|
let mut shim = f.shim_idx;
|
||||||
let (js, _ts, _js_doc) = {
|
let (js, _ts, _js_doc) = {
|
||||||
let mut builder = Js2Rust::new("", self.cx);
|
let mut builder = Js2Rust::new("", self.cx);
|
||||||
if mutable {
|
if mutable {
|
||||||
@ -274,10 +285,11 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
} else {
|
} else {
|
||||||
builder.rust_argument("this.a");
|
builder.rust_argument("this.a");
|
||||||
}
|
}
|
||||||
builder
|
builder.rust_argument("this.b").process(f)?.finish(
|
||||||
.rust_argument("this.b")
|
"function",
|
||||||
.process(f)?
|
"this.f",
|
||||||
.finish("function", "this.f")
|
ExportedShim::TableElement(&mut shim),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
self.cx.function_table_needed = true;
|
self.cx.function_table_needed = true;
|
||||||
self.global_idx();
|
self.global_idx();
|
||||||
@ -291,7 +303,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
abi,
|
abi,
|
||||||
arg2,
|
arg2,
|
||||||
js = js,
|
js = js,
|
||||||
idx = f.shim_idx,
|
idx = shim,
|
||||||
));
|
));
|
||||||
self.finally(&format!("cb{0}.a = cb{0}.b = 0;", abi));
|
self.finally(&format!("cb{0}.a = cb{0}.b = 0;", abi));
|
||||||
self.js_arguments.push(format!("cb{0}.bind(cb{0})", abi));
|
self.js_arguments.push(format!("cb{0}.bind(cb{0})", abi));
|
||||||
@ -349,6 +361,20 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if ty.is_anyref() {
|
if ty.is_anyref() {
|
||||||
|
if self.cx.config.anyref {
|
||||||
|
if optional {
|
||||||
|
self.cx.expose_add_to_anyref_table()?;
|
||||||
|
self.cx.expose_is_like_none();
|
||||||
|
self.ret_expr = "
|
||||||
|
const val = JS;
|
||||||
|
return isLikeNone(val) ? 0 : addToAnyrefTable(val);
|
||||||
|
"
|
||||||
|
.to_string();
|
||||||
|
} else {
|
||||||
|
self.ret_anyref = true;
|
||||||
|
self.ret_expr = "return JS;".to_string()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
self.cx.expose_add_heap_object();
|
self.cx.expose_add_heap_object();
|
||||||
if optional {
|
if optional {
|
||||||
self.cx.expose_is_like_none();
|
self.cx.expose_is_like_none();
|
||||||
@ -360,6 +386,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
} else {
|
} else {
|
||||||
self.ret_expr = "return addHeapObject(JS);".to_string()
|
self.ret_expr = "return addHeapObject(JS);".to_string()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if optional {
|
if optional {
|
||||||
@ -565,6 +592,8 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
arg_idx: _,
|
arg_idx: _,
|
||||||
cx: _,
|
cx: _,
|
||||||
global_idx: _,
|
global_idx: _,
|
||||||
|
anyref_args: _,
|
||||||
|
ret_anyref: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
!catch &&
|
!catch &&
|
||||||
@ -581,7 +610,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
js_arguments == shim_arguments
|
js_arguments == shim_arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(&mut self, invoc: &ImportTarget) -> Result<String, Error> {
|
pub fn finish(&mut self, invoc: &ImportTarget, shim: &str) -> Result<String, Error> {
|
||||||
let mut ret = String::new();
|
let mut ret = String::new();
|
||||||
ret.push_str("function(");
|
ret.push_str("function(");
|
||||||
ret.push_str(&self.shim_arguments.join(", "));
|
ret.push_str(&self.shim_arguments.join(", "));
|
||||||
@ -596,7 +625,6 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
|
|
||||||
let variadic = self.variadic;
|
let variadic = self.variadic;
|
||||||
let ret_expr = &self.ret_expr;
|
let ret_expr = &self.ret_expr;
|
||||||
let js_arguments = &self.js_arguments;
|
|
||||||
let handle_variadic = |invoc: &str, js_arguments: &[String]| {
|
let handle_variadic = |invoc: &str, js_arguments: &[String]| {
|
||||||
let ret = if variadic {
|
let ret = if variadic {
|
||||||
let (last_arg, args) = match js_arguments.split_last() {
|
let (last_arg, args) = match js_arguments.split_last() {
|
||||||
@ -617,6 +645,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
Ok(ret)
|
Ok(ret)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let js_arguments = &self.js_arguments;
|
||||||
let fixed = |desc: &str, class: &Option<String>, amt: usize| {
|
let fixed = |desc: &str, class: &Option<String>, amt: usize| {
|
||||||
if variadic {
|
if variadic {
|
||||||
bail!("{} cannot be variadic", desc);
|
bail!("{} cannot be variadic", desc);
|
||||||
@ -670,7 +699,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if self.catch {
|
if self.catch {
|
||||||
self.cx.expose_handle_error();
|
self.cx.expose_handle_error()?;
|
||||||
invoc = format!(
|
invoc = format!(
|
||||||
"\
|
"\
|
||||||
try {{\n\
|
try {{\n\
|
||||||
@ -712,6 +741,33 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
ret.push_str(&invoc);
|
ret.push_str(&invoc);
|
||||||
|
|
||||||
ret.push_str("\n}\n");
|
ret.push_str("\n}\n");
|
||||||
|
|
||||||
|
if self.ret_anyref || self.anyref_args.len() > 0 {
|
||||||
|
// Some return values go at the the beginning of the argument list
|
||||||
|
// (they force a return pointer). Handle that here by offsetting all
|
||||||
|
// our arg indices by one, but throw in some sanity checks for if
|
||||||
|
// this ever changes.
|
||||||
|
if let Some(start) = self.shim_arguments.get(0) {
|
||||||
|
if start == "ret" {
|
||||||
|
assert!(!self.ret_anyref);
|
||||||
|
if let Some(next) = self.shim_arguments.get(1) {
|
||||||
|
assert_eq!(next, "arg0");
|
||||||
|
}
|
||||||
|
for (idx, _) in self.anyref_args.iter_mut() {
|
||||||
|
*idx += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert_eq!(start, "arg0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.cx.anyref.import_xform(
|
||||||
|
"__wbindgen_placeholder__",
|
||||||
|
shim,
|
||||||
|
&self.anyref_args,
|
||||||
|
self.ret_anyref,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ pub struct Bindgen {
|
|||||||
// Experimental support for the wasm threads proposal, transforms the wasm
|
// Experimental support for the wasm threads proposal, transforms the wasm
|
||||||
// module to be "ready to be instantiated on any thread"
|
// module to be "ready to be instantiated on any thread"
|
||||||
threads: Option<wasm_bindgen_threads_xform::Config>,
|
threads: Option<wasm_bindgen_threads_xform::Config>,
|
||||||
|
anyref: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Input {
|
enum Input {
|
||||||
@ -62,6 +63,7 @@ impl Bindgen {
|
|||||||
emit_start: true,
|
emit_start: true,
|
||||||
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
|
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
|
||||||
threads: threads_config(),
|
threads: threads_config(),
|
||||||
|
anyref: env::var("WASM_BINDGEN_ANYREF").is_ok(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +178,22 @@ impl Bindgen {
|
|||||||
(module, stem)
|
(module, stem)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This isn't the hardest thing in the world too support but we
|
||||||
|
// basically don't know how to rationalize #[wasm_bindgen(start)] and
|
||||||
|
// the actual `start` function if present. Figure this out later if it
|
||||||
|
// comes up, but otherwise we should continue to be compatible with
|
||||||
|
// LLVM's output today.
|
||||||
|
//
|
||||||
|
// Note that start function handling in `js/mod.rs` will need to be
|
||||||
|
// updated as well, because `#[wasm_bindgen(start)]` is inserted *after*
|
||||||
|
// a module's start function, if any, because we assume start functions
|
||||||
|
// only show up when injected on behalf of wasm-bindgen's passes.
|
||||||
|
if module.start.is_some() {
|
||||||
|
bail!("wasm-bindgen is currently incompatible with modules that \
|
||||||
|
already have a start function");
|
||||||
|
}
|
||||||
|
|
||||||
let mut program_storage = Vec::new();
|
let mut program_storage = Vec::new();
|
||||||
let programs = extract_programs(&mut module, &mut program_storage)
|
let programs = extract_programs(&mut module, &mut program_storage)
|
||||||
.with_context(|_| "failed to extract wasm-bindgen custom sections")?;
|
.with_context(|_| "failed to extract wasm-bindgen custom sections")?;
|
||||||
@ -233,7 +251,10 @@ impl Bindgen {
|
|||||||
imported_statics: Default::default(),
|
imported_statics: Default::default(),
|
||||||
direct_imports: Default::default(),
|
direct_imports: Default::default(),
|
||||||
start: None,
|
start: None,
|
||||||
|
anyref: Default::default(),
|
||||||
};
|
};
|
||||||
|
cx.anyref.enabled = self.anyref;
|
||||||
|
cx.anyref.prepare(cx.module)?;
|
||||||
for program in programs.iter() {
|
for program in programs.iter() {
|
||||||
js::SubContext {
|
js::SubContext {
|
||||||
program,
|
program,
|
||||||
|
@ -191,7 +191,10 @@ fn set_f64() {
|
|||||||
|
|
||||||
Reflect::set_f64(&a, 0.0, &JsValue::from_str("Bye!")).unwrap();
|
Reflect::set_f64(&a, 0.0, &JsValue::from_str("Bye!")).unwrap();
|
||||||
|
|
||||||
assert_eq!(Reflect::get_f64(&a, 0.0).unwrap(), JsValue::from_str("Bye!"));
|
assert_eq!(
|
||||||
|
Reflect::get_f64(&a, 0.0).unwrap(),
|
||||||
|
JsValue::from_str("Bye!")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
|
@ -27,6 +27,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[
|
|||||||
"wasm-bindgen-wasm-interpreter",
|
"wasm-bindgen-wasm-interpreter",
|
||||||
"wasm-bindgen-webidl",
|
"wasm-bindgen-webidl",
|
||||||
"wasm-bindgen-threads-xform",
|
"wasm-bindgen-threads-xform",
|
||||||
|
"wasm-bindgen-anyref-xform",
|
||||||
"wasm-bindgen-cli-support",
|
"wasm-bindgen-cli-support",
|
||||||
"wasm-bindgen-cli",
|
"wasm-bindgen-cli",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
208
src/anyref.rs
Normal file
208
src/anyref.rs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
use std::slice;
|
||||||
|
use std::vec::Vec;
|
||||||
|
use std::ptr;
|
||||||
|
use std::alloc::{self, Layout};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use JsValue;
|
||||||
|
|
||||||
|
externs! {
|
||||||
|
#[link(wasm_import_module = "__wbindgen_anyref_xform__")]
|
||||||
|
extern "C" {
|
||||||
|
fn __wbindgen_anyref_table_grow(delta: usize) -> i32;
|
||||||
|
fn __wbindgen_anyref_table_set_null(idx: usize) -> ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Slab {
|
||||||
|
data: Vec<usize>,
|
||||||
|
head: usize,
|
||||||
|
base: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Slab {
|
||||||
|
fn new() -> Slab {
|
||||||
|
Slab {
|
||||||
|
data: Vec::new(),
|
||||||
|
head: 0,
|
||||||
|
base: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc(&mut self) -> usize {
|
||||||
|
let ret = self.head;
|
||||||
|
if ret == self.data.len() {
|
||||||
|
if self.data.len() == self.data.capacity() {
|
||||||
|
let extra = 128;
|
||||||
|
let r = unsafe {
|
||||||
|
__wbindgen_anyref_table_grow(extra)
|
||||||
|
};
|
||||||
|
if r == -1 {
|
||||||
|
internal_error("table grow failure")
|
||||||
|
}
|
||||||
|
if self.base == 0 {
|
||||||
|
self.base = r as usize + (super::JSIDX_RESERVED as usize);
|
||||||
|
} else if self.base + self.data.len() != r as usize {
|
||||||
|
internal_error("someone else allocated table entires?")
|
||||||
|
}
|
||||||
|
|
||||||
|
// poor man's `try_reserve_exact` until that's stable
|
||||||
|
unsafe {
|
||||||
|
let new_cap = self.data.capacity() + extra;
|
||||||
|
let size = mem::size_of::<usize>() * new_cap;
|
||||||
|
let align = mem::align_of::<usize>();
|
||||||
|
let layout = match Layout::from_size_align(size, align) {
|
||||||
|
Ok(l) => l,
|
||||||
|
Err(_) => internal_error("size/align layout failure"),
|
||||||
|
};
|
||||||
|
let ptr = alloc::alloc(layout) as *mut usize;
|
||||||
|
if ptr.is_null() {
|
||||||
|
internal_error("allocation failure");
|
||||||
|
}
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
self.data.as_ptr(),
|
||||||
|
ptr,
|
||||||
|
self.data.len(),
|
||||||
|
);
|
||||||
|
let new_vec = Vec::from_raw_parts(
|
||||||
|
ptr,
|
||||||
|
self.data.len(),
|
||||||
|
new_cap,
|
||||||
|
);
|
||||||
|
let mut old = mem::replace(&mut self.data, new_vec);
|
||||||
|
old.set_len(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom condition to ensure `push` below doesn't call `reserve` in
|
||||||
|
// optimized builds which pulls in lots of panic infrastructure
|
||||||
|
if self.data.len() >= self.data.capacity() {
|
||||||
|
internal_error("push should be infallible now")
|
||||||
|
}
|
||||||
|
self.data.push(ret + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// usage of `get_mut` thwarts panicking infrastructure in optimized
|
||||||
|
// builds
|
||||||
|
match self.data.get_mut(ret) {
|
||||||
|
Some(slot) => self.head = *slot,
|
||||||
|
None => internal_error("ret out of bounds"),
|
||||||
|
}
|
||||||
|
ret + self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dealloc(&mut self, slot: usize) {
|
||||||
|
if slot < self.base {
|
||||||
|
internal_error("free reserved slot");
|
||||||
|
}
|
||||||
|
let slot = slot - self.base;
|
||||||
|
|
||||||
|
// usage of `get_mut` thwarts panicking infrastructure in optimized
|
||||||
|
// builds
|
||||||
|
match self.data.get_mut(slot) {
|
||||||
|
Some(ptr) => {
|
||||||
|
*ptr = self.head;
|
||||||
|
self.head = slot;
|
||||||
|
}
|
||||||
|
None => internal_error("slot out of bounds"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn internal_error(msg: &str) -> ! {
|
||||||
|
let msg = if cfg!(debug_assertions) { msg } else { "" };
|
||||||
|
super::throw_str(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whoa, there's two `tl` modules here! That's currently intention, but for sort
|
||||||
|
// of a weird reason. The table here is fundamentally thread local, so we want
|
||||||
|
// to use the `thread_local!` macro. The implementation of thread locals (as of
|
||||||
|
// the time of this writing) generates a lot of code as it pulls in panic paths
|
||||||
|
// in libstd (even when using `try_with`). There's a patch to fix that
|
||||||
|
// (rust-lang/rust#55518), but in the meantime the stable/beta channels produce
|
||||||
|
// a lot of code.
|
||||||
|
//
|
||||||
|
// Matters are made worse here because this code is almost never used (it's only
|
||||||
|
// here for an unstable feature). If we were to have panics here, though, then
|
||||||
|
// we couldn't effectively gc away the panic infrastructure, meaning this unused
|
||||||
|
// infrastructure would show up in binaries! That's a no-no for wasm-bindgen.
|
||||||
|
//
|
||||||
|
// In the meantime, if the atomics feature is turned on (which it never is by
|
||||||
|
// default) then we use `thread_local!`, otherwise we use a home-grown
|
||||||
|
// implementation that will be replaced once #55518 lands on stable.
|
||||||
|
#[cfg(target_feature = "atomics")]
|
||||||
|
mod tl {
|
||||||
|
use std::*; // hack to get `thread_local!` to work
|
||||||
|
use super::Slab;
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
thread_local!(pub static HEAP_SLAB: Cell<Slab> = Cell::new(Slab::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_feature = "atomics"))]
|
||||||
|
mod tl {
|
||||||
|
use std::alloc::{self, Layout};
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::ptr;
|
||||||
|
use super::Slab;
|
||||||
|
|
||||||
|
pub struct HeapSlab;
|
||||||
|
pub static HEAP_SLAB: HeapSlab = HeapSlab;
|
||||||
|
static mut SLOT: *mut Cell<Slab> = 0 as *mut Cell<Slab>;
|
||||||
|
|
||||||
|
impl HeapSlab {
|
||||||
|
pub fn try_with<R>(&self, f: impl FnOnce(&Cell<Slab>) -> R) -> Result<R, ()> {
|
||||||
|
unsafe {
|
||||||
|
if SLOT.is_null() {
|
||||||
|
let ptr = alloc::alloc(Layout::new::<Cell<Slab>>());
|
||||||
|
if ptr.is_null() {
|
||||||
|
super::internal_error("allocation failure");
|
||||||
|
}
|
||||||
|
let ptr = ptr as *mut Cell<Slab>;
|
||||||
|
ptr::write(ptr, Cell::new(Slab::new()));
|
||||||
|
SLOT = ptr;
|
||||||
|
}
|
||||||
|
Ok(f(&*SLOT))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn __wbindgen_anyref_table_alloc() -> usize {
|
||||||
|
tl::HEAP_SLAB.try_with(|slot| {
|
||||||
|
let mut slab = slot.replace(Slab::new());
|
||||||
|
let ret = slab.alloc();
|
||||||
|
slot.replace(slab);
|
||||||
|
ret
|
||||||
|
}).unwrap_or_else(|_| internal_error("tls access failure"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn __wbindgen_anyref_table_dealloc(idx: usize) {
|
||||||
|
if idx < super::JSIDX_RESERVED as usize {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// clear this value from the table so while the table slot is un-allocated
|
||||||
|
// we don't keep around a strong reference to a potentially large object
|
||||||
|
unsafe {
|
||||||
|
__wbindgen_anyref_table_set_null(idx);
|
||||||
|
}
|
||||||
|
tl::HEAP_SLAB.try_with(|slot| {
|
||||||
|
let mut slab = slot.replace(Slab::new());
|
||||||
|
slab.dealloc(idx);
|
||||||
|
slot.replace(slab);
|
||||||
|
}).unwrap_or_else(|_| internal_error("tls access failure"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern fn __wbindgen_drop_anyref_slice(ptr: *mut JsValue, len: usize) {
|
||||||
|
for slot in slice::from_raw_parts_mut(ptr, len) {
|
||||||
|
ptr::drop_in_place(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// see comment in module above this in `link_mem_intrinsics`
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn link_intrinsics() {
|
||||||
|
}
|
43
src/lib.rs
43
src/lib.rs
@ -30,6 +30,24 @@ macro_rules! if_std {
|
|||||||
)*)
|
)*)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! externs {
|
||||||
|
($(#[$attr:meta])* extern "C" { $(fn $name:ident($($args:tt)*) -> $ret:ty;)* }) => (
|
||||||
|
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
||||||
|
$(#[$attr])*
|
||||||
|
extern "C" {
|
||||||
|
$(fn $name($($args)*) -> $ret;)*
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
unsafe extern fn $name($($args)*) -> $ret {
|
||||||
|
panic!("function not implemented on non-wasm32 targets")
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// A module which is typically glob imported from:
|
/// A module which is typically glob imported from:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -57,6 +75,7 @@ if_std! {
|
|||||||
extern crate std;
|
extern crate std;
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
pub mod closure;
|
pub mod closure;
|
||||||
|
mod anyref;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of an object owned by JS.
|
/// Representation of an object owned by JS.
|
||||||
@ -462,25 +481,9 @@ macro_rules! numbers {
|
|||||||
|
|
||||||
numbers! { i8 u8 i16 u16 i32 u32 f32 f64 }
|
numbers! { i8 u8 i16 u16 i32 u32 f32 f64 }
|
||||||
|
|
||||||
macro_rules! externs {
|
externs! {
|
||||||
($(fn $name:ident($($args:tt)*) -> $ret:ty;)*) => (
|
|
||||||
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
|
||||||
#[link(wasm_import_module = "__wbindgen_placeholder__")]
|
#[link(wasm_import_module = "__wbindgen_placeholder__")]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
$(fn $name($($args)*) -> $ret;)*
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
unsafe extern "C" fn $name($($args)*) -> $ret {
|
|
||||||
panic!("function not implemented on non-wasm32 targets")
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
externs! {
|
|
||||||
fn __wbindgen_object_clone_ref(idx: u32) -> u32;
|
fn __wbindgen_object_clone_ref(idx: u32) -> u32;
|
||||||
fn __wbindgen_object_drop_ref(idx: u32) -> ();
|
fn __wbindgen_object_drop_ref(idx: u32) -> ();
|
||||||
fn __wbindgen_string_new(ptr: *const u8, len: usize) -> u32;
|
fn __wbindgen_string_new(ptr: *const u8, len: usize) -> u32;
|
||||||
@ -512,8 +515,10 @@ externs! {
|
|||||||
fn __wbindgen_memory() -> u32;
|
fn __wbindgen_memory() -> u32;
|
||||||
fn __wbindgen_module() -> u32;
|
fn __wbindgen_module() -> u32;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Clone for JsValue {
|
impl Clone for JsValue {
|
||||||
|
#[inline]
|
||||||
fn clone(&self) -> JsValue {
|
fn clone(&self) -> JsValue {
|
||||||
unsafe {
|
unsafe {
|
||||||
let idx = __wbindgen_object_clone_ref(self.idx);
|
let idx = __wbindgen_object_clone_ref(self.idx);
|
||||||
@ -973,7 +978,9 @@ pub mod __rt {
|
|||||||
/// in the object file and link the intrinsics.
|
/// in the object file and link the intrinsics.
|
||||||
///
|
///
|
||||||
/// Ideas for how to improve this are most welcome!
|
/// Ideas for how to improve this are most welcome!
|
||||||
pub fn link_mem_intrinsics() {}
|
pub fn link_mem_intrinsics() {
|
||||||
|
::anyref::link_intrinsics();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper type around slices and vectors for binding the `Uint8ClampedArray`
|
/// A wrapper type around slices and vectors for binding the `Uint8ClampedArray`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user