mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-16 06:21:22 +00:00
Merge branch 'master' into variadic_js_functions
This commit is contained in:
@ -94,7 +94,7 @@ pub enum VectorKind {
|
||||
impl Descriptor {
|
||||
pub fn decode(mut data: &[u32]) -> Descriptor {
|
||||
let descriptor = Descriptor::_decode(&mut data);
|
||||
assert!(data.is_empty());
|
||||
assert!(data.is_empty(), "remaining data {:?}", data);
|
||||
descriptor
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ use wasm_gc;
|
||||
|
||||
use super::Bindgen;
|
||||
use descriptor::{Descriptor, VectorKind};
|
||||
use wasm_interpreter::Interpreter;
|
||||
|
||||
mod js2rust;
|
||||
use self::js2rust::Js2Rust;
|
||||
@ -23,6 +24,8 @@ pub struct Context<'a> {
|
||||
pub typescript: String,
|
||||
pub exposed_globals: HashSet<&'static str>,
|
||||
pub required_internal_exports: HashSet<&'static str>,
|
||||
pub imported_functions: HashSet<String>,
|
||||
pub imported_statics: HashSet<String>,
|
||||
pub config: &'a Bindgen,
|
||||
pub module: &'a mut Module,
|
||||
|
||||
@ -41,7 +44,8 @@ pub struct Context<'a> {
|
||||
|
||||
pub exported_classes: HashMap<String, ExportedClass>,
|
||||
pub function_table_needed: bool,
|
||||
pub run_descriptor: &'a Fn(&str) -> Option<Vec<u32>>,
|
||||
pub interpreter: &'a mut Interpreter,
|
||||
pub memory_init: Option<ResizableLimits>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -376,6 +380,19 @@ impl<'a> Context<'a> {
|
||||
))
|
||||
})?;
|
||||
|
||||
self.bind("__wbindgen_memory", &|me| {
|
||||
me.expose_add_heap_object();
|
||||
let mem = me.memory();
|
||||
Ok(format!(
|
||||
"
|
||||
function() {{
|
||||
return addHeapObject({});
|
||||
}}
|
||||
", mem
|
||||
))
|
||||
})?;
|
||||
|
||||
self.create_memory_export();
|
||||
self.unexport_unused_internal_exports();
|
||||
self.gc()?;
|
||||
|
||||
@ -490,6 +507,39 @@ impl<'a> Context<'a> {
|
||||
let mut dst = format!("class {} {{\n", name);
|
||||
let mut ts_dst = format!("export {}", dst);
|
||||
|
||||
let (mkweakref, freeref) = if self.config.weak_refs {
|
||||
// When weak refs are enabled we use them to automatically free the
|
||||
// contents of an exported rust class when it's gc'd. Note that a
|
||||
// manual `free` function still exists for deterministic
|
||||
// destruction.
|
||||
//
|
||||
// This is implemented by using a `WeakRefGroup` to run finalizers
|
||||
// for all `WeakRef` objects that it creates. Upon construction of
|
||||
// a new wasm object we use `makeRef` with "holdings" of a thunk to
|
||||
// free the wasm instance. Once the `this` (the instance we're
|
||||
// creating) is gc'd then the finalizer will run with the
|
||||
// `WeakRef`, and we'll pull out the `holdings`, our pointer.
|
||||
//
|
||||
// Note, though, that if manual finalization happens we want to
|
||||
// cancel the `WeakRef`-generated finalization, so we retain the
|
||||
// `WeakRef` in a global map. This global map is then used to
|
||||
// `drop()` the `WeakRef` (cancel finalization) whenever it is
|
||||
// finalized.
|
||||
self.expose_cleanup_groups();
|
||||
let mk = format!("
|
||||
const cleanup_ptr = this.ptr;
|
||||
const ref = CLEANUPS.makeRef(this, () => free{}(cleanup_ptr));
|
||||
CLEANUPS_MAP.set(this.ptr, ref);
|
||||
", name);
|
||||
let free = "
|
||||
CLEANUPS_MAP.get(ptr).drop();
|
||||
CLEANUPS_MAP.delete(ptr);
|
||||
";
|
||||
(mk, free)
|
||||
} else {
|
||||
(String::new(), "")
|
||||
};
|
||||
|
||||
if self.config.debug || class.constructor.is_some() {
|
||||
self.expose_constructor_token();
|
||||
|
||||
@ -516,9 +566,11 @@ impl<'a> Context<'a> {
|
||||
// This invocation of new will call this constructor with a ConstructorToken
|
||||
let instance = {class}.{constructor}(...args);
|
||||
this.ptr = instance.ptr;
|
||||
{mkweakref}
|
||||
",
|
||||
class = name,
|
||||
constructor = constructor
|
||||
constructor = constructor,
|
||||
mkweakref = mkweakref,
|
||||
));
|
||||
} else {
|
||||
dst.push_str(
|
||||
@ -537,9 +589,11 @@ impl<'a> Context<'a> {
|
||||
|
||||
constructor(ptr) {{
|
||||
this.ptr = ptr;
|
||||
{}
|
||||
}}
|
||||
",
|
||||
name
|
||||
name,
|
||||
mkweakref,
|
||||
));
|
||||
}
|
||||
|
||||
@ -599,15 +653,26 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
self.global(&format!(
|
||||
"
|
||||
function free{}(ptr) {{
|
||||
{}
|
||||
wasm.{}(ptr);
|
||||
}}
|
||||
",
|
||||
name,
|
||||
freeref,
|
||||
shared::free_function(&name)
|
||||
));
|
||||
dst.push_str(&format!(
|
||||
"
|
||||
free() {{
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
wasm.{}(ptr);
|
||||
free{}(ptr);
|
||||
}}
|
||||
",
|
||||
shared::free_function(&name)
|
||||
name,
|
||||
));
|
||||
ts_dst.push_str("free(): void;\n");
|
||||
dst.push_str(&class.contents);
|
||||
@ -636,6 +701,20 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_memory_export(&mut self) {
|
||||
let limits = match self.memory_init.clone() {
|
||||
Some(limits) => limits,
|
||||
None => return,
|
||||
};
|
||||
let mut initializer = String::from("new WebAssembly.Memory({");
|
||||
initializer.push_str(&format!("initial:{}", limits.initial()));
|
||||
if let Some(max) = limits.maximum() {
|
||||
initializer.push_str(&format!(",maximum:{}", max));
|
||||
}
|
||||
initializer.push_str("})");
|
||||
self.export("memory", &initializer, None);
|
||||
}
|
||||
|
||||
fn rewrite_imports(&mut self, module_name: &str) {
|
||||
for (name, contents) in self._rewrite_imports(module_name) {
|
||||
self.export(&name, &contents, None);
|
||||
@ -666,6 +745,15 @@ impl<'a> Context<'a> {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If memory is imported we'll have exported it from the shim module
|
||||
// so let's import it from there.
|
||||
if import.field() == "memory" {
|
||||
import.module_mut().truncate(0);
|
||||
import.module_mut().push_str("./");
|
||||
import.module_mut().push_str(module_name);
|
||||
continue
|
||||
}
|
||||
|
||||
let renamed_import = format!("__wbindgen_{}", import.field());
|
||||
let mut bind_math = |expr: &str| {
|
||||
math_imports.push((renamed_import.clone(), format!("function{}", expr)));
|
||||
@ -1091,13 +1179,30 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
self.expose_text_decoder();
|
||||
self.expose_uint8_memory();
|
||||
self.global(
|
||||
"
|
||||
function getStringFromWasm(ptr, len) {
|
||||
return cachedDecoder.decode(getUint8Memory().subarray(ptr, ptr + len));
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
// Typically we try to give a raw view of memory out to `TextDecoder` to
|
||||
// avoid copying too much data. If, however, a `SharedArrayBuffer` is
|
||||
// being used it looks like that is rejected by `TextDecoder` or
|
||||
// otherwise doesn't work with it. When we detect a shared situation we
|
||||
// use `slice` which creates a new array instead of `subarray` which
|
||||
// creates just a view. That way in shared mode we copy more data but in
|
||||
// non-shared mode there's no need to copy the data except for the
|
||||
// string itself.
|
||||
self.memory(); // set self.memory_init
|
||||
let is_shared = self.module
|
||||
.memory_section()
|
||||
.map(|s| s.entries()[0].limits().shared())
|
||||
.unwrap_or(match &self.memory_init {
|
||||
Some(limits) => limits.shared(),
|
||||
None => false,
|
||||
});
|
||||
let method = if is_shared { "slice" } else { "subarray" };
|
||||
|
||||
self.global(&format!("
|
||||
function getStringFromWasm(ptr, len) {{
|
||||
return cachedDecoder.decode(getUint8Memory().{}(ptr, ptr + len));
|
||||
}}
|
||||
", method));
|
||||
}
|
||||
|
||||
fn expose_get_array_js_value_from_wasm(&mut self) {
|
||||
@ -1284,18 +1389,20 @@ impl<'a> Context<'a> {
|
||||
if !self.exposed_globals.insert(name) {
|
||||
return;
|
||||
}
|
||||
let mem = self.memory();
|
||||
self.global(&format!(
|
||||
"
|
||||
let cache{name} = null;
|
||||
function {name}() {{
|
||||
if (cache{name} === null || cache{name}.buffer !== wasm.memory.buffer) {{
|
||||
cache{name} = new {js}(wasm.memory.buffer);
|
||||
if (cache{name} === null || cache{name}.buffer !== {mem}.buffer) {{
|
||||
cache{name} = new {js}({mem}.buffer);
|
||||
}}
|
||||
return cache{name};
|
||||
}}
|
||||
",
|
||||
name = name,
|
||||
js = js,
|
||||
mem = mem,
|
||||
));
|
||||
}
|
||||
|
||||
@ -1594,6 +1701,18 @@ impl<'a> Context<'a> {
|
||||
");
|
||||
}
|
||||
|
||||
fn expose_cleanup_groups(&mut self) {
|
||||
if !self.exposed_globals.insert("cleanup_groups") {
|
||||
return
|
||||
}
|
||||
self.global(
|
||||
"
|
||||
const CLEANUPS = new WeakRefGroup(x => x.holdings());
|
||||
const CLEANUPS_MAP = new Map();
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
fn gc(&mut self) -> Result<(), Error> {
|
||||
let module = mem::replace(self.module, Module::default());
|
||||
let module = module.parse_names().unwrap_or_else(|p| p.1);
|
||||
@ -1608,9 +1727,9 @@ impl<'a> Context<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn describe(&self, name: &str) -> Option<Descriptor> {
|
||||
fn describe(&mut self, name: &str) -> Option<Descriptor> {
|
||||
let name = format!("__wbindgen_describe_{}", name);
|
||||
(self.run_descriptor)(&name).map(|d| Descriptor::decode(&d))
|
||||
Some(Descriptor::decode(self.interpreter.interpret(&name, self.module)?))
|
||||
}
|
||||
|
||||
fn global(&mut self, s: &str) {
|
||||
@ -1628,6 +1747,29 @@ impl<'a> Context<'a> {
|
||||
fn use_node_require(&self) -> bool {
|
||||
self.config.nodejs && !self.config.nodejs_experimental_modules
|
||||
}
|
||||
|
||||
fn memory(&mut self) -> &'static str {
|
||||
if self.module.memory_section().is_some() {
|
||||
return "wasm.memory";
|
||||
}
|
||||
|
||||
let (entry, mem) = self.module.import_section()
|
||||
.expect("must import memory")
|
||||
.entries()
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
match i.external() {
|
||||
External::Memory(m) => Some((i, m)),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.expect("must import memory");
|
||||
assert_eq!(entry.module(), "env");
|
||||
assert_eq!(entry.field(), "memory");
|
||||
self.memory_init = Some(mem.limits().clone());
|
||||
"memory"
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> SubContext<'a, 'b> {
|
||||
@ -1772,6 +1914,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
info: &shared::Import,
|
||||
import: &shared::ImportStatic,
|
||||
) -> Result<(), Error> {
|
||||
// The same static can be imported in multiple locations, so only
|
||||
// generate bindings once for it.
|
||||
if !self.cx.imported_statics.insert(import.shim.clone()) {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// TODO: should support more types to import here
|
||||
let obj = self.import_name(info, &import.name)?;
|
||||
self.cx.expose_add_heap_object();
|
||||
@ -1799,132 +1947,39 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// It's possible for the same function to be imported in two locations,
|
||||
// but we only want to generate one.
|
||||
if !self.cx.imported_functions.insert(import.shim.clone()) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let descriptor = match self.cx.describe(&import.shim) {
|
||||
None => return Ok(()),
|
||||
Some(d) => d,
|
||||
};
|
||||
|
||||
let target = match &import.method {
|
||||
Some(shared::MethodData { class, kind }) => {
|
||||
let class = self.import_name(info, class)?;
|
||||
match kind {
|
||||
shared::MethodKind::Constructor => format!("new {}", class),
|
||||
shared::MethodKind::Operation(shared::Operation { is_static, kind }) => {
|
||||
let target = if import.structural {
|
||||
let location = if *is_static { &class } else { "this" };
|
||||
let target = self.generated_import_target(info, import, &descriptor)?;
|
||||
|
||||
match kind {
|
||||
shared::OperationKind::Regular => {
|
||||
let nargs = descriptor.unwrap_function().arguments.len();
|
||||
let mut s = format!("function(");
|
||||
for i in 0..nargs - 1 {
|
||||
if i > 0 {
|
||||
drop(write!(s, ", "));
|
||||
}
|
||||
drop(write!(s, "x{}", i));
|
||||
}
|
||||
s.push_str(") { \nreturn this.");
|
||||
s.push_str(&import.function.name);
|
||||
s.push_str("(");
|
||||
for i in 0..nargs - 1 {
|
||||
if i > 0 {
|
||||
drop(write!(s, ", "));
|
||||
}
|
||||
drop(write!(s, "x{}", i));
|
||||
}
|
||||
s.push_str(");\n}");
|
||||
s
|
||||
}
|
||||
shared::OperationKind::Getter(g) => format!(
|
||||
"function() {{
|
||||
return {}.{};
|
||||
}}",
|
||||
location, g
|
||||
),
|
||||
shared::OperationKind::Setter(s) => format!(
|
||||
"function(y) {{
|
||||
{}.{} = y;
|
||||
}}",
|
||||
location, s
|
||||
),
|
||||
shared::OperationKind::IndexingGetter => format!(
|
||||
"function(y) {{
|
||||
return {}[y];
|
||||
}}",
|
||||
location
|
||||
),
|
||||
shared::OperationKind::IndexingSetter => format!(
|
||||
"function(y, z) {{
|
||||
{}[y] = z;
|
||||
}}",
|
||||
location
|
||||
),
|
||||
shared::OperationKind::IndexingDeleter => format!(
|
||||
"function(y) {{
|
||||
delete {}[y];
|
||||
}}",
|
||||
location
|
||||
),
|
||||
}
|
||||
} else {
|
||||
let (location, binding) = if *is_static {
|
||||
("", format!(".bind({})", class))
|
||||
} else {
|
||||
(".prototype", "".into())
|
||||
};
|
||||
let js = Rust2Js::new(self.cx)
|
||||
.catch(import.catch)
|
||||
.variadic(import.variadic)
|
||||
.process(descriptor.unwrap_function())?
|
||||
.finish(&target);
|
||||
self.cx.export(&import.shim, &js, None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
match kind {
|
||||
shared::OperationKind::Regular => {
|
||||
format!("{}{}.{}{}", class, location, import.function.name, binding)
|
||||
}
|
||||
shared::OperationKind::Getter(g) => {
|
||||
self.cx.expose_get_inherited_descriptor();
|
||||
format!(
|
||||
"GetOwnOrInheritedPropertyDescriptor({}{}, '{}').get{}",
|
||||
class, location, g, binding,
|
||||
)
|
||||
}
|
||||
shared::OperationKind::Setter(s) => {
|
||||
self.cx.expose_get_inherited_descriptor();
|
||||
format!(
|
||||
"GetOwnOrInheritedPropertyDescriptor({}{}, '{}').set{}",
|
||||
class, location, s, binding,
|
||||
)
|
||||
}
|
||||
shared::OperationKind::IndexingGetter => panic!("indexing getter should be structural"),
|
||||
shared::OperationKind::IndexingSetter => panic!("indexing setter should be structural"),
|
||||
shared::OperationKind::IndexingDeleter => panic!("indexing deleter should be structural"),
|
||||
}
|
||||
};
|
||||
|
||||
let fallback = if import.structural {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!(
|
||||
" || function() {{
|
||||
throw new Error(`wasm-bindgen: {} does not exist`);
|
||||
}}",
|
||||
target
|
||||
)
|
||||
};
|
||||
|
||||
self.cx.global(&format!(
|
||||
"
|
||||
const {}_target = {} {} ;
|
||||
",
|
||||
import.shim, target, fallback
|
||||
));
|
||||
format!(
|
||||
"{}_target{}",
|
||||
import.shim,
|
||||
if *is_static { "" } else { ".call" }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn generated_import_target(
|
||||
&mut self,
|
||||
info: &shared::Import,
|
||||
import: &shared::ImportFunction,
|
||||
descriptor: &Descriptor,
|
||||
) -> Result<String, Error> {
|
||||
let method_data = match &import.method {
|
||||
Some(data) => data,
|
||||
None => {
|
||||
let name = self.import_name(info, &import.function.name)?;
|
||||
if name.contains(".") {
|
||||
return Ok(if name.contains(".") {
|
||||
self.cx.global(&format!(
|
||||
"
|
||||
const {}_target = {};
|
||||
@ -1934,17 +1989,146 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
format!("{}_target", import.shim)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let js = Rust2Js::new(self.cx)
|
||||
.catch(import.catch)
|
||||
.variadic(import.variadic)
|
||||
.process(descriptor.unwrap_function())?
|
||||
.finish(&target);
|
||||
self.cx.export(&import.shim, &js, None);
|
||||
Ok(())
|
||||
let class = match &method_data.class {
|
||||
Some(class) => self.import_name(info, class)?,
|
||||
None => {
|
||||
let op = match &method_data.kind {
|
||||
shared::MethodKind::Operation(op) => op,
|
||||
shared::MethodKind::Constructor => {
|
||||
bail!("\"no class\" methods cannot be constructors")
|
||||
}
|
||||
};
|
||||
match &op.kind {
|
||||
shared::OperationKind::Regular => {
|
||||
return Ok(import.function.name.to_string())
|
||||
}
|
||||
shared::OperationKind::Getter(g) => {
|
||||
return Ok(format!("(() => {})", g));
|
||||
}
|
||||
shared::OperationKind::Setter(g) => {
|
||||
return Ok(format!("(v => {} = v)", g));
|
||||
}
|
||||
_ => bail!("\"no class\" methods must be regular/getter/setter"),
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
let op = match &method_data.kind {
|
||||
shared::MethodKind::Constructor => return Ok(format!("new {}", class)),
|
||||
shared::MethodKind::Operation(op) => op,
|
||||
};
|
||||
let target = if import.structural {
|
||||
let location = if op.is_static { &class } else { "this" };
|
||||
|
||||
match &op.kind {
|
||||
shared::OperationKind::Regular => {
|
||||
let nargs = descriptor.unwrap_function().arguments.len();
|
||||
let mut s = format!("function(");
|
||||
for i in 0..nargs - 1 {
|
||||
if i > 0 {
|
||||
drop(write!(s, ", "));
|
||||
}
|
||||
drop(write!(s, "x{}", i));
|
||||
}
|
||||
s.push_str(") { \nreturn this.");
|
||||
s.push_str(&import.function.name);
|
||||
s.push_str("(");
|
||||
for i in 0..nargs - 1 {
|
||||
if i > 0 {
|
||||
drop(write!(s, ", "));
|
||||
}
|
||||
drop(write!(s, "x{}", i));
|
||||
}
|
||||
s.push_str(");\n}");
|
||||
s
|
||||
}
|
||||
shared::OperationKind::Getter(g) => format!(
|
||||
"function() {{
|
||||
return {}.{};
|
||||
}}",
|
||||
location, g
|
||||
),
|
||||
shared::OperationKind::Setter(s) => format!(
|
||||
"function(y) {{
|
||||
{}.{} = y;
|
||||
}}",
|
||||
location, s
|
||||
),
|
||||
shared::OperationKind::IndexingGetter => format!(
|
||||
"function(y) {{
|
||||
return {}[y];
|
||||
}}",
|
||||
location
|
||||
),
|
||||
shared::OperationKind::IndexingSetter => format!(
|
||||
"function(y, z) {{
|
||||
{}[y] = z;
|
||||
}}",
|
||||
location
|
||||
),
|
||||
shared::OperationKind::IndexingDeleter => format!(
|
||||
"function(y) {{
|
||||
delete {}[y];
|
||||
}}",
|
||||
location
|
||||
),
|
||||
}
|
||||
} else {
|
||||
let (location, binding) = if op.is_static {
|
||||
("", format!(".bind({})", class))
|
||||
} else {
|
||||
(".prototype", "".into())
|
||||
};
|
||||
|
||||
match &op.kind {
|
||||
shared::OperationKind::Regular => {
|
||||
format!("{}{}.{}{}", class, location, import.function.name, binding)
|
||||
}
|
||||
shared::OperationKind::Getter(g) => {
|
||||
self.cx.expose_get_inherited_descriptor();
|
||||
format!(
|
||||
"GetOwnOrInheritedPropertyDescriptor({}{}, '{}').get{}",
|
||||
class, location, g, binding,
|
||||
)
|
||||
}
|
||||
shared::OperationKind::Setter(s) => {
|
||||
self.cx.expose_get_inherited_descriptor();
|
||||
format!(
|
||||
"GetOwnOrInheritedPropertyDescriptor({}{}, '{}').set{}",
|
||||
class, location, s, binding,
|
||||
)
|
||||
}
|
||||
shared::OperationKind::IndexingGetter => panic!("indexing getter should be structural"),
|
||||
shared::OperationKind::IndexingSetter => panic!("indexing setter should be structural"),
|
||||
shared::OperationKind::IndexingDeleter => panic!("indexing deleter should be structural"),
|
||||
}
|
||||
};
|
||||
|
||||
let fallback = if import.structural {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!(
|
||||
" || function() {{
|
||||
throw new Error(`wasm-bindgen: {} does not exist`);
|
||||
}}",
|
||||
target
|
||||
)
|
||||
};
|
||||
|
||||
self.cx.global(&format!(
|
||||
"const {}_target = {}{};",
|
||||
import.shim, target, fallback
|
||||
));
|
||||
Ok(format!(
|
||||
"{}_target{}",
|
||||
import.shim,
|
||||
if op.is_static { "" } else { ".call" }
|
||||
))
|
||||
>>>>>>> master
|
||||
}
|
||||
|
||||
fn generate_import_type(
|
||||
|
@ -4,13 +4,13 @@ extern crate parity_wasm;
|
||||
extern crate wasm_bindgen_shared as shared;
|
||||
extern crate serde_json;
|
||||
extern crate wasm_gc;
|
||||
extern crate wasmi;
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
extern crate wasm_bindgen_wasm_interpreter as wasm_interpreter;
|
||||
|
||||
use std::any::Any;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -33,6 +33,9 @@ pub struct Bindgen {
|
||||
typescript: bool,
|
||||
demangle: bool,
|
||||
keep_debug: bool,
|
||||
// Experimental support for `WeakRefGroup`, an upcoming ECMAScript feature.
|
||||
// Currently only enable-able through an env var.
|
||||
weak_refs: bool,
|
||||
}
|
||||
|
||||
enum Input {
|
||||
@ -55,6 +58,7 @@ impl Bindgen {
|
||||
typescript: false,
|
||||
demangle: true,
|
||||
keep_debug: false,
|
||||
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,13 +183,7 @@ impl Bindgen {
|
||||
// This means that whenever we encounter an import or export we'll
|
||||
// execute a shim function which informs us about its type so we can
|
||||
// then generate the appropriate bindings.
|
||||
//
|
||||
// TODO: avoid a `clone` here of the module if we can
|
||||
let instance = wasmi::Module::from_parity_wasm_module(module.clone())
|
||||
.with_context(|_| "failed to create wasmi module")?;
|
||||
let instance = wasmi::ModuleInstance::new(&instance, &MyResolver)
|
||||
.with_context(|_| "failed to instantiate wasm module")?;
|
||||
let instance = instance.not_started_instance();
|
||||
let mut instance = wasm_interpreter::Interpreter::new(&module);
|
||||
|
||||
let (js, ts) = {
|
||||
let mut cx = js::Context {
|
||||
@ -201,20 +199,10 @@ impl Bindgen {
|
||||
config: &self,
|
||||
module: &mut module,
|
||||
function_table_needed: false,
|
||||
run_descriptor: &|name| {
|
||||
let mut v = MyExternals(Vec::new());
|
||||
match instance.invoke_export(name, &[], &mut v) {
|
||||
Ok(None) => Some(v.0),
|
||||
Ok(Some(_)) => unreachable!(
|
||||
"there is only one export, and we only return None from it"
|
||||
),
|
||||
// Allow missing exported describe functions. This can
|
||||
// happen when a nested dependency crate exports things
|
||||
// but the root crate doesn't use them.
|
||||
Err(wasmi::Error::Function(_)) => None,
|
||||
Err(e) => panic!("unexpected error running descriptor: {}", e),
|
||||
}
|
||||
},
|
||||
interpreter: &mut instance,
|
||||
memory_init: None,
|
||||
imported_functions: Default::default(),
|
||||
imported_statics: Default::default(),
|
||||
};
|
||||
for program in programs.iter() {
|
||||
js::SubContext {
|
||||
@ -336,7 +324,6 @@ fn extract_programs(module: &mut Module) -> Result<Vec<shared::Program>, Error>
|
||||
to_remove.push(i);
|
||||
|
||||
let mut payload = custom.payload();
|
||||
let mut added_programs = Vec::new();
|
||||
while payload.len() > 0 {
|
||||
let len = ((payload[0] as usize) << 0)
|
||||
| ((payload[1] as usize) << 8)
|
||||
@ -345,19 +332,6 @@ fn extract_programs(module: &mut Module) -> Result<Vec<shared::Program>, Error>
|
||||
let (a, b) = payload[4..].split_at(len as usize);
|
||||
payload = b;
|
||||
|
||||
// Due to a nasty LLVM bug it's currently possible for LLVM to
|
||||
// duplicate custom section directives in intermediate object files.
|
||||
// This means that we could see multiple program directives when in
|
||||
// fact we were originally only meant to see one!
|
||||
//
|
||||
// Work around the issue here until the upstream bug,
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=38184, is hopefully fixed
|
||||
// via some other means.
|
||||
if added_programs.iter().any(|p| a == *p) {
|
||||
continue
|
||||
}
|
||||
added_programs.push(a);
|
||||
|
||||
let p: shared::ProgramOnlySchema = match serde_json::from_slice(&a) {
|
||||
Ok(f) => f,
|
||||
Err(e) => bail!("failed to decode what looked like wasm-bindgen data: {}", e),
|
||||
@ -404,106 +378,6 @@ to open an issue at https://github.com/rustwasm/wasm-bindgen/issues!
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
struct MyResolver;
|
||||
|
||||
impl wasmi::ImportResolver for MyResolver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
signature: &wasmi::Signature,
|
||||
) -> Result<wasmi::FuncRef, wasmi::Error> {
|
||||
// Route our special "describe" export to 1 and everything else to 0.
|
||||
// That way whenever the function 1 is invoked we know what to do and
|
||||
// when 0 is invoked (by accident) we'll trap and produce an error.
|
||||
let idx = (module_name == "__wbindgen_placeholder__" && field_name == "__wbindgen_describe")
|
||||
as usize;
|
||||
Ok(wasmi::FuncInstance::alloc_host(signature.clone(), idx))
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
&self,
|
||||
_module_name: &str,
|
||||
_field_name: &str,
|
||||
descriptor: &wasmi::GlobalDescriptor,
|
||||
) -> Result<wasmi::GlobalRef, wasmi::Error> {
|
||||
// dummy implementation to ensure instantiation succeeds
|
||||
let val = match descriptor.value_type() {
|
||||
wasmi::ValueType::I32 => wasmi::RuntimeValue::I32(0),
|
||||
wasmi::ValueType::I64 => wasmi::RuntimeValue::I64(0),
|
||||
wasmi::ValueType::F32 => wasmi::RuntimeValue::F32(0.0.into()),
|
||||
wasmi::ValueType::F64 => wasmi::RuntimeValue::F64(0.0.into()),
|
||||
};
|
||||
Ok(wasmi::GlobalInstance::alloc(val, descriptor.is_mutable()))
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
_module_name: &str,
|
||||
_field_name: &str,
|
||||
descriptor: &wasmi::MemoryDescriptor,
|
||||
) -> Result<wasmi::MemoryRef, wasmi::Error> {
|
||||
// dummy implementation to ensure instantiation succeeds
|
||||
use wasmi::memory_units::Pages;
|
||||
let initial = Pages(descriptor.initial() as usize);
|
||||
let maximum = descriptor.maximum().map(|i| Pages(i as usize));
|
||||
wasmi::MemoryInstance::alloc(initial, maximum)
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
&self,
|
||||
_module_name: &str,
|
||||
_field_name: &str,
|
||||
descriptor: &wasmi::TableDescriptor,
|
||||
) -> Result<wasmi::TableRef, wasmi::Error> {
|
||||
// dummy implementation to ensure instantiation succeeds
|
||||
let initial = descriptor.initial();
|
||||
let maximum = descriptor.maximum();
|
||||
wasmi::TableInstance::alloc(initial, maximum)
|
||||
}
|
||||
}
|
||||
|
||||
struct MyExternals(Vec<u32>);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MyError(String);
|
||||
|
||||
impl wasmi::Externals for MyExternals {
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: wasmi::RuntimeArgs,
|
||||
) -> Result<Option<wasmi::RuntimeValue>, wasmi::Trap> {
|
||||
macro_rules! bail {
|
||||
($($t:tt)*) => ({
|
||||
let s = MyError(format!($($t)*));
|
||||
return Err(wasmi::Trap::new(wasmi::TrapKind::Host(Box::new(s))))
|
||||
})
|
||||
}
|
||||
// We only recognize one function here which was mapped to the index 1
|
||||
// by the resolver above.
|
||||
if index != 1 {
|
||||
bail!("only __wbindgen_describe can be run at this time")
|
||||
}
|
||||
if args.len() != 1 {
|
||||
bail!("must have exactly one argument");
|
||||
}
|
||||
match args.nth_value_checked(0)? {
|
||||
wasmi::RuntimeValue::I32(i) => self.0.push(i as u32),
|
||||
_ => bail!("expected one argument of i32 type"),
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl wasmi::HostError for MyError {}
|
||||
|
||||
impl fmt::Display for MyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_indentation(s: &str) -> String {
|
||||
let mut indent: u32 = 0;
|
||||
let mut dst = String::new();
|
||||
|
@ -150,19 +150,6 @@ impl Output {
|
||||
if let Some(i) = self.module.import_section() {
|
||||
let mut set = HashSet::new();
|
||||
for entry in i.entries() {
|
||||
match *entry.external() {
|
||||
External::Function(_) => {}
|
||||
External::Table(_) => {
|
||||
bail!("wasm imports a table which isn't supported yet");
|
||||
}
|
||||
External::Memory(_) => {
|
||||
bail!("wasm imports memory which isn't supported yet");
|
||||
}
|
||||
External::Global(_) => {
|
||||
bail!("wasm imports globals which aren't supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
if !set.insert(entry.module()) {
|
||||
continue;
|
||||
}
|
||||
|
Reference in New Issue
Block a user