mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-11-24 06:33:33 +03:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
14eb317509
@ -25,8 +25,11 @@ test_script:
|
||||
- where chromedriver
|
||||
- set CHROMEDRIVER=C:\Tools\WebDriver\chromedriver.exe
|
||||
- cargo test -p js-sys --target wasm32-unknown-unknown
|
||||
- cargo test --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --all-features
|
||||
- cargo test -p webidl-tests --target wasm32-unknown-unknown
|
||||
# Try just a few features for `web-sys`, unfortunately the whole crate blows
|
||||
# system command line limits meaning we can't even spawn rustc to enable all
|
||||
# the features.
|
||||
- cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features "Node Window Document"
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
46
CHANGELOG.md
46
CHANGELOG.md
@ -32,6 +32,52 @@ Released YYYY-MM-DD.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 0.2.21
|
||||
|
||||
Released 2018-09-07
|
||||
|
||||
### Added
|
||||
|
||||
* Added many more bindings for `WebAssembly` in the `js-sys` crate.
|
||||
|
||||
### Fixed
|
||||
|
||||
* The "names" section of the wasm binary is now correctly preserved by
|
||||
wasm-bindgen.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 0.2.20
|
||||
|
||||
Released 2018-09-06
|
||||
|
||||
### Added
|
||||
|
||||
* All of `wasm-bindgen` is configured to compile on stable Rust as of the
|
||||
upcoming 1.30.0 release, scheduled for October 25, 2018.
|
||||
* The underlying `JsValue` of a `Closure<T>` type can now be extracted at any
|
||||
time.
|
||||
* Initial and experimental support was added for modules that have shared memory
|
||||
(use atomic instructions).
|
||||
|
||||
### Removed
|
||||
|
||||
* The `--wasm2asm` flag of `wasm2es6js` was removed because the `wasm2asm` tool
|
||||
has been removed from upstream Binaryen. This is replaced with the new
|
||||
`wasm2js` tool from Binaryen.
|
||||
|
||||
### Fixed
|
||||
|
||||
* The "schema" version for wasm-bindgen now changes on all publishes, meaning we
|
||||
can't forget to update it. This means that the crate version and CLI version
|
||||
must exactly match.
|
||||
* The `wasm-bindgen` crate now has a `links` key which forbids multiple versions
|
||||
of `wasm-bindgen` from being linked into a dependency graph, fixing obscure
|
||||
linking errors with a more first-class error message.
|
||||
* Binary releases for Windows has been fixed.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 0.2.19 (and 0.2.18)
|
||||
|
||||
Released 2018-08-27.
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
# Because only a single `wasm_bindgen` version can be used in a dependency
|
||||
@ -35,13 +35,13 @@ nightly = []
|
||||
xxx_debug_only_print_generated_code = ["wasm-bindgen-macro/xxx_debug_only_print_generated_code"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.19" }
|
||||
wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.21" }
|
||||
serde = { version = "1.0", optional = true }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
js-sys = { path = 'crates/js-sys', version = '0.2.4' }
|
||||
wasm-bindgen-test = { path = 'crates/test', version = '=0.2.19' }
|
||||
js-sys = { path = 'crates/js-sys', version = '0.2.6' }
|
||||
wasm-bindgen-test = { path = 'crates/test', version = '=0.2.21' }
|
||||
serde_derive = "1.0"
|
||||
wasm-bindgen-test-crate-a = { path = 'tests/crates/a', version = '0.1' }
|
||||
wasm-bindgen-test-crate-b = { path = 'tests/crates/b', version = '0.1' }
|
||||
|
4
build.rs
4
build.rs
@ -1,2 +1,4 @@
|
||||
// Empty `build.rs` so that `[package] links = ...` works in `Cargo.toml`.
|
||||
fn main() {}
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend"
|
||||
@ -20,5 +20,5 @@ log = "0.4"
|
||||
proc-macro2 = "0.4.8"
|
||||
quote = '0.6'
|
||||
serde_json = "1.0"
|
||||
syn = { version = '0.14', features = ['full', 'visit'] }
|
||||
wasm-bindgen-shared = { path = "../shared", version = "=0.2.19" }
|
||||
syn = { version = '0.15', features = ['full', 'visit'] }
|
||||
wasm-bindgen-shared = { path = "../shared", version = "=0.2.21" }
|
||||
|
@ -233,6 +233,7 @@ impl ImportedTypes for syn::GenericArgument {
|
||||
syn::GenericArgument::Type(ty) => ty.imported_types(f),
|
||||
syn::GenericArgument::Binding(_) => {}, // TODO
|
||||
syn::GenericArgument::Const(_) => {}, // TODO
|
||||
syn::GenericArgument::Constraint(_) => {}, // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use proc_macro2::*;
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::parse::Error;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! err_span {
|
||||
@ -26,6 +27,7 @@ enum Repr {
|
||||
text: String,
|
||||
span: Option<(Span, Span)>,
|
||||
},
|
||||
SynError(Error),
|
||||
Multi {
|
||||
diagnostics: Vec<Diagnostic>,
|
||||
}
|
||||
@ -62,11 +64,20 @@ impl Diagnostic {
|
||||
pub fn panic(&self) -> ! {
|
||||
match &self.inner {
|
||||
Repr::Single { text, .. } => panic!("{}", text),
|
||||
Repr::SynError(error) => panic!("{}", error),
|
||||
Repr::Multi { diagnostics } => diagnostics[0].panic(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for Diagnostic {
|
||||
fn from(err: Error) -> Diagnostic {
|
||||
Diagnostic {
|
||||
inner: Repr::SynError(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_spans(node: &ToTokens) -> Option<(Span, Span)> {
|
||||
let mut t = TokenStream::new();
|
||||
node.to_tokens(&mut t);
|
||||
@ -95,6 +106,9 @@ impl ToTokens for Diagnostic {
|
||||
diagnostic.to_tokens(dst);
|
||||
}
|
||||
}
|
||||
Repr::SynError(err) => {
|
||||
err.to_compile_error().to_tokens(dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-cli-support"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/cli-support"
|
||||
@ -17,6 +17,6 @@ parity-wasm = "0.32"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
tempfile = "3.0"
|
||||
wasm-bindgen-shared = { path = "../shared", version = '=0.2.19' }
|
||||
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.19' }
|
||||
wasm-bindgen-shared = { path = "../shared", version = '=0.2.21' }
|
||||
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.21' }
|
||||
wasm-gc-api = "0.1.9"
|
||||
|
@ -189,13 +189,6 @@ impl Descriptor {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ref_closure(&self) -> Option<&Closure> {
|
||||
match *self {
|
||||
Descriptor::Ref(ref s) => s.closure(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn closure(&self) -> Option<&Closure> {
|
||||
match *self {
|
||||
Descriptor::Closure(ref s) => Some(s),
|
||||
|
386
crates/cli-support/src/js/closures.rs
Normal file
386
crates/cli-support/src/js/closures.rs
Normal file
@ -0,0 +1,386 @@
|
||||
//! Support for closures in wasm-bindgen
|
||||
//!
|
||||
//! This module contains the bulk of the support necessary to support closures
|
||||
//! in `wasm-bindgen`. The main "support" here is that `Closure::wrap` creates
|
||||
//! a `JsValue` through... well... unconventional mechanisms.
|
||||
//!
|
||||
//! This module contains one public function, `rewrite`. The function will
|
||||
//! rewrite the wasm module to correctly call closure factories and thread
|
||||
//! through values into the final `Closure` object. More details about how all
|
||||
//! this works can be found in the code below.
|
||||
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::mem;
|
||||
|
||||
use failure::Error;
|
||||
use parity_wasm::elements::*;
|
||||
|
||||
use descriptor::Descriptor;
|
||||
use js::Context;
|
||||
use js::js2rust::Js2Rust;
|
||||
|
||||
pub fn rewrite(input: &mut Context) -> Result<(), Error> {
|
||||
let info = ClosureDescriptors::new(input);
|
||||
|
||||
// Sanity check to make sure things look ok and skip everything below if
|
||||
// there's not calls to `Closure::new`.
|
||||
assert_eq!(
|
||||
info.element_removal_list.len(),
|
||||
info.code_idx_to_descriptor.len(),
|
||||
);
|
||||
if info.element_removal_list.len() == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// Make sure the names section is available in the wasm module because we'll
|
||||
// want to remap those function indices, and then actually remap all
|
||||
// function indices. We're going to be injecting a few imported functions
|
||||
// below which will shift the index space for all defined functions.
|
||||
input.parse_wasm_names();
|
||||
Remap {
|
||||
code_idx_to_descriptor: &info.code_idx_to_descriptor,
|
||||
old_num_imports: input.module
|
||||
.import_section()
|
||||
.map(|s| s.functions())
|
||||
.unwrap_or(0) as u32,
|
||||
}.remap_module(input.module);
|
||||
|
||||
info.delete_function_table_entries(input);
|
||||
info.inject_imports(input)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ClosureDescriptors {
|
||||
/// A list of elements to remove from the function table. The first element
|
||||
/// of the pair is the index of the entry in the element section, and the
|
||||
/// second element of the pair is the index within that entry to remove.
|
||||
element_removal_list: Vec<(usize, usize)>,
|
||||
|
||||
/// A map from indexes in the code section which contain calls to
|
||||
/// `__wbindgen_describe_closure` to the new function the whole function is
|
||||
/// replaced with as well as the descriptor that the function describes.
|
||||
///
|
||||
/// This map is later used to replace all calls to the keys of this map with
|
||||
/// calls to the value of the map.
|
||||
code_idx_to_descriptor: BTreeMap<u32, (u32, Descriptor)>,
|
||||
}
|
||||
|
||||
impl ClosureDescriptors {
|
||||
/// Find all invocations of `__wbindgen_describe_closure`.
|
||||
///
|
||||
/// We'll be rewriting all calls to functions who call this import. Here we
|
||||
/// iterate over all code found in the module, and anything which calls our
|
||||
/// special imported function is interpreted. The result of interpretation will
|
||||
/// inform of us of an entry to remove from the function table (as the describe
|
||||
/// function is never needed at runtime) as well as a `Descriptor` which
|
||||
/// describes the type of closure needed.
|
||||
///
|
||||
/// All this information is then returned in the `ClosureDescriptors` return
|
||||
/// value.
|
||||
fn new(input: &mut Context) -> ClosureDescriptors {
|
||||
let wbindgen_describe_closure = match input.interpreter.describe_closure_idx() {
|
||||
Some(i) => i,
|
||||
None => return Default::default(),
|
||||
};
|
||||
let imports = input.module.import_section()
|
||||
.map(|s| s.functions())
|
||||
.unwrap_or(0);
|
||||
let mut ret = ClosureDescriptors::default();
|
||||
|
||||
let code = match input.module.code_section() {
|
||||
Some(code) => code,
|
||||
None => return Default::default(),
|
||||
};
|
||||
for (i, function) in code.bodies().iter().enumerate() {
|
||||
let mut call_found = false;
|
||||
for instruction in function.code().elements() {
|
||||
match instruction {
|
||||
Instruction::Call(idx) if *idx == wbindgen_describe_closure => {
|
||||
call_found = true;
|
||||
break
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if !call_found {
|
||||
continue
|
||||
}
|
||||
let descriptor = input.interpreter.interpret_closure_descriptor(
|
||||
i,
|
||||
input.module,
|
||||
&mut ret.element_removal_list,
|
||||
).unwrap();
|
||||
// `new_idx` is the function-space index of the function that we'll
|
||||
// be injecting. Calls to the code function `i` will instead be
|
||||
// rewritten to calls to `new_idx`, which is an import that we'll
|
||||
// inject based on `descriptor`.
|
||||
let new_idx = (ret.code_idx_to_descriptor.len() + imports) as u32;
|
||||
ret.code_idx_to_descriptor.insert(
|
||||
i as u32,
|
||||
(new_idx, Descriptor::decode(descriptor)),
|
||||
);
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
/// Here we remove elements from the function table. All our descriptor
|
||||
/// functions are entries in this function table and can be removed once we
|
||||
/// use them as they're not actually needed at runtime.
|
||||
///
|
||||
/// One option for removal is to replace the function table entry with an
|
||||
/// index to a dummy function, but for now we simply remove the table entry
|
||||
/// altogether by splitting the section and having multiple `elem` sections
|
||||
/// with holes in them.
|
||||
fn delete_function_table_entries(&self, input: &mut Context) {
|
||||
let elements = input.module.elements_section_mut().unwrap();
|
||||
let mut remove = HashMap::new();
|
||||
for (entry, idx) in self.element_removal_list.iter().cloned() {
|
||||
remove.entry(entry).or_insert(HashSet::new()).insert(idx);
|
||||
}
|
||||
|
||||
let entries = mem::replace(elements.entries_mut(), Vec::new());
|
||||
let empty = HashSet::new();
|
||||
for (i, entry) in entries.into_iter().enumerate() {
|
||||
let to_remove = remove.get(&i).unwrap_or(&empty);
|
||||
|
||||
let mut current = Vec::new();
|
||||
assert_eq!(entry.offset().code().len(), 2);
|
||||
let mut offset = match entry.offset().code()[0] {
|
||||
Instruction::I32Const(x) => x,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
for (j, idx) in entry.members().iter().enumerate() {
|
||||
// If we keep this entry, then keep going
|
||||
if !to_remove.contains(&j) {
|
||||
current.push(*idx);
|
||||
continue
|
||||
}
|
||||
|
||||
// If we have members of `current` then we save off a section
|
||||
// of the function table, then update `offset` and keep going.
|
||||
let next_offset = offset + (current.len() as i32) + 1;
|
||||
if current.len() > 0 {
|
||||
let members = mem::replace(&mut current, Vec::new());
|
||||
let offset = InitExpr::new(vec![
|
||||
Instruction::I32Const(offset),
|
||||
Instruction::End,
|
||||
]);
|
||||
let new_entry = ElementSegment::new(0, offset, members);
|
||||
elements.entries_mut().push(new_entry);
|
||||
}
|
||||
offset = next_offset;
|
||||
}
|
||||
// Any remaining function table entries get pushed at the end.
|
||||
if current.len() > 0 {
|
||||
let offset = InitExpr::new(vec![
|
||||
Instruction::I32Const(offset),
|
||||
Instruction::End,
|
||||
]);
|
||||
let new_entry = ElementSegment::new(0, offset, current);
|
||||
elements.entries_mut().push(new_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inject new imports into the module.
|
||||
///
|
||||
/// This function will inject new imported functions into the `input` module
|
||||
/// described by the fields internally. These new imports will be closure
|
||||
/// factories and are freshly generated shim in JS.
|
||||
fn inject_imports(&self, input: &mut Context) -> Result<(), Error> {
|
||||
// We'll be injecting new imports and we'll need to give them all a
|
||||
// type. The signature is all `(i32, i32) -> i32` currently and we know
|
||||
// that this signature already exists in the module as it's the
|
||||
// signature of our `#[inline(never)]` functions. Find the type
|
||||
// signature index so we can assign it below.
|
||||
let type_idx = input.module.type_section()
|
||||
.unwrap()
|
||||
.types()
|
||||
.iter()
|
||||
.position(|ty| {
|
||||
let fnty = match ty {
|
||||
Type::Function(f) => f,
|
||||
};
|
||||
fnty.params() == &[ValueType::I32, ValueType::I32] &&
|
||||
fnty.return_type() == Some(ValueType::I32)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// The last piece of the magic. For all our descriptors we found we
|
||||
// inject a JS shim for the descriptor. This JS shim will manufacture a
|
||||
// JS `function`, and prepare it to be invoked.
|
||||
//
|
||||
// Once all that's said and done we inject a new import into the wasm module
|
||||
// of our new wrapper, and the `Remap` step above already wrote calls to
|
||||
// this function within the module.
|
||||
for (i, (_new_idx, descriptor)) in self.code_idx_to_descriptor.iter() {
|
||||
let import_name = format!("__wbindgen_closure_wrapper{}", i);
|
||||
|
||||
let closure = descriptor.closure().unwrap();
|
||||
|
||||
let (js, _ts, _js_doc) = {
|
||||
let mut builder = Js2Rust::new("", input);
|
||||
if closure.mutable {
|
||||
builder
|
||||
.prelude("let a = this.a;\n")
|
||||
.prelude("this.a = 0;\n")
|
||||
.rust_argument("a")
|
||||
.finally("this.a = a;\n");
|
||||
} else {
|
||||
builder.rust_argument("this.a");
|
||||
}
|
||||
builder
|
||||
.process(&closure.function)?
|
||||
.finish("function", "this.f")
|
||||
};
|
||||
input.expose_add_heap_object();
|
||||
input.function_table_needed = true;
|
||||
let body = format!(
|
||||
"function(ptr, f) {{
|
||||
let cb = {};
|
||||
cb.f = wasm.__wbg_function_table.get(f);
|
||||
cb.a = ptr;
|
||||
let real = cb.bind(cb);
|
||||
real.original = cb;
|
||||
return addHeapObject(real);
|
||||
}}",
|
||||
js,
|
||||
);
|
||||
input.export(&import_name, &body, None);
|
||||
|
||||
let new_import = ImportEntry::new(
|
||||
"__wbindgen_placeholder__".to_string(),
|
||||
import_name,
|
||||
External::Function(type_idx as u32),
|
||||
);
|
||||
input.module.import_section_mut()
|
||||
.unwrap()
|
||||
.entries_mut()
|
||||
.push(new_import);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Remap<'a> {
|
||||
code_idx_to_descriptor: &'a BTreeMap<u32, (u32, Descriptor)>,
|
||||
old_num_imports: u32,
|
||||
}
|
||||
|
||||
impl<'a> Remap<'a> {
|
||||
fn remap_module(&self, module: &mut Module) {
|
||||
for section in module.sections_mut() {
|
||||
match section {
|
||||
Section::Export(e) => self.remap_export_section(e),
|
||||
Section::Element(e) => self.remap_element_section(e),
|
||||
Section::Code(e) => self.remap_code_section(e),
|
||||
Section::Start(i) => { self.remap_idx(i); }
|
||||
Section::Name(n) => self.remap_name_section(n),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_export_section(&self, section: &mut ExportSection) {
|
||||
for entry in section.entries_mut() {
|
||||
self.remap_export_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_export_entry(&self, entry: &mut ExportEntry) {
|
||||
match entry.internal_mut() {
|
||||
Internal::Function(i) => { self.remap_idx(i); }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn remap_element_section(&self, section: &mut ElementSection) {
|
||||
for entry in section.entries_mut() {
|
||||
self.remap_element_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_element_entry(&self, entry: &mut ElementSegment) {
|
||||
for member in entry.members_mut() {
|
||||
self.remap_idx(member);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_code_section(&self, section: &mut CodeSection) {
|
||||
for body in section.bodies_mut() {
|
||||
self.remap_func_body(body);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_func_body(&self, body: &mut FuncBody) {
|
||||
self.remap_instructions(body.code_mut());
|
||||
}
|
||||
|
||||
fn remap_instructions(&self, code: &mut Instructions) {
|
||||
for instr in code.elements_mut() {
|
||||
self.remap_instruction(instr);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_instruction(&self, instr: &mut Instruction) {
|
||||
match instr {
|
||||
Instruction::Call(i) => { self.remap_idx(i); }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_name_section(&self, names: &mut NameSection) {
|
||||
match names {
|
||||
NameSection::Function(f) => self.remap_function_name_section(f),
|
||||
NameSection::Local(f) => self.remap_local_name_section(f),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_function_name_section(&self, names: &mut FunctionNameSection) {
|
||||
let map = names.names_mut();
|
||||
let new = IndexMap::with_capacity(map.len());
|
||||
for (mut idx, name) in mem::replace(map, new) {
|
||||
if !self.remap_idx(&mut idx) {
|
||||
map.insert(idx, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_local_name_section(&self, names: &mut LocalNameSection) {
|
||||
let map = names.local_names_mut();
|
||||
let new = IndexMap::with_capacity(map.len());
|
||||
for (mut idx, name) in mem::replace(map, new) {
|
||||
if !self.remap_idx(&mut idx) {
|
||||
map.insert(idx, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether `idx` pointed to a previously known descriptor function
|
||||
/// that we're switching to an import
|
||||
fn remap_idx(&self, idx: &mut u32) -> bool {
|
||||
// If this was an imported function we didn't reorder those, so nothing
|
||||
// to do.
|
||||
if *idx < self.old_num_imports {
|
||||
return false
|
||||
}
|
||||
let code_idx = *idx - self.old_num_imports;
|
||||
|
||||
// If this `idx` points to a function which was effectively a descriptor
|
||||
// function, then we want to re-point it to our imported function which
|
||||
// is actually the shim factory.
|
||||
if let Some((new_idx, _)) = self.code_idx_to_descriptor.get(&code_idx) {
|
||||
*idx = *new_idx;
|
||||
return true
|
||||
}
|
||||
|
||||
// And finally, otherwise this is just a normal function reference we
|
||||
// don't want to touch, but we're injecting imports which shifts all
|
||||
// function indices.
|
||||
*idx += self.code_idx_to_descriptor.len() as u32;
|
||||
false
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ mod js2rust;
|
||||
use self::js2rust::Js2Rust;
|
||||
mod rust2js;
|
||||
use self::rust2js::Rust2Js;
|
||||
mod closures;
|
||||
|
||||
pub struct Context<'a> {
|
||||
pub globals: String,
|
||||
@ -394,6 +395,7 @@ impl<'a> Context<'a> {
|
||||
|
||||
self.create_memory_export();
|
||||
self.unexport_unused_internal_exports();
|
||||
closures::rewrite(self)?;
|
||||
self.gc()?;
|
||||
|
||||
// Note that it's important `throw` comes last *after* we gc. The
|
||||
@ -1714,8 +1716,8 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
|
||||
fn gc(&mut self) -> Result<(), Error> {
|
||||
self.parse_wasm_names();
|
||||
let module = mem::replace(self.module, Module::default());
|
||||
let module = module.parse_names().unwrap_or_else(|p| p.1);
|
||||
let result = wasm_gc::Config::new()
|
||||
.demangle(self.config.demangle)
|
||||
.keep_debug(self.config.keep_debug || self.config.debug)
|
||||
@ -1727,9 +1729,16 @@ impl<'a> Context<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_wasm_names(&mut self) {
|
||||
let module = mem::replace(self.module, Module::default());
|
||||
let module = module.parse_names().unwrap_or_else(|p| p.1);
|
||||
*self.module = module;
|
||||
}
|
||||
|
||||
fn describe(&mut self, name: &str) -> Option<Descriptor> {
|
||||
let name = format!("__wbindgen_describe_{}", name);
|
||||
Some(Descriptor::decode(self.interpreter.interpret(&name, self.module)?))
|
||||
let descriptor = self.interpreter.interpret_descriptor(&name, self.module)?;
|
||||
Some(Descriptor::decode(descriptor))
|
||||
}
|
||||
|
||||
fn global(&mut self, s: &str) {
|
||||
|
@ -275,54 +275,6 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(closure) = arg.ref_closure() {
|
||||
let (js, _ts, _js_doc) = {
|
||||
let mut builder = Js2Rust::new("", self.cx);
|
||||
if closure.mutable {
|
||||
builder
|
||||
.prelude("let a = this.a;\n")
|
||||
.prelude("this.a = 0;\n")
|
||||
.rust_argument("a")
|
||||
.finally("this.a = a;\n");
|
||||
} else {
|
||||
builder.rust_argument("this.a");
|
||||
}
|
||||
builder
|
||||
.process(&closure.function)?
|
||||
.finish("function", "this.f")
|
||||
};
|
||||
self.cx.expose_get_global_argument()?;
|
||||
self.cx.expose_uint32_memory();
|
||||
self.cx.expose_add_heap_object();
|
||||
self.cx.function_table_needed = true;
|
||||
let reset_idx = format!(
|
||||
"\
|
||||
let cb{0} = {js};\n\
|
||||
cb{0}.f = wasm.__wbg_function_table.get(getGlobalArgument({f}));\n\
|
||||
cb{0}.a = getGlobalArgument({a});\n\
|
||||
let real = cb{0}.bind(cb{0});\n\
|
||||
real.original = cb{0};\n\
|
||||
idx{0} = getUint32Memory()[{0} / 4] = addHeapObject(real);\n\
|
||||
",
|
||||
abi,
|
||||
js = js,
|
||||
f = self.global_idx(),
|
||||
a = self.global_idx(),
|
||||
);
|
||||
self.prelude(&format!(
|
||||
"\
|
||||
let idx{0} = getUint32Memory()[{0} / 4];\n\
|
||||
if (idx{0} === 0xffffffff) {{\n\
|
||||
{1}\
|
||||
}}\n\
|
||||
",
|
||||
abi, &reset_idx
|
||||
));
|
||||
self.cx.expose_get_object();
|
||||
self.js_arguments.push(format!("getObject(idx{})", abi));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let invoc_arg = match *arg {
|
||||
ref d if d.is_number() => abi,
|
||||
Descriptor::Boolean => format!("{} !== 0", abi),
|
||||
|
@ -1,12 +1,9 @@
|
||||
extern crate base64;
|
||||
extern crate tempfile;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::process::Command;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use failure::{Error, ResultExt};
|
||||
use failure::Error;
|
||||
use parity_wasm::elements::*;
|
||||
|
||||
pub struct Config {
|
||||
@ -221,35 +218,3 @@ impl Output {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {
|
||||
let output = cmd.output().with_context(|e| {
|
||||
if e.kind() == io::ErrorKind::NotFound {
|
||||
format!(
|
||||
"failed to execute `{}`, is the tool installed \
|
||||
from the binaryen project?\ncommand line: {:?}",
|
||||
program, cmd
|
||||
)
|
||||
} else {
|
||||
format!("failed to execute: {:?}", cmd)
|
||||
}
|
||||
})?;
|
||||
if output.status.success() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut s = format!("failed to execute: {:?}\nstatus: {}\n", cmd, output.status);
|
||||
if !output.stdout.is_empty() {
|
||||
s.push_str(&format!(
|
||||
"----- stdout ------\n{}\n",
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
));
|
||||
}
|
||||
if !output.stderr.is_empty() {
|
||||
s.push_str(&format!(
|
||||
"----- stderr ------\n{}\n",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
));
|
||||
}
|
||||
bail!("{}", s)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-cli"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/cli"
|
||||
@ -23,8 +23,8 @@ rouille = { version = "2.1.0", default-features = false }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.19" }
|
||||
wasm-bindgen-shared = { path = "../shared", version = "=0.2.19" }
|
||||
wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.21" }
|
||||
wasm-bindgen-shared = { path = "../shared", version = "=0.2.21" }
|
||||
openssl = { version = '0.10.11', optional = true }
|
||||
|
||||
[features]
|
||||
|
@ -7,12 +7,12 @@ license = "MIT/Apache-2.0"
|
||||
name = "wasm-bindgen-futures"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures"
|
||||
readme = "./README.md"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.20"
|
||||
js-sys = { path = "../js-sys", version = '0.2.4' }
|
||||
wasm-bindgen = { path = "../..", version = '0.2.19' }
|
||||
js-sys = { path = "../js-sys", version = '0.2.6' }
|
||||
wasm-bindgen = { path = "../..", version = '0.2.21' }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
wasm-bindgen-test = { path = '../test', version = '0.2.19' }
|
||||
wasm-bindgen-test = { path = '../test', version = '0.2.21' }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "js-sys"
|
||||
version = "0.2.4"
|
||||
version = "0.2.6"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
readme = "./README.md"
|
||||
categories = ["wasm"]
|
||||
@ -18,9 +18,9 @@ test = false
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = { path = "../..", version = "0.2.19" }
|
||||
wasm-bindgen = { path = "../..", version = "0.2.21" }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
futures = "0.1.20"
|
||||
wasm-bindgen-test = { path = '../test', version = '=0.2.19' }
|
||||
wasm-bindgen-futures = { path = '../futures', version = '=0.2.19' }
|
||||
wasm-bindgen-test = { path = '../test', version = '=0.2.21' }
|
||||
wasm-bindgen-futures = { path = '../futures', version = '=0.2.21' }
|
||||
|
@ -1203,6 +1203,7 @@ extern {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[derive(Clone, Debug)]
|
||||
#[wasm_bindgen(extends = Object)]
|
||||
pub type Math;
|
||||
|
||||
/// The Math.abs() function returns the absolute value of a number, that is
|
||||
@ -2127,6 +2128,7 @@ extern {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[derive(Clone, Debug)]
|
||||
#[wasm_bindgen(extends = Object)]
|
||||
pub type Reflect;
|
||||
|
||||
/// The static `Reflect.apply()` method calls a target function with
|
||||
@ -2864,6 +2866,29 @@ pub mod WebAssembly {
|
||||
#[wasm_bindgen(js_namespace = WebAssembly)]
|
||||
pub fn compile(buffer_source: &JsValue) -> Promise;
|
||||
|
||||
/// The `WebAssembly.instantiate()` function allows you to compile and
|
||||
/// instantiate WebAssembly code.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate)
|
||||
#[wasm_bindgen(js_namespace = WebAssembly, js_name = instantiate)]
|
||||
pub fn instantiate_buffer(buffer: &[u8], imports: &Object) -> Promise;
|
||||
|
||||
/// The `WebAssembly.instantiate()` function allows you to compile and
|
||||
/// instantiate WebAssembly code.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate)
|
||||
#[wasm_bindgen(js_namespace = WebAssembly, js_name = instantiate)]
|
||||
pub fn instantiate_module(module: &Module, imports: &Object) -> Promise;
|
||||
|
||||
/// The `WebAssembly.instantiateStreaming()` function compiles and
|
||||
/// instantiates a WebAssembly module directly from a streamed
|
||||
/// underlying source. This is the most efficient, optimized way to load
|
||||
/// wasm code.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming)
|
||||
#[wasm_bindgen(js_namespace = WebAssembly, js_name = instantiateStreaming)]
|
||||
pub fn instantiate_streaming(response: &Promise, imports: &Object) -> Promise;
|
||||
|
||||
/// The `WebAssembly.validate()` function validates a given typed
|
||||
/// array of WebAssembly binary code, returning whether the bytes
|
||||
/// form a valid wasm module (`true`) or not (`false`).
|
||||
@ -2894,6 +2919,38 @@ pub mod WebAssembly {
|
||||
pub fn new(message: &str) -> CompileError;
|
||||
}
|
||||
|
||||
// WebAssembly.Instance
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
/// A `WebAssembly.Instance` object is a stateful, executable instance
|
||||
/// of a `WebAssembly.Module`. Instance objects contain all the exported
|
||||
/// WebAssembly functions that allow calling into WebAssembly code from
|
||||
/// JavaScript.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance)
|
||||
#[wasm_bindgen(extends = Object, js_namespace = WebAssembly)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub type Instance;
|
||||
|
||||
/// The `WebAssembly.Instance()` constructor function can be called to
|
||||
/// synchronously instantiate a given `WebAssembly.Module`
|
||||
/// object. However, the primary way to get an `Instance` is through the
|
||||
/// asynchronous `WebAssembly.instantiateStreaming()` function.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance)
|
||||
#[wasm_bindgen(catch, constructor, js_namespace = WebAssembly)]
|
||||
pub fn new(module: &Module, imports: &Object) -> Result<Instance, JsValue>;
|
||||
|
||||
/// The `exports` readonly property of the `WebAssembly.Instance` object
|
||||
/// prototype returns an object containing as its members all the
|
||||
/// functions exported from the WebAssembly module instance, to allow
|
||||
/// them to be accessed and used by JavaScript.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance/exports)
|
||||
#[wasm_bindgen(getter, method, js_namespace = WebAssembly)]
|
||||
pub fn exports(this: &Instance) -> Object;
|
||||
}
|
||||
|
||||
// WebAssembly.LinkError
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
@ -3004,6 +3061,28 @@ pub mod WebAssembly {
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/length)
|
||||
#[wasm_bindgen(method, getter, js_namespace = WebAssembly)]
|
||||
pub fn length(this: &Table) -> u32;
|
||||
|
||||
/// The `get()` prototype method of the `WebAssembly.Table()` object
|
||||
/// retrieves a function reference stored at a given index.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/get)
|
||||
#[wasm_bindgen(method, catch, js_namespace = WebAssembly)]
|
||||
pub fn get(this: &Table, index: u32) -> Result<Function, JsValue>;
|
||||
|
||||
/// The `grow()` prototype method of the `WebAssembly.Table` object
|
||||
/// increases the size of the `Table` instance by a specified number of
|
||||
/// elements.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/grow)
|
||||
#[wasm_bindgen(method, catch, js_namespace = WebAssembly)]
|
||||
pub fn grow(this: &Table, additional_capacity: u32) -> Result<u32, JsValue>;
|
||||
|
||||
/// The `set()` prototype method of the `WebAssembly.Table` object mutates a
|
||||
/// reference stored at a given index to a different value.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/set)
|
||||
#[wasm_bindgen(method, catch, js_namespace = WebAssembly)]
|
||||
pub fn set(this: &Table, index: u32, function: &Function) -> Result<(), JsValue>;
|
||||
}
|
||||
|
||||
// WebAssembly.Memory
|
||||
@ -3048,8 +3127,12 @@ pub mod WebAssembly {
|
||||
// JSON
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
|
||||
/// The `JSON` object contains methods for parsing [JavaScript Object
|
||||
/// Notation (JSON)](https://json.org/) and converting values to JSON. It
|
||||
/// can't be called or constructed, and aside from its two method
|
||||
/// properties, it has no interesting functionality of its own.
|
||||
#[derive(Clone, Debug)]
|
||||
#[wasm_bindgen(extends = Object)]
|
||||
pub type JSON;
|
||||
|
||||
/// The `JSON.parse()` method parses a JSON string, constructing the
|
||||
|
@ -1,4 +1,4 @@
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_test::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use js_sys::*;
|
||||
@ -82,3 +82,15 @@ fn stringify_error() {
|
||||
let err_msg: String = From::from(err.message());
|
||||
assert!(err_msg.contains("rust really rocks"));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn json_extends() {
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
#[wasm_bindgen(js_name = JSON)]
|
||||
static json: JSON;
|
||||
}
|
||||
|
||||
assert!(json.is_instance_of::<Object>());
|
||||
let _: &Object = json.as_ref();
|
||||
}
|
||||
|
@ -1,9 +1,22 @@
|
||||
use std::f64::consts::PI;
|
||||
use std::f64::{NEG_INFINITY, NAN};
|
||||
|
||||
use wasm_bindgen::{JsCast, prelude::*};
|
||||
use wasm_bindgen_test::*;
|
||||
use js_sys::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn math_extends() {
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
#[wasm_bindgen(js_name = Math)]
|
||||
static math: Math;
|
||||
}
|
||||
|
||||
assert!(math.is_instance_of::<Object>());
|
||||
let _: &Object = math.as_ref();
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($a:expr, $b:expr) => ({
|
||||
let (a, b) = (&$a, &$b);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::{JsCast, prelude::*};
|
||||
use wasm_bindgen_test::*;
|
||||
use js_sys::*;
|
||||
|
||||
@ -180,3 +180,15 @@ fn set_prototype_of() {
|
||||
let obj = JsValue::from(obj);
|
||||
assert_eq!(JsValue::from(Reflect::get_prototype_of(&obj)), JsValue::null());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn reflect_extends() {
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
#[wasm_bindgen(js_name = Reflect)]
|
||||
static reflect: Reflect;
|
||||
}
|
||||
|
||||
assert!(reflect.is_instance_of::<Object>());
|
||||
let _: &Object = reflect.as_ref();
|
||||
}
|
||||
|
@ -21,8 +21,25 @@ function getInvalidTableObject() {
|
||||
return { element: "anyfunc", initial: 1, maximum: 0 }
|
||||
}
|
||||
|
||||
function getImports() {
|
||||
return {
|
||||
imports: {
|
||||
imported_func: function () {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Polyfill `WebAssembly.instantiateStreaming` for node.
|
||||
if (!global.WebAssembly.instantiateStreaming) {
|
||||
global.WebAssembly.instantiateStreaming =
|
||||
(response, imports) => response.then(buf => WebAssembly.instantiate(buf, imports));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getInvalidTableObject,
|
||||
getTableObject,
|
||||
getWasmArray,
|
||||
getImports,
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
use futures::Future;
|
||||
use js_sys::*;
|
||||
use wasm_bindgen::{JsCast, prelude::*};
|
||||
use wasm_bindgen::{prelude::*, JsCast};
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[wasm_bindgen(module = "tests/wasm/WebAssembly.js")]
|
||||
extern {
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_name = getWasmArray)]
|
||||
fn get_wasm_array() -> Uint8Array;
|
||||
|
||||
@ -14,6 +14,9 @@ extern {
|
||||
|
||||
#[wasm_bindgen(js_name = getInvalidTableObject)]
|
||||
fn get_invalid_table_object() -> Object;
|
||||
|
||||
#[wasm_bindgen(js_name = getImports)]
|
||||
fn get_imports() -> Object;
|
||||
}
|
||||
|
||||
fn get_invalid_wasm() -> JsValue {
|
||||
@ -38,23 +41,19 @@ fn validate() {
|
||||
#[wasm_bindgen_test(async)]
|
||||
fn compile_compile_error() -> impl Future<Item = (), Error = JsValue> {
|
||||
let p = WebAssembly::compile(&get_invalid_wasm());
|
||||
JsFuture::from(p)
|
||||
.map(|_| unreachable!())
|
||||
.or_else(|e| {
|
||||
assert!(e.is_instance_of::<WebAssembly::CompileError>());
|
||||
Ok(())
|
||||
})
|
||||
JsFuture::from(p).map(|_| unreachable!()).or_else(|e| {
|
||||
assert!(e.is_instance_of::<WebAssembly::CompileError>());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
fn compile_type_error() -> impl Future<Item = (), Error = JsValue> {
|
||||
let p = WebAssembly::compile(&get_bad_type_wasm());
|
||||
JsFuture::from(p)
|
||||
.map(|_| unreachable!())
|
||||
.or_else(|e| {
|
||||
assert!(e.is_instance_of::<TypeError>());
|
||||
Ok(())
|
||||
})
|
||||
JsFuture::from(p).map(|_| unreachable!()).or_else(|e| {
|
||||
assert!(e.is_instance_of::<TypeError>());
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
@ -63,8 +62,7 @@ fn compile_valid() -> impl Future<Item = (), Error = JsValue> {
|
||||
JsFuture::from(p)
|
||||
.map(|module| {
|
||||
assert!(module.is_instance_of::<WebAssembly::Module>());
|
||||
})
|
||||
.map_err(|_| unreachable!())
|
||||
}).map_err(|_| unreachable!())
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
@ -81,7 +79,9 @@ fn module_error() {
|
||||
let error = WebAssembly::Module::new(&get_invalid_wasm()).err().unwrap();
|
||||
assert!(error.is_instance_of::<WebAssembly::CompileError>());
|
||||
|
||||
let error = WebAssembly::Module::new(&get_bad_type_wasm()).err().unwrap();
|
||||
let error = WebAssembly::Module::new(&get_bad_type_wasm())
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(error.is_instance_of::<TypeError>());
|
||||
}
|
||||
|
||||
@ -117,7 +117,9 @@ fn table_inheritance() {
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn table_error() {
|
||||
let error = WebAssembly::Table::new(&get_invalid_table_object()).err().unwrap();
|
||||
let error = WebAssembly::Table::new(&get_invalid_table_object())
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(error.is_instance_of::<RangeError>());
|
||||
}
|
||||
|
||||
@ -125,6 +127,15 @@ fn table_error() {
|
||||
fn table() {
|
||||
let table = WebAssembly::Table::new(&get_table_object().into()).unwrap();
|
||||
assert_eq!(table.length(), 1);
|
||||
|
||||
assert!(table.get(0).is_ok());
|
||||
assert!(table.get(999).is_err());
|
||||
|
||||
table.grow(1).unwrap();
|
||||
assert_eq!(table.length(), 2);
|
||||
|
||||
let f = table.get(0).unwrap();
|
||||
table.set(1, &f).unwrap();
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
@ -154,6 +165,47 @@ fn runtime_error_inheritance() {
|
||||
let _: &Error = error.as_ref();
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn webassembly_instance() {
|
||||
let module = WebAssembly::Module::new(&get_valid_wasm()).unwrap();
|
||||
let imports = get_imports();
|
||||
let instance = WebAssembly::Instance::new(&module, &imports).unwrap();
|
||||
|
||||
// Inheritance chain is correct.
|
||||
assert!(instance.is_instance_of::<WebAssembly::Instance>());
|
||||
assert!(instance.is_instance_of::<Object>());
|
||||
let _: &Object = instance.as_ref();
|
||||
|
||||
// Has expected exports.
|
||||
let exports = instance.exports();
|
||||
assert!(Reflect::has(exports.as_ref(), &"exported_func".into()));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
fn instantiate_module() -> impl Future<Item = (), Error = JsValue> {
|
||||
let module = WebAssembly::Module::new(&get_valid_wasm()).unwrap();
|
||||
let imports = get_imports();
|
||||
let p = WebAssembly::instantiate_module(&module, &imports);
|
||||
JsFuture::from(p)
|
||||
.map(|inst| {
|
||||
assert!(inst.is_instance_of::<WebAssembly::Instance>());
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
fn instantiate_streaming() -> impl Future<Item = (), Error = JsValue> {
|
||||
let response = Promise::resolve(&get_valid_wasm());
|
||||
let imports = get_imports();
|
||||
let p = WebAssembly::instantiate_streaming(&response, &imports);
|
||||
JsFuture::from(p)
|
||||
.map(|obj| {
|
||||
assert!(
|
||||
Reflect::get(obj.as_ref(), &"instance".into())
|
||||
.is_instance_of::<WebAssembly::Instance>()
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn memory_works() {
|
||||
let obj = Object::new();
|
||||
@ -166,7 +218,10 @@ fn memory_works() {
|
||||
assert_eq!(mem.grow(2), 2);
|
||||
assert_eq!(mem.grow(3), 4);
|
||||
assert_eq!(
|
||||
mem.buffer().dyn_into::<ArrayBuffer>().unwrap().byte_length(),
|
||||
mem.buffer()
|
||||
.dyn_into::<ArrayBuffer>()
|
||||
.unwrap()
|
||||
.byte_length(),
|
||||
7 * 64 * 1024,
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro-support"
|
||||
@ -15,8 +15,8 @@ spans = ["wasm-bindgen-backend/spans"]
|
||||
extra-traits = ["syn/extra-traits"]
|
||||
|
||||
[dependencies]
|
||||
syn = { version = '0.14', features = ['full'] }
|
||||
syn = { version = '0.15.0', features = ['full'] }
|
||||
quote = '0.6'
|
||||
proc-macro2 = "0.4.9"
|
||||
wasm-bindgen-backend = { path = "../backend", version = "=0.2.19" }
|
||||
wasm-bindgen-shared = { path = "../shared", version = "=0.2.19" }
|
||||
wasm-bindgen-backend = { path = "../backend", version = "=0.2.21" }
|
||||
wasm-bindgen-shared = { path = "../shared", version = "=0.2.21" }
|
||||
|
@ -20,8 +20,8 @@ mod parser;
|
||||
|
||||
/// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings
|
||||
pub fn expand(attr: TokenStream, input: TokenStream) -> Result<TokenStream, Diagnostic> {
|
||||
let item = syn_parse::<syn::Item>(input, "rust item")?;
|
||||
let opts = syn_parse(attr, "#[wasm_bindgen] attribute options")?;
|
||||
let item = syn::parse2::<syn::Item>(input)?;
|
||||
let opts = syn::parse2(attr)?;
|
||||
|
||||
let mut tokens = proc_macro2::TokenStream::new();
|
||||
let mut program = backend::ast::Program::default();
|
||||
@ -29,10 +29,3 @@ pub fn expand(attr: TokenStream, input: TokenStream) -> Result<TokenStream, Diag
|
||||
program.try_to_tokens(&mut tokens)?;
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
fn syn_parse<T: syn::synom::Synom>(tokens: TokenStream, name: &str) -> Result<T, Diagnostic> {
|
||||
syn::parse2(tokens.clone())
|
||||
.map_err(|err| {
|
||||
Diagnostic::span_error(&tokens, format!("error parsing {}: {}", name, err))
|
||||
})
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
|
||||
use quote::ToTokens;
|
||||
use shared;
|
||||
use syn;
|
||||
use syn::parse::{Parse, ParseStream, Result as SynResult};
|
||||
|
||||
/// Parsed attributes from a `#[wasm_bindgen(..)]`.
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
@ -39,7 +40,7 @@ impl BindgenAttrs {
|
||||
if group.delimiter() != Delimiter::Parenthesis {
|
||||
bail_span!(attr, "malformed #[wasm_bindgen] attribute");
|
||||
}
|
||||
super::syn_parse(group.stream(), "#[wasm_bindgen] attribute options")
|
||||
Ok(syn::parse2(group.stream())?)
|
||||
}
|
||||
|
||||
/// Get the first module attribute
|
||||
@ -193,19 +194,15 @@ impl BindgenAttrs {
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttrs {
|
||||
named!(parse -> Self, alt!(
|
||||
do_parse!(
|
||||
opts: call!(
|
||||
syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated
|
||||
) >>
|
||||
(BindgenAttrs {
|
||||
attrs: opts.into_iter().collect(),
|
||||
})
|
||||
) => { |s| s }
|
||||
|
|
||||
epsilon!() => { |_| BindgenAttrs { attrs: Vec::new() } }
|
||||
));
|
||||
impl Parse for BindgenAttrs {
|
||||
fn parse(input: ParseStream) -> SynResult<Self> {
|
||||
if input.is_empty() {
|
||||
return Ok(BindgenAttrs { attrs: Vec::new() })
|
||||
}
|
||||
|
||||
let opts = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?;
|
||||
Ok(BindgenAttrs { attrs: opts.into_iter().collect() })
|
||||
}
|
||||
}
|
||||
|
||||
/// The possible attributes in the `#[wasm_bindgen]`.
|
||||
@ -230,109 +227,94 @@ pub enum BindgenAttr {
|
||||
Variadic,
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttr {
|
||||
named!(parse -> Self, alt!(
|
||||
call!(term, "catch") => { |_| BindgenAttr::Catch }
|
||||
|
|
||||
call!(term, "constructor") => { |_| BindgenAttr::Constructor }
|
||||
|
|
||||
call!(term, "method") => { |_| BindgenAttr::Method }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "static_method_of") >>
|
||||
punct!(=) >>
|
||||
cls: call!(term2ident) >>
|
||||
(cls)
|
||||
)=> { BindgenAttr::StaticMethodOf }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "getter") >>
|
||||
val: option!(do_parse!(
|
||||
punct!(=) >>
|
||||
s: call!(term2ident) >>
|
||||
(s)
|
||||
)) >>
|
||||
(val)
|
||||
)=> { BindgenAttr::Getter }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "setter") >>
|
||||
val: option!(do_parse!(
|
||||
punct!(=) >>
|
||||
s: call!(term2ident) >>
|
||||
(s)
|
||||
)) >>
|
||||
(val)
|
||||
)=> { BindgenAttr::Setter }
|
||||
|
|
||||
call!(term, "indexing_getter") => { |_| BindgenAttr::IndexingGetter }
|
||||
|
|
||||
call!(term, "indexing_setter") => { |_| BindgenAttr::IndexingSetter }
|
||||
|
|
||||
call!(term, "indexing_deleter") => { |_| BindgenAttr::IndexingDeleter }
|
||||
|
|
||||
call!(term, "structural") => { |_| BindgenAttr::Structural }
|
||||
|
|
||||
call!(term, "readonly") => { |_| BindgenAttr::Readonly }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_namespace") >>
|
||||
punct!(=) >>
|
||||
ns: call!(term2ident) >>
|
||||
(ns)
|
||||
)=> { BindgenAttr::JsNamespace }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "module") >>
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::Module }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_name") >>
|
||||
punct!(=) >>
|
||||
name: alt!(
|
||||
syn!(syn::LitStr) => { |s| s.value() }
|
||||
|
|
||||
call!(term2ident) => { |s| s.to_string() }
|
||||
) >>
|
||||
(name)
|
||||
)=> { BindgenAttr::JsName }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_class") >>
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::JsClass }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "extends") >>
|
||||
punct!(=) >>
|
||||
ns: call!(term2ident) >>
|
||||
(ns)
|
||||
)=> { BindgenAttr::Extends }
|
||||
|
|
||||
call!(term, "variadic") => { |_| BindgenAttr::Variadic }
|
||||
));
|
||||
}
|
||||
|
||||
/// Consumes a `Ident` with the given name
|
||||
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> {
|
||||
if let Some((ident, next)) = cursor.ident() {
|
||||
if ident == name {
|
||||
return Ok(((), next));
|
||||
impl Parse for BindgenAttr {
|
||||
fn parse(input: ParseStream) -> SynResult<Self> {
|
||||
let original = input.fork();
|
||||
let attr: Ident = input.parse()?;
|
||||
if attr == "catch" {
|
||||
return Ok(BindgenAttr::Catch)
|
||||
}
|
||||
if attr == "constructor" {
|
||||
return Ok(BindgenAttr::Constructor)
|
||||
}
|
||||
if attr == "method" {
|
||||
return Ok(BindgenAttr::Method)
|
||||
}
|
||||
if attr == "indexing_getter" {
|
||||
return Ok(BindgenAttr::IndexingGetter)
|
||||
}
|
||||
if attr == "indexing_setter" {
|
||||
return Ok(BindgenAttr::IndexingSetter)
|
||||
}
|
||||
if attr == "indexing_deleter" {
|
||||
return Ok(BindgenAttr::IndexingDeleter)
|
||||
}
|
||||
if attr == "structural" {
|
||||
return Ok(BindgenAttr::Structural)
|
||||
}
|
||||
if attr == "readonly" {
|
||||
return Ok(BindgenAttr::Readonly)
|
||||
}
|
||||
if attr == "variadic" {
|
||||
return Ok(BindgenAttr::Variadic)
|
||||
}
|
||||
if attr == "static_method_of" {
|
||||
input.parse::<Token![=]>()?;
|
||||
return Ok(BindgenAttr::StaticMethodOf(input.parse::<AnyIdent>()?.0))
|
||||
}
|
||||
if attr == "getter" {
|
||||
if input.parse::<Token![=]>().is_ok() {
|
||||
return Ok(BindgenAttr::Getter(Some(input.parse::<AnyIdent>()?.0)))
|
||||
} else {
|
||||
return Ok(BindgenAttr::Getter(None))
|
||||
}
|
||||
}
|
||||
if attr == "setter" {
|
||||
if input.parse::<Token![=]>().is_ok() {
|
||||
return Ok(BindgenAttr::Setter(Some(input.parse::<AnyIdent>()?.0)))
|
||||
} else {
|
||||
return Ok(BindgenAttr::Setter(None))
|
||||
}
|
||||
}
|
||||
if attr == "js_namespace" {
|
||||
input.parse::<Token![=]>()?;
|
||||
return Ok(BindgenAttr::JsNamespace(input.parse::<AnyIdent>()?.0))
|
||||
}
|
||||
if attr == "extends" {
|
||||
input.parse::<Token![=]>()?;
|
||||
return Ok(BindgenAttr::Extends(input.parse::<AnyIdent>()?.0))
|
||||
}
|
||||
if attr == "module" {
|
||||
input.parse::<Token![=]>()?;
|
||||
return Ok(BindgenAttr::Module(input.parse::<syn::LitStr>()?.value()))
|
||||
}
|
||||
if attr == "js_class" {
|
||||
input.parse::<Token![=]>()?;
|
||||
return Ok(BindgenAttr::JsClass(input.parse::<syn::LitStr>()?.value()))
|
||||
}
|
||||
if attr == "js_name" {
|
||||
input.parse::<Token![=]>()?;
|
||||
let val = match input.parse::<syn::LitStr>() {
|
||||
Ok(str) => str.value(),
|
||||
Err(_) => input.parse::<AnyIdent>()?.0.to_string(),
|
||||
};
|
||||
return Ok(BindgenAttr::JsName(val))
|
||||
}
|
||||
|
||||
Err(original.error("unknown attribute"))
|
||||
}
|
||||
syn::parse_error()
|
||||
}
|
||||
|
||||
/// Consumes a `Ident` and returns it.
|
||||
fn term2ident<'a>(cursor: syn::buffer::Cursor<'a>) -> syn::synom::PResult<'a, Ident> {
|
||||
match cursor.ident() {
|
||||
Some(pair) => Ok(pair),
|
||||
None => syn::parse_error(),
|
||||
struct AnyIdent(Ident);
|
||||
|
||||
impl Parse for AnyIdent {
|
||||
fn parse(input: ParseStream) -> SynResult<Self> {
|
||||
input.step(|cursor| {
|
||||
match cursor.ident() {
|
||||
Some((ident, remaining)) => Ok((AnyIdent(ident), remaining)),
|
||||
None => Err(cursor.error("expected an identifier")),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -841,6 +823,10 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) {
|
||||
&*item,
|
||||
"type definitions in impls aren't supported with #[wasm_bindgen]"
|
||||
),
|
||||
syn::ImplItem::Existential(_) => bail_span!(
|
||||
&*item,
|
||||
"existentials in impls aren't supported with #[wasm_bindgen]"
|
||||
),
|
||||
syn::ImplItem::Macro(_) => {
|
||||
bail_span!(&*item, "macros in impls aren't supported");
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro"
|
||||
@ -18,5 +18,5 @@ spans = ["wasm-bindgen-macro-support/spans"]
|
||||
xxx_debug_only_print_generated_code = []
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen-macro-support = { path = "../macro-support", version = "=0.2.19" }
|
||||
wasm-bindgen-macro-support = { path = "../macro-support", version = "=0.2.21" }
|
||||
quote = "0.6"
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: error parsing #[wasm_bindgen] attribute options: failed to parse anything
|
||||
error: unknown attribute
|
||||
--> $DIR/attribute-fails-to-parse.rs:5:16
|
||||
|
|
||||
5 | #[wasm_bindgen(nonsense)]
|
||||
|
@ -1,10 +1,10 @@
|
||||
error: error parsing #[wasm_bindgen] attribute options: failed to parse anything
|
||||
error: unknown attribute
|
||||
--> $DIR/invalid-attr.rs:5:16
|
||||
|
|
||||
5 | #[wasm_bindgen(x)]
|
||||
| ^
|
||||
|
||||
error: error parsing #[wasm_bindgen] attribute options: failed to parse anything
|
||||
error: unknown attribute
|
||||
--> $DIR/invalid-attr.rs:10:20
|
||||
|
|
||||
10 | #[wasm_bindgen(y)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/shared"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
description = "Internal testing macro for wasm-bindgen"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
description = "Internal testing crate for wasm-bindgen"
|
||||
license = "MIT/Apache-2.0"
|
||||
@ -9,11 +9,11 @@ repository = "https://github.com/rustwasm/wasm-bindgen"
|
||||
[dependencies]
|
||||
console_error_panic_hook = '0.1'
|
||||
futures = "0.1"
|
||||
js-sys = { path = '../js-sys', version = '0.2.4' }
|
||||
js-sys = { path = '../js-sys', version = '0.2.6' }
|
||||
scoped-tls = "0.1"
|
||||
wasm-bindgen = { path = '../..', version = '0.2.19' }
|
||||
wasm-bindgen-futures = { path = '../futures', version = '0.2.19' }
|
||||
wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.2.19' }
|
||||
wasm-bindgen = { path = '../..', version = '0.2.21' }
|
||||
wasm-bindgen-futures = { path = '../futures', version = '0.2.21' }
|
||||
wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.2.21' }
|
||||
|
||||
[lib]
|
||||
test = false
|
||||
|
@ -10,6 +10,6 @@ serde_json = "1.0"
|
||||
|
||||
proc-macro2 = "0.4.8"
|
||||
quote = "0.6"
|
||||
syn = { version = "0.14", default-features = false }
|
||||
syn = { version = "0.15", default-features = false }
|
||||
wasm-bindgen = { path = "../..", default-features = false }
|
||||
wasm-bindgen-backend = { path = "../backend", default-features = false }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-wasm-interpreter"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/wasm-interpreter"
|
||||
|
@ -35,17 +35,22 @@ pub struct Interpreter {
|
||||
// calculations)
|
||||
imports: usize,
|
||||
|
||||
// Function index of the `__wbindgen_describe` imported function. We special
|
||||
// case this to know when the environment's imported function is called.
|
||||
// Function index of the `__wbindgen_describe` and
|
||||
// `__wbindgen_describe_closure` imported functions. We special case this
|
||||
// to know when the environment's imported function is called.
|
||||
describe_idx: Option<u32>,
|
||||
describe_closure_idx: Option<u32>,
|
||||
|
||||
// A mapping of string names to the function index, filled with all exported
|
||||
// functions.
|
||||
name_map: HashMap<String, u32>,
|
||||
|
||||
// The numerical index of the code section in the wasm module, indexed into
|
||||
// The numerical index of the sections in the wasm module, indexed into
|
||||
// the module's list of sections.
|
||||
code_idx: Option<usize>,
|
||||
types_idx: Option<usize>,
|
||||
functions_idx: Option<usize>,
|
||||
elements_idx: Option<usize>,
|
||||
|
||||
// The current stack pointer (global 0) and wasm memory (the stack). Only
|
||||
// used in a limited capacity.
|
||||
@ -60,6 +65,18 @@ pub struct Interpreter {
|
||||
// very specific to wasm-bindgen and is the purpose for the existence of
|
||||
// this module.
|
||||
descriptor: Vec<u32>,
|
||||
|
||||
// When invoking the `__wbindgen_describe_closure` imported function, this
|
||||
// stores the last table index argument, used for finding a different
|
||||
// descriptor.
|
||||
descriptor_table_idx: Option<u32>,
|
||||
}
|
||||
|
||||
struct Sections<'a> {
|
||||
code: &'a CodeSection,
|
||||
types: &'a TypeSection,
|
||||
functions: &'a FunctionSection,
|
||||
elements: &'a ElementSection,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
@ -82,6 +99,9 @@ impl Interpreter {
|
||||
for (i, s) in module.sections().iter().enumerate() {
|
||||
match s {
|
||||
Section::Code(_) => ret.code_idx = Some(i),
|
||||
Section::Element(_) => ret.elements_idx = Some(i),
|
||||
Section::Type(_) => ret.types_idx = Some(i),
|
||||
Section::Function(_) => ret.functions_idx = Some(i),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -101,10 +121,11 @@ impl Interpreter {
|
||||
if entry.module() != "__wbindgen_placeholder__" {
|
||||
continue
|
||||
}
|
||||
if entry.field() != "__wbindgen_describe" {
|
||||
continue
|
||||
if entry.field() == "__wbindgen_describe" {
|
||||
ret.describe_idx = Some(idx - 1 as u32);
|
||||
} else if entry.field() == "__wbindgen_describe_closure" {
|
||||
ret.describe_closure_idx = Some(idx - 1 as u32);
|
||||
}
|
||||
ret.describe_idx = Some(idx - 1 as u32);
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,47 +163,171 @@ impl Interpreter {
|
||||
///
|
||||
/// Returns `Some` if `func` was found in the `module` and `None` if it was
|
||||
/// not found in the `module`.
|
||||
pub fn interpret(&mut self, func: &str, module: &Module) -> Option<&[u32]> {
|
||||
self.descriptor.truncate(0);
|
||||
pub fn interpret_descriptor(
|
||||
&mut self,
|
||||
func: &str,
|
||||
module: &Module,
|
||||
) -> Option<&[u32]> {
|
||||
let idx = *self.name_map.get(func)?;
|
||||
let code = match &module.sections()[self.code_idx.unwrap()] {
|
||||
Section::Code(s) => s,
|
||||
_ => panic!(),
|
||||
};
|
||||
self.with_sections(module, |me, sections| {
|
||||
me.interpret_descriptor_idx(idx, sections)
|
||||
})
|
||||
}
|
||||
|
||||
fn interpret_descriptor_idx(
|
||||
&mut self,
|
||||
idx: u32,
|
||||
sections: &Sections,
|
||||
) -> Option<&[u32]> {
|
||||
self.descriptor.truncate(0);
|
||||
|
||||
// We should have a blank wasm and LLVM stack at both the start and end
|
||||
// of the call.
|
||||
assert_eq!(self.sp, self.mem.len() as i32);
|
||||
assert_eq!(self.stack.len(), 0);
|
||||
self.call(idx, code);
|
||||
self.call(idx, sections);
|
||||
assert_eq!(self.stack.len(), 0);
|
||||
assert_eq!(self.sp, self.mem.len() as i32);
|
||||
Some(&self.descriptor)
|
||||
}
|
||||
|
||||
fn call(&mut self, idx: u32, code: &CodeSection) {
|
||||
/// Interprets a "closure descriptor", figuring out the signature of the
|
||||
/// closure that was intended.
|
||||
///
|
||||
/// This function will take a `code_idx` which is known to internally
|
||||
/// execute `__wbindgen_describe_closure` and interpret it. The
|
||||
/// `wasm-bindgen` crate controls all callers of this internal import. It
|
||||
/// will then take the index passed to `__wbindgen_describe_closure` and
|
||||
/// interpret it as a function pointer. This means it'll look up within the
|
||||
/// element section (function table) which index it points to. Upon finding
|
||||
/// the relevant entry it'll assume that function is a descriptor function,
|
||||
/// and then it will execute the descriptor function.
|
||||
///
|
||||
/// The returned value is the return value of the descriptor function found.
|
||||
/// The `entry_removal_list` list is also then populated with an index of
|
||||
/// the entry in the elements section (and then the index within that
|
||||
/// section) of the function that needs to be snip'd out.
|
||||
pub fn interpret_closure_descriptor(
|
||||
&mut self,
|
||||
code_idx: usize,
|
||||
module: &Module,
|
||||
entry_removal_list: &mut Vec<(usize, usize)>,
|
||||
) -> Option<&[u32]> {
|
||||
self.with_sections(module, |me, sections| {
|
||||
me._interpret_closure_descriptor(code_idx, sections, entry_removal_list)
|
||||
})
|
||||
}
|
||||
|
||||
fn _interpret_closure_descriptor(
|
||||
&mut self,
|
||||
code_idx: usize,
|
||||
sections: &Sections,
|
||||
entry_removal_list: &mut Vec<(usize, usize)>,
|
||||
) -> Option<&[u32]> {
|
||||
// Call the `code_idx` function. This is an internal `#[inline(never)]`
|
||||
// whose code is completely controlled by the `wasm-bindgen` crate, so
|
||||
// it should take two arguments and return one (all of which we don't
|
||||
// care about here). What we're interested in is that while executing
|
||||
// this function it'll call `__wbindgen_describe_closure` with an
|
||||
// argument that we look for.
|
||||
assert!(self.descriptor_table_idx.is_none());
|
||||
let closure_descriptor_idx = (code_idx + self.imports) as u32;
|
||||
self.stack.push(0);
|
||||
self.stack.push(0);
|
||||
self.call(closure_descriptor_idx, sections);
|
||||
assert_eq!(self.stack.len(), 1);
|
||||
self.stack.pop();
|
||||
let descriptor_table_idx = self.descriptor_table_idx.take().unwrap();
|
||||
|
||||
// After we've got the table index of the descriptor function we're
|
||||
// interested go take a look in the function table to find what the
|
||||
// actual index of the function is.
|
||||
let (entry_idx, offset, entry) = sections.elements.entries()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, entry)| {
|
||||
let code = entry.offset().code();
|
||||
if code.len() != 2 {
|
||||
return None
|
||||
}
|
||||
if code[1] != Instruction::End {
|
||||
return None
|
||||
}
|
||||
match code[0] {
|
||||
Instruction::I32Const(x) => Some((i, x as u32, entry)),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.find(|(_i, offset, entry)| {
|
||||
*offset <= descriptor_table_idx &&
|
||||
descriptor_table_idx < (*offset + entry.members().len() as u32)
|
||||
})
|
||||
.expect("failed to find index in table elements");
|
||||
let idx = (descriptor_table_idx - offset) as usize;
|
||||
let descriptor_idx = entry.members()[idx];
|
||||
|
||||
// This is used later to actually remove the entry from the table, but
|
||||
// we don't do the removal just yet
|
||||
entry_removal_list.push((entry_idx, idx));
|
||||
|
||||
// And now execute the descriptor!
|
||||
self.interpret_descriptor_idx(descriptor_idx, sections)
|
||||
}
|
||||
|
||||
/// Returns the function space index of the `__wbindgen_describe_closure`
|
||||
/// imported function.
|
||||
pub fn describe_closure_idx(&self) -> Option<u32> {
|
||||
self.describe_closure_idx
|
||||
}
|
||||
|
||||
fn call(&mut self, idx: u32, sections: &Sections) {
|
||||
use parity_wasm::elements::Instruction::*;
|
||||
|
||||
let idx = idx as usize;
|
||||
assert!(idx >= self.imports); // can't call imported functions
|
||||
let body = &code.bodies()[idx - self.imports];
|
||||
let code_idx = idx - self.imports;
|
||||
let body = §ions.code.bodies()[code_idx];
|
||||
|
||||
// Allocate space for our call frame's local variables. All local
|
||||
// variables should be of the `i32` type.
|
||||
assert!(body.locals().len() <= 1, "too many local types");
|
||||
let locals = body.locals()
|
||||
let nlocals = body.locals()
|
||||
.get(0)
|
||||
.map(|i| {
|
||||
assert_eq!(i.value_type(), ValueType::I32);
|
||||
i.count()
|
||||
})
|
||||
.unwrap_or(0);
|
||||
let mut locals = vec![0; locals as usize];
|
||||
|
||||
let code_sig = sections.functions.entries()[code_idx].type_ref();
|
||||
let function_ty = match §ions.types.types()[code_sig as usize] {
|
||||
Type::Function(t) => t,
|
||||
};
|
||||
let mut locals = Vec::with_capacity(function_ty.params().len() + nlocals as usize);
|
||||
// Any function parameters we have get popped off the stack and put into
|
||||
// the first few locals ...
|
||||
for param in function_ty.params() {
|
||||
assert_eq!(*param, ValueType::I32);
|
||||
locals.push(self.stack.pop().unwrap());
|
||||
}
|
||||
// ... and the remaining locals all start as zero ...
|
||||
for _ in 0..nlocals {
|
||||
locals.push(0);
|
||||
}
|
||||
// ... and we expect one stack slot at the end if there's a returned
|
||||
// value
|
||||
let before = self.stack.len();
|
||||
let stack_after = match function_ty.return_type() {
|
||||
Some(t) => {
|
||||
assert_eq!(t, ValueType::I32);
|
||||
before + 1
|
||||
}
|
||||
None => before,
|
||||
};
|
||||
|
||||
// Actual interpretation loop! We keep track of our stack's length to
|
||||
// recover it as part of the `Return` instruction, and otherwise this is
|
||||
// a pretty straightforward interpretation loop.
|
||||
let before = self.stack.len();
|
||||
for instr in body.code().elements() {
|
||||
match instr {
|
||||
I32Const(x) => self.stack.push(*x),
|
||||
@ -198,8 +343,14 @@ impl Interpreter {
|
||||
// Otherwise this is a normal call so we recurse.
|
||||
if Some(*idx) == self.describe_idx {
|
||||
self.descriptor.push(self.stack.pop().unwrap() as u32);
|
||||
} else if Some(*idx) == self.describe_closure_idx {
|
||||
self.descriptor_table_idx =
|
||||
Some(self.stack.pop().unwrap() as u32);
|
||||
assert_eq!(self.stack.pop(), Some(0));
|
||||
assert_eq!(self.stack.pop(), Some(0));
|
||||
self.stack.push(0);
|
||||
} else {
|
||||
self.call(*idx, code);
|
||||
self.call(*idx, sections);
|
||||
}
|
||||
}
|
||||
GetGlobal(0) => self.stack.push(self.sp),
|
||||
@ -223,7 +374,7 @@ impl Interpreter {
|
||||
let addr = self.stack.pop().unwrap() as u32;
|
||||
self.stack.push(self.mem[((addr + *offset) as usize) / 4]);
|
||||
}
|
||||
Return => self.stack.truncate(before),
|
||||
Return => self.stack.truncate(stack_after),
|
||||
End => break,
|
||||
|
||||
// All other instructions shouldn't be used by our various
|
||||
@ -238,6 +389,37 @@ impl Interpreter {
|
||||
s => panic!("unknown instruction {:?}", s),
|
||||
}
|
||||
}
|
||||
assert_eq!(self.stack.len(), before);
|
||||
assert_eq!(self.stack.len(), stack_after);
|
||||
}
|
||||
|
||||
fn with_sections<'a, T>(
|
||||
&'a mut self,
|
||||
module: &Module,
|
||||
f: impl FnOnce(&'a mut Self, &Sections) -> T,
|
||||
) -> T {
|
||||
macro_rules! access_with_defaults {
|
||||
($(
|
||||
let $var: ident = module.sections[self.$field:ident]
|
||||
($name:ident);
|
||||
)*) => {$(
|
||||
let default = Default::default();
|
||||
let $var = match self.$field {
|
||||
Some(i) => {
|
||||
match &module.sections()[i] {
|
||||
Section::$name(s) => s,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
None => &default,
|
||||
};
|
||||
)*}
|
||||
}
|
||||
access_with_defaults! {
|
||||
let code = module.sections[self.code_idx] (Code);
|
||||
let types = module.sections[self.types_idx] (Type);
|
||||
let functions = module.sections[self.functions_idx] (Function);
|
||||
let elements = module.sections[self.elements_idx] (Element);
|
||||
}
|
||||
f(self, &Sections { code, types, functions, elements })
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,13 @@ wasm-bindgen-webidl = { path = "../webidl", version = "=0.2.17" }
|
||||
sourcefile = "0.1"
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = { path = "../..", version = "0.2.19" }
|
||||
js-sys = { path = '../js-sys', version = '0.2.4' }
|
||||
wasm-bindgen = { path = "../..", version = "0.2.21" }
|
||||
js-sys = { path = '../js-sys', version = '0.2.6' }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
futures = "0.1"
|
||||
wasm-bindgen-test = { path = '../test', version = '0.2.19' }
|
||||
wasm-bindgen-futures = { path = '../futures', version = '0.2.19' }
|
||||
wasm-bindgen-test = { path = '../test', version = '0.2.21' }
|
||||
wasm-bindgen-futures = { path = '../futures', version = '0.2.21' }
|
||||
|
||||
# This list is generated by passing `__WASM_BINDGEN_DUMP_FEATURES=foo` when
|
||||
# compiling this crate which dumps the total list of features to a file called
|
||||
|
@ -7,5 +7,5 @@ fn take_and_return_a_bunch_of_slices() {
|
||||
let f = ArrayBufferTest::new().unwrap();
|
||||
let x = f.get_buffer();
|
||||
f.set_buffer(None);
|
||||
f.set_buffer(Some(x));
|
||||
f.set_buffer(Some(&x));
|
||||
}
|
||||
|
@ -153,3 +153,13 @@ global.MixinFoo = class MixinFoo {
|
||||
global.Overloads = class {
|
||||
foo() {}
|
||||
};
|
||||
|
||||
global.InvokeCallback = class {
|
||||
invoke(f) { f(); }
|
||||
callAdd(f) {
|
||||
return f(1, 2);
|
||||
}
|
||||
callRepeat(f) {
|
||||
return f('ab', 4);
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,6 @@
|
||||
use wasm_bindgen_test::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/simple.rs"));
|
||||
|
||||
@ -130,3 +132,26 @@ fn overload_naming() {
|
||||
o.foo_with_arg_and_f32("x", 2.0);
|
||||
o.foo_with_arg_and_i16("x", 5);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn callback() {
|
||||
let o = InvokeCallback::new().unwrap();
|
||||
{
|
||||
static mut HIT: bool = false;
|
||||
let cb = Closure::wrap(Box::new(move || {
|
||||
unsafe { HIT = true; }
|
||||
}) as Box<FnMut()>);
|
||||
o.invoke(cb.as_ref().unchecked_ref());
|
||||
assert!(unsafe { HIT });
|
||||
}
|
||||
|
||||
let cb = Closure::wrap(Box::new(move |a, b| {
|
||||
a + b
|
||||
}) as Box<FnMut(u32, u32) -> u32>);
|
||||
assert_eq!(o.call_add(cb.as_ref().unchecked_ref()), 3);
|
||||
|
||||
let cb = Closure::wrap(Box::new(move |a: String, b| {
|
||||
a.repeat(b)
|
||||
}) as Box<FnMut(String, usize) -> String>);
|
||||
assert_eq!(o.call_repeat(cb.as_ref().unchecked_ref()), "abababab");
|
||||
}
|
||||
|
12
crates/webidl-tests/simple.webidl
vendored
12
crates/webidl-tests/simple.webidl
vendored
@ -100,3 +100,15 @@ interface Overloads {
|
||||
void foo(DOMString arg, optional long a);
|
||||
void foo(DOMString arg, (float or short) b);
|
||||
};
|
||||
|
||||
callback MyCallback = any();
|
||||
callback AddCallback = long(long a, long b);
|
||||
callback RepeatCallback = DOMString(DOMString a, long cnt);
|
||||
callback GetAnswer = long();
|
||||
|
||||
[Constructor()]
|
||||
interface InvokeCallback {
|
||||
void invoke(MyCallback callback);
|
||||
long callAdd(AddCallback callback);
|
||||
DOMString callRepeat(RepeatCallback callback);
|
||||
};
|
||||
|
@ -18,6 +18,6 @@ heck = "0.3"
|
||||
log = "0.4.1"
|
||||
proc-macro2 = "0.4.8"
|
||||
quote = '0.6'
|
||||
syn = { version = '0.14', features = ['full'] }
|
||||
wasm-bindgen-backend = { version = "=0.2.19", path = "../backend" }
|
||||
syn = { version = '0.15', features = ['full'] }
|
||||
wasm-bindgen-backend = { version = "=0.2.21", path = "../backend" }
|
||||
weedle = "0.7"
|
||||
|
@ -34,6 +34,7 @@ pub(crate) struct FirstPassRecord<'src> {
|
||||
pub(crate) namespaces: BTreeMap<&'src str, NamespaceData<'src>>,
|
||||
pub(crate) includes: BTreeMap<&'src str, BTreeSet<&'src str>>,
|
||||
pub(crate) dictionaries: BTreeMap<&'src str, DictionaryData<'src>>,
|
||||
pub(crate) callbacks: BTreeSet<&'src str>,
|
||||
}
|
||||
|
||||
/// We need to collect interface data during the first pass, to be used later.
|
||||
@ -136,12 +137,9 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> {
|
||||
Namespace(namespace) => namespace.first_pass(record, ()),
|
||||
PartialNamespace(namespace) => namespace.first_pass(record, ()),
|
||||
Typedef(typedef) => typedef.first_pass(record, ()),
|
||||
Callback(callback) => callback.first_pass(record, ()),
|
||||
|
||||
Implements(_) => Ok(()),
|
||||
Callback(..) => {
|
||||
warn!("Unsupported WebIDL Callback definition: {:?}", self);
|
||||
Ok(())
|
||||
}
|
||||
CallbackInterface(..) => {
|
||||
warn!("Unsupported WebIDL CallbackInterface definition: {:?}", self);
|
||||
Ok(())
|
||||
@ -684,6 +682,13 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceM
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::CallbackDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, _: ()) -> Result<()> {
|
||||
record.callbacks.insert(self.identifier.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FirstPassRecord<'a> {
|
||||
pub fn all_superclasses<'me>(&'me self, interface: &str)
|
||||
-> impl Iterator<Item = String> + 'me
|
||||
|
@ -28,6 +28,7 @@ pub(crate) enum IdlType<'a> {
|
||||
Object,
|
||||
Symbol,
|
||||
Error,
|
||||
Callback,
|
||||
|
||||
ArrayBuffer,
|
||||
DataView,
|
||||
@ -293,6 +294,8 @@ impl<'a> ToIdlType<'a> for Identifier<'a> {
|
||||
Some(IdlType::Dictionary(self.0))
|
||||
} else if record.enums.contains_key(self.0) {
|
||||
Some(IdlType::Enum(self.0))
|
||||
} else if record.callbacks.contains(self.0) {
|
||||
Some(IdlType::Callback)
|
||||
} else {
|
||||
warn!("Unrecognized type: {}", self.0);
|
||||
None
|
||||
@ -364,6 +367,7 @@ impl<'a> IdlType<'a> {
|
||||
IdlType::Object => dst.push_str("object"),
|
||||
IdlType::Symbol => dst.push_str("symbol"),
|
||||
IdlType::Error => dst.push_str("error"),
|
||||
IdlType::Callback => dst.push_str("callback"),
|
||||
|
||||
IdlType::ArrayBuffer => dst.push_str("array_buffer"),
|
||||
IdlType::DataView => dst.push_str("data_view"),
|
||||
@ -426,6 +430,17 @@ impl<'a> IdlType<'a> {
|
||||
|
||||
/// Converts to syn type if possible.
|
||||
pub(crate) fn to_syn_type(&self, pos: TypePosition) -> Option<syn::Type> {
|
||||
let anyref = |ty| {
|
||||
Some(match pos {
|
||||
TypePosition::Argument => shared_ref(ty, false),
|
||||
TypePosition::Return => ty,
|
||||
})
|
||||
};
|
||||
let js_sys = |name: &str| {
|
||||
let path = vec![rust_ident("js_sys"), rust_ident(name)];
|
||||
let ty = leading_colon_path_ty(path);
|
||||
anyref(ty)
|
||||
};
|
||||
match self {
|
||||
IdlType::Boolean => Some(ident_ty(raw_ident("bool"))),
|
||||
IdlType::Byte => Some(ident_ty(raw_ident("i8"))),
|
||||
@ -446,17 +461,11 @@ impl<'a> IdlType<'a> {
|
||||
TypePosition::Argument => Some(shared_ref(ident_ty(raw_ident("str")), false)),
|
||||
TypePosition::Return => Some(ident_ty(raw_ident("String"))),
|
||||
},
|
||||
IdlType::Object => {
|
||||
let path = vec![rust_ident("js_sys"), rust_ident("Object")];
|
||||
Some(leading_colon_path_ty(path))
|
||||
},
|
||||
IdlType::Object => js_sys("Object"),
|
||||
IdlType::Symbol => None,
|
||||
IdlType::Error => None,
|
||||
|
||||
IdlType::ArrayBuffer => {
|
||||
let path = vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")];
|
||||
Some(leading_colon_path_ty(path))
|
||||
},
|
||||
IdlType::ArrayBuffer => js_sys("ArrayBuffer"),
|
||||
IdlType::DataView => None,
|
||||
IdlType::Int8Array => Some(array("i8", pos, false)),
|
||||
IdlType::Uint8Array => Some(array("u8", pos, false)),
|
||||
@ -469,10 +478,7 @@ impl<'a> IdlType<'a> {
|
||||
IdlType::Float32Array => Some(array("f32", pos, false)),
|
||||
IdlType::Float64Array => Some(array("f64", pos, false)),
|
||||
|
||||
IdlType::ArrayBufferView | IdlType::BufferSource => {
|
||||
let path = vec![rust_ident("js_sys"), rust_ident("Object")];
|
||||
Some(leading_colon_path_ty(path))
|
||||
},
|
||||
IdlType::ArrayBufferView | IdlType::BufferSource => js_sys("Object"),
|
||||
IdlType::Interface(name)
|
||||
| IdlType::Dictionary(name) => {
|
||||
let ty = ident_ty(rust_ident(camel_case_ident(name).as_str()));
|
||||
@ -487,15 +493,7 @@ impl<'a> IdlType<'a> {
|
||||
IdlType::Nullable(idl_type) => Some(option_ty(idl_type.to_syn_type(pos)?)),
|
||||
IdlType::FrozenArray(_idl_type) => None,
|
||||
IdlType::Sequence(_idl_type) => None,
|
||||
IdlType::Promise(_idl_type) => {
|
||||
let path = vec![rust_ident("js_sys"), rust_ident("Promise")];
|
||||
let ty = leading_colon_path_ty(path);
|
||||
if pos == TypePosition::Argument {
|
||||
Some(shared_ref(ty, false))
|
||||
} else {
|
||||
Some(ty)
|
||||
}
|
||||
}
|
||||
IdlType::Promise(_idl_type) => js_sys("Promise"),
|
||||
IdlType::Record(_idl_type_from, _idl_type_to) => None,
|
||||
IdlType::Union(idl_types) => {
|
||||
// Handles union types in all places except operation argument types.
|
||||
@ -521,9 +519,10 @@ impl<'a> IdlType<'a> {
|
||||
|
||||
IdlType::Any => {
|
||||
let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")];
|
||||
Some(leading_colon_path_ty(path))
|
||||
anyref(leading_colon_path_ty(path))
|
||||
},
|
||||
IdlType::Void => None,
|
||||
IdlType::Callback => js_sys("Function"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -577,9 +576,12 @@ impl<'a> IdlType<'a> {
|
||||
.iter()
|
||||
.flat_map(|idl_type| idl_type.flatten())
|
||||
.collect(),
|
||||
IdlType::ArrayBufferView | IdlType::BufferSource =>
|
||||
vec![IdlType::Object, IdlType::Uint8ArrayMut],
|
||||
|
||||
IdlType::ArrayBufferView => {
|
||||
vec![IdlType::ArrayBufferView, IdlType::Uint8ArrayMut]
|
||||
}
|
||||
IdlType::BufferSource => {
|
||||
vec![IdlType::BufferSource, IdlType::Uint8ArrayMut]
|
||||
}
|
||||
idl_type @ _ => vec![idl_type.clone()],
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ fn builtin_idents() -> BTreeSet<Ident> {
|
||||
vec![
|
||||
"str", "char", "bool", "JsValue", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64",
|
||||
"usize", "isize", "f32", "f64", "Result", "String", "Box", "Vec", "Option",
|
||||
"ArrayBuffer", "Object", "Promise",
|
||||
"ArrayBuffer", "Object", "Promise", "Function",
|
||||
].into_iter()
|
||||
.map(|id| proc_macro2::Ident::new(id, proc_macro2::Span::call_site())),
|
||||
)
|
||||
|
@ -5,4 +5,99 @@ source lives at `wasm-bindgen/crates/web-sys`.
|
||||
|
||||
The `web-sys` crate is **entirely** mechanically generated inside `build.rs`
|
||||
using `wasm-bindgen`'s WebIDL frontend and the WebIDL interface definitions for
|
||||
Web APIs.
|
||||
Web APIs. This means that `web-sys` isn't always the most ergonomic crate to
|
||||
use, but it's intended to provide verified and correct bindings to the web
|
||||
platform, and then better interfaces can be iterated on crates.io!
|
||||
|
||||
### Using `web-sys`
|
||||
|
||||
Let's say you want to use an API defined on the web. Chances are this API is
|
||||
defined in `web-sys`, so let's go through some steps necessary to use it!
|
||||
|
||||
First up, search the [api documentation][api] for your API. For example if
|
||||
we're looking for JS's [`fetch`][jsfetch] API we'd start out by [searching for
|
||||
`fetch`][search-fetch]. The first thing you'll probably notice is that there's
|
||||
no function called `fetch`! Fear not, though, as the API exists in multiple
|
||||
forms:
|
||||
|
||||
* [`Window::fetch_with_str`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_str)
|
||||
* [`Window::fetch_with_request`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request)
|
||||
* [`Window::fetch_with_str_and_init`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/str_and_inituct.Window.html#method.fetch_with_str_and_init)
|
||||
* [`Window::fetch_with_request_and_init`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request_and_init)
|
||||
|
||||
What's happening here is that the [`fetch` function][fetchfn] actually supports
|
||||
multiple signatures of arguments, and we've taken the WebIDL definition for this
|
||||
function and expanded it to unique signatures in Rust (as Rust doesn't have
|
||||
function name overloading).
|
||||
|
||||
When an API is selected it should have documentation pointing at MDN indicating
|
||||
what web API its binding. This is often a great way to double check arguments
|
||||
and such as well, MDN is a great resource! You'll also notice in the
|
||||
documentation that the API may require some `web-sys` Cargo features to be
|
||||
activated. For example [`fetch_with_str`] requires the `Window` feature to be
|
||||
activated. In general an API needs features corresponding to all the types
|
||||
you'll find in the signature to be activated.
|
||||
|
||||
To load up this API let's depend on `web-sys`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.1", features = ['Window'] }
|
||||
|
||||
# Or optionally,
|
||||
# [target.wasm32-unknown-unknown.dependencies]
|
||||
# ...
|
||||
```
|
||||
|
||||
> **Note**: Currently `web-sys` is not available on crates.io so you'll also
|
||||
> need to do this in your manifest:
|
||||
>
|
||||
> ```toml
|
||||
> [patch.crates-io]
|
||||
> web-sys = { git = 'https://github.com/rustwasm/wasm-bindgen' }
|
||||
> wasm-bindgen = { git = 'https://github.com/rustwasm/wasm-bindgen' }
|
||||
> ```
|
||||
|
||||
And next up we can use the API like so:
|
||||
|
||||
```rust
|
||||
extern crate web_sys;
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::Window;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn run() {
|
||||
let promise = Window::fetch_with_str("http://example.com/");
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
and you should be good to go!
|
||||
|
||||
### Type translations in `web-sys`
|
||||
|
||||
Most of the types specified in WebIDL have relatively straightforward
|
||||
translations into `web-sys`, but it's worth calling out a few in particular:
|
||||
|
||||
* `BufferSource` and `ArrayBufferView` - these two types show up in a number of
|
||||
APIs that generally deal with a buffer of bytes. We bind them in `web-sys`
|
||||
with two different types, `Object` and `&mut [u8]`. Using `Object` allows
|
||||
passing in arbitrary JS values which represent a view of bytes (like any typed
|
||||
array object), and `&mut [u8]` allows using a raw slice in Rust. Unfortunately
|
||||
we must pessimistically assume that JS will modify all slices as we don't
|
||||
currently have information of whether they're modified or not.
|
||||
|
||||
* Callbacks are all represented as `js_sys::Function`. This means that all
|
||||
callbacks going through `web-sys` are a raw JS value. You can work with this
|
||||
by either juggling actual `js_sys::Function` instances or you can create a
|
||||
`Closure<FnMut(...)>`, extract the underlying `JsValue` with `as_ref`, and
|
||||
then use `JsCast::unchecked_ref` to convert it to a `js_sys::Function`.
|
||||
|
||||
[api]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/
|
||||
[jsfetch]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
|
||||
[search-fetch]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/?search=fetch
|
||||
[fetchfn]: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
|
||||
[`fetch_with_str`]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_str
|
||||
|
103
src/closure.rs
103
src/closure.rs
@ -4,8 +4,6 @@
|
||||
//! closures" from Rust to JS. Some more details can be found on the `Closure`
|
||||
//! type itself.
|
||||
|
||||
#![allow(const_err)] // FIXME(rust-lang/rust#52603)
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
#[cfg(feature = "nightly")]
|
||||
use std::marker::Unsize;
|
||||
@ -70,14 +68,12 @@ use throw;
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Closure<T: ?Sized> {
|
||||
// Actually a `Rc` pointer, but in raw form so we can easily make copies.
|
||||
// See below documentation for why this is in an `Rc`.
|
||||
inner: *const UnsafeCell<Box<T>>,
|
||||
js: UnsafeCell<ManuallyDrop<JsValue>>,
|
||||
js: ManuallyDrop<JsValue>,
|
||||
_keep_this_data_alive: Rc<UnsafeCell<Box<T>>>,
|
||||
}
|
||||
|
||||
impl<T> Closure<T>
|
||||
where T: ?Sized,
|
||||
where T: ?Sized + WasmClosure,
|
||||
{
|
||||
/// Creates a new instance of `Closure` from the provided Rust closure.
|
||||
///
|
||||
@ -103,9 +99,73 @@ impl<T> Closure<T>
|
||||
///
|
||||
/// This is the function where the JS closure is manufactured.
|
||||
pub fn wrap(t: Box<T>) -> Closure<T> {
|
||||
let data = Rc::new(UnsafeCell::new(t));
|
||||
let ptr = &*data as *const UnsafeCell<Box<T>>;
|
||||
|
||||
// Here we need to create a `JsValue` with the data and `T::invoke()`
|
||||
// function pointer. To do that we... take a few unconventional turns.
|
||||
// In essence what happens here is this:
|
||||
//
|
||||
// 1. First up, below we call a function, `breaks_if_inlined`. This
|
||||
// function, as the name implies, does not work if it's inlined.
|
||||
// More on that in a moment.
|
||||
// 2. This function internally calls a special import recognized by the
|
||||
// `wasm-bindgen` CLI tool, `__wbindgen_describe_closure`. This
|
||||
// imported symbol is similar to `__wbindgen_describe` in that it's
|
||||
// not intended to show up in the final binary but it's an
|
||||
// intermediate state for a `wasm-bindgen` binary.
|
||||
// 3. The `__wbindgen_describe_closure` import is namely passed a
|
||||
// descriptor function, monomorphized for each invocation.
|
||||
//
|
||||
// Most of this doesn't actually make sense to happen at runtime! The
|
||||
// real magic happens when `wasm-bindgen` comes along and updates our
|
||||
// generated code. When `wasm-bindgen` runs it performs a few tasks:
|
||||
//
|
||||
// * First, it finds all functions that call
|
||||
// `__wbindgen_describe_closure`. These are all `breaks_if_inlined`
|
||||
// defined below as the symbol isn't called anywhere else.
|
||||
// * Next, `wasm-bindgen` executes the `breaks_if_inlined`
|
||||
// monomorphized functions, passing it dummy arguments. This will
|
||||
// execute the function just enough to invoke the special import,
|
||||
// namely telling us about the function pointer that is the describe
|
||||
// shim.
|
||||
// * This knowledge is then used to actually find the descriptor in the
|
||||
// function table which is then executed to figure out the signature
|
||||
// of the closure.
|
||||
// * Finally, and probably most heinously, the call to
|
||||
// `breaks_if_inlined` is rewritten to call an otherwise globally
|
||||
// imported function. This globally imported function will generate
|
||||
// the `JsValue` for this closure specialized for the signature in
|
||||
// question.
|
||||
//
|
||||
// Later on `wasm-gc` will clean up all the dead code and ensure that
|
||||
// we don't actually call `__wbindgen_describe_closure` at runtime. This
|
||||
// means we will end up not actually calling `breaks_if_inlined` in the
|
||||
// final binary, all calls to that function should be pruned.
|
||||
//
|
||||
// See crates/cli-support/src/js/closures.rs for a more information
|
||||
// about what's going on here.
|
||||
|
||||
extern fn describe<T: WasmClosure + ?Sized>() {
|
||||
inform(CLOSURE);
|
||||
T::describe()
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
unsafe fn breaks_if_inlined<T: WasmClosure + ?Sized>(
|
||||
ptr: usize,
|
||||
invoke: u32,
|
||||
) -> u32 {
|
||||
super::__wbindgen_describe_closure(ptr as u32, invoke, describe::<T> as u32)
|
||||
}
|
||||
|
||||
let idx = unsafe {
|
||||
breaks_if_inlined::<T>(ptr as usize, T::invoke_fn())
|
||||
};
|
||||
|
||||
Closure {
|
||||
inner: Rc::into_raw(Rc::new(UnsafeCell::new(t))),
|
||||
js: UnsafeCell::new(ManuallyDrop::new(JsValue { idx: !0 })),
|
||||
js: ManuallyDrop::new(JsValue { idx }),
|
||||
_keep_this_data_alive: data,
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +182,7 @@ impl<T> Closure<T>
|
||||
/// cleanup as it can.
|
||||
pub fn forget(self) {
|
||||
unsafe {
|
||||
let idx = (*self.js.get()).idx;
|
||||
let idx = self.js.idx;
|
||||
if idx != !0 {
|
||||
super::__wbindgen_cb_forget(idx);
|
||||
}
|
||||
@ -131,12 +191,17 @@ impl<T> Closure<T>
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> AsRef<JsValue> for Closure<T> {
|
||||
fn as_ref(&self) -> &JsValue {
|
||||
&self.js
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WasmDescribe for Closure<T>
|
||||
where T: WasmClosure + ?Sized,
|
||||
{
|
||||
fn describe() {
|
||||
inform(CLOSURE);
|
||||
T::describe();
|
||||
inform(ANYREF);
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,11 +212,7 @@ impl<'a, T> IntoWasmAbi for &'a Closure<T>
|
||||
type Abi = u32;
|
||||
|
||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||
unsafe {
|
||||
extra.push(T::invoke_fn());
|
||||
extra.push(self.inner as u32);
|
||||
&mut (*self.js.get()).idx as *const u32 as u32
|
||||
}
|
||||
(&*self.js).into_abi(extra)
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,11 +231,9 @@ impl<T> Drop for Closure<T>
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let idx = (*self.js.get()).idx;
|
||||
if idx != !0 {
|
||||
super::__wbindgen_cb_drop(idx);
|
||||
}
|
||||
drop(Rc::from_raw(self.inner));
|
||||
// this will implicitly drop our strong reference in addition to
|
||||
// invalidating all future invocations of the closure
|
||||
super::__wbindgen_cb_drop(self.js.idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -433,6 +433,7 @@ externs! {
|
||||
fn __wbindgen_cb_forget(idx: u32) -> ();
|
||||
|
||||
fn __wbindgen_describe(v: u32) -> ();
|
||||
fn __wbindgen_describe_closure(a: u32, b: u32, c: u32) -> u32;
|
||||
|
||||
fn __wbindgen_json_parse(ptr: *const u8, len: usize) -> u32;
|
||||
fn __wbindgen_json_serialize(idx: u32, ptr: *mut *mut u8) -> usize;
|
||||
|
Loading…
Reference in New Issue
Block a user