This commit is contained in:
Anton Danilkin 2018-08-13 18:59:52 +03:00
commit f35296f8ac
62 changed files with 1537 additions and 168 deletions

View File

@ -62,9 +62,9 @@ matrix:
- cargo test --target wasm32-unknown-unknown --features serde-serialize
# Make sure the `std` feature works if disabled
- cargo test --target wasm32-unknown-unknown -p no-std
# Make sure the `wasm-bindgen-futures` tests pass. Right now, this just
# verifies that the example program in the crate level docs compiles.
# Make sure the `wasm-bindgen-futures` tests pass.
- cargo test -p wasm-bindgen-futures
- cargo test -p wasm-bindgen-futures --target wasm32-unknown-unknown
addons:
firefox: latest
if: branch = master

View File

@ -55,6 +55,7 @@ members = [
"examples/comments",
"examples/console_log",
"examples/dom",
"examples/guide-supported-types-examples",
"examples/hello_world",
"examples/import_js",
"examples/julia_set",

View File

@ -19,6 +19,8 @@ pub struct Program {
pub structs: Vec<Struct>,
/// rust consts
pub consts: Vec<Const>,
/// rust submodules
pub modules: Vec<Module>,
}
/// A rust to js interface. Allows interaction with rust objects/functions
@ -239,6 +241,18 @@ pub enum ConstValue {
Null,
}
/// A rust module
///
/// This exists to give the ability to namespace js imports.
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
#[derive(Clone)]
pub struct Module {
pub vis: syn::Visibility,
pub name: Ident,
/// js -> rust interfaces
pub imports: Vec<Import>,
}
impl Program {
pub(crate) fn shared(&self) -> Result<shared::Program, Diagnostic> {
Ok(shared::Program {
@ -246,6 +260,8 @@ impl Program {
structs: self.structs.iter().map(|a| a.shared()).collect(),
enums: self.enums.iter().map(|a| a.shared()).collect(),
imports: self.imports.iter()
// add in imports from inside modules
.chain(self.modules.iter().flat_map(|m| m.imports.iter()))
.map(|a| a.shared())
.collect::<Result<_, Diagnostic>>()?,
version: shared::version(),
@ -365,7 +381,9 @@ impl ImportFunction {
/// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`)
fn infer_setter_property(&self) -> String {
let name = self.function.name.to_string();
assert!(name.starts_with("set_"), "setters must start with `set_`");
if !name.starts_with("set_") {
panic!("error: setters must start with `set_`, found: {}", name);
}
name[4..].to_string()
}

View File

@ -43,6 +43,8 @@ impl TryToTokens for ast::Program {
for i in self.imports.iter() {
DescribeImport(&i.kind).to_tokens(tokens);
// If there is a js namespace, check that name isn't a type. If it is,
// this import might be a method on that type.
if let Some(ns) = &i.js_namespace {
if types.contains(ns) && i.kind.fits_on_impl() {
let kind = match i.kind.try_to_token_stream() {
@ -67,6 +69,11 @@ impl TryToTokens for ast::Program {
for c in self.consts.iter() {
c.to_tokens(tokens);
}
for m in self.modules.iter() {
if let Err(e) = m.try_to_tokens(tokens) {
errors.push(e);
}
}
Diagnostic::from_vec(errors)?;
@ -87,6 +94,7 @@ impl TryToTokens for ast::Program {
// Each JSON blob is prepended with the length of the JSON blob so when
// all these sections are concatenated in the final wasm file we know
// how to extract all the JSON pieces, so insert the byte length here.
// The value is little-endian.
let generated_static_length = description.len() + 4;
let mut bytes = vec![
(description.len() >> 0) as u8,
@ -1103,6 +1111,30 @@ impl ToTokens for ast::Const {
}
}
impl<'a> TryToTokens for ast::Module {
fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
for import in &self.imports {
DescribeImport(&import.kind).to_tokens(tokens);
}
let vis = &self.vis;
let name = &self.name;
let mut errors = Vec::new();
let mut body = TokenStream::new();
for import in &self.imports {
if let Err(e) = import.kind.try_to_tokens(&mut body) {
errors.push(e);
}
}
Diagnostic::from_vec(errors)?;
(quote!{
#vis mod #name {
#body
}
}).to_tokens(tokens);
Ok(())
}
}
/// Emits the necessary glue tokens for "descriptor", generating an appropriate
/// symbol name as well as attributes around the descriptor function itself.
struct Descriptor<'a, T>(&'a Ident, T);

View File

@ -1868,24 +1868,28 @@ impl<'a, 'b> SubContext<'a, 'b> {
),
}
} else {
let location = if *is_static { "" } else { ".prototype" };
let (location, binding) = if *is_static {
("", format!(".bind({})", class))
} else {
(".prototype", "".into())
};
match kind {
shared::OperationKind::Regular => {
format!("{}{}.{}", class, location, import.function.name)
format!("{}{}.{}{}", class, location, import.function.name, binding)
}
shared::OperationKind::Getter(g) => {
self.cx.expose_get_inherited_descriptor();
format!(
"GetOwnOrInheritedPropertyDescriptor({}{}, '{}').get",
class, location, g,
"GetOwnOrInheritedPropertyDescriptor({}{}, '{}').get{}",
class, location, g, binding,
)
}
shared::OperationKind::Setter(s) => {
self.cx.expose_get_inherited_descriptor();
format!(
"GetOwnOrInheritedPropertyDescriptor({}{}, '{}').set",
class, location, s,
"GetOwnOrInheritedPropertyDescriptor({}{}, '{}').set{}",
class, location, s, binding,
)
}
shared::OperationKind::IndexingGetter => panic!("indexing getter should be structural"),

View File

@ -7,3 +7,6 @@ authors = ["The wasm-bindgen Developers"]
futures = "0.1.20"
js-sys = { path = "../js-sys", version = '0.2.0' }
wasm-bindgen = { path = "../..", version = '0.2.15' }
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = { path = '../test', version = '0.2.15' }

View File

@ -54,7 +54,7 @@
//! pub fn new() -> NextTick {
//! // Create a resolved promise that will run its callbacks on the next
//! // tick of the micro task queue.
//! let promise = js_sys::Promise::resolve(JsValue::NULL);
//! let promise = js_sys::Promise::resolve(&JsValue::NULL);
//! // Convert the promise into a `JsFuture`.
//! let inner = JsFuture::from(promise);
//! NextTick { inner }

51
crates/futures/tests/tests.rs Executable file
View File

@ -0,0 +1,51 @@
#![feature(use_extern_macros)]
#![cfg(target_arch = "wasm32")]
extern crate futures;
extern crate js_sys;
extern crate wasm_bindgen;
extern crate wasm_bindgen_futures;
extern crate wasm_bindgen_test;
use futures::Future;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::{future_to_promise, JsFuture};
use wasm_bindgen_test::*;
#[wasm_bindgen_test(async)]
fn promise_resolve_is_ok_future() -> impl Future<Item = (), Error = JsValue> {
let p = js_sys::Promise::resolve(&JsValue::from(42));
JsFuture::from(p)
.map(|x| {
assert_eq!(x, 42);
}).map_err(|_| unreachable!())
}
#[wasm_bindgen_test(async)]
fn promise_reject_is_error_future() -> impl Future<Item = (), Error = JsValue> {
let p = js_sys::Promise::reject(&JsValue::from(42));
JsFuture::from(p).map(|_| unreachable!()).or_else(|e| {
assert_eq!(e, 42);
Ok(())
})
}
#[wasm_bindgen_test(async)]
fn ok_future_is_resolved_promise() -> impl Future<Item = (), Error = JsValue> {
let f = futures::future::ok(JsValue::from(42));
let p = future_to_promise(f);
JsFuture::from(p)
.map(|x| {
assert_eq!(x, 42);
}).map_err(|_| unreachable!())
}
#[wasm_bindgen_test(async)]
fn error_future_is_rejected_promise() -> impl Future<Item = (), Error = JsValue> {
let f = futures::future::err(JsValue::from(42));
let p = future_to_promise(f);
JsFuture::from(p).map(|_| unreachable!()).or_else(|e| {
assert_eq!(e, 42);
Ok(())
})
}

View File

@ -31,17 +31,20 @@ use wasm_bindgen::prelude::*;
// * Keep imports in alphabetical order.
//
// * Rename imports with `js_name = ...` according to the note about `camelCase`
// and `snake_case` in the module's documentation above.
// and `snake_case` in the module's documentation above.
//
// * Include the one sentence summary of the import from the MDN link in the
// module's documentation above, and the MDN link itself.
// module's documentation above, and the MDN link itself.
//
// * If a function or method can throw an exception, make it catchable by adding
// `#[wasm_bindgen(catch)]`.
// `#[wasm_bindgen(catch)]`.
//
// * Add a new `#[test]` into the appropriate file in the
// `crates/js-sys/tests/wasm/` directory. If the imported function or method
// can throw an exception, make sure to also add test coverage for that case.
//
// * Arguments that are `JsValue`s or imported JavaScript types should be taken
// by reference.
#[wasm_bindgen]
extern "C" {
@ -125,6 +128,7 @@ extern "C" {
// Array
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type Array;
@ -246,6 +250,40 @@ extern "C" {
#[wasm_bindgen(method)]
pub fn map(this: &Array, predicate: &mut FnMut(JsValue, u32, Array) -> JsValue) -> Array;
/// The `Array.of()` method creates a new Array instance with a variable
/// number of arguments, regardless of number or type of the arguments.
///
/// The difference between `Array.of()` and the `Array` constructor is in the
/// handling of integer arguments: `Array.of(7)` creates an array with a single
/// element, `7`, whereas `Array(7)` creates an empty array with a `length`
/// property of `7` (Note: this implies an array of 7 empty slots, not slots
/// with actual undefined values).
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of
///
/// # Notes
///
/// There are a few bindings to `of` in `js-sys`: `of1`, `of2`, etc...
/// with different arities.
#[wasm_bindgen(static_method_of = Array, js_name = of)]
pub fn of1(a: &JsValue) -> Array;
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of
#[wasm_bindgen(static_method_of = Array, js_name = of)]
pub fn of2(a: &JsValue, b: &JsValue) -> Array;
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of
#[wasm_bindgen(static_method_of = Array, js_name = of)]
pub fn of3(a: &JsValue, b: &JsValue, c: &JsValue) -> Array;
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of
#[wasm_bindgen(static_method_of = Array, js_name = of)]
pub fn of4(a: &JsValue, b: &JsValue, c: &JsValue, d: &JsValue) -> Array;
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of
#[wasm_bindgen(static_method_of = Array, js_name = of)]
pub fn of5(a: &JsValue, b: &JsValue, c: &JsValue, d: &JsValue, e: &JsValue) -> Array;
/// The pop() method removes the last element from an array and returns that
/// element. This method changes the length of the array.
///
@ -347,6 +385,7 @@ extern "C" {
// ArrayBuffer
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type ArrayBuffer;
@ -421,6 +460,7 @@ extern "C" {
// Boolean
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type Boolean;
@ -440,6 +480,7 @@ extern "C" {
// DataView
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type DataView;
@ -587,6 +628,7 @@ extern "C" {
// Error
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type Error;
@ -622,9 +664,27 @@ extern "C" {
pub fn to_string(this: &Error) -> JsString;
}
// EvalError
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object, extends = Error)]
#[derive(Clone, Debug)]
pub type EvalError;
/// The EvalError object indicates an error regarding the global eval() function. This
/// exception is not thrown by JavaScript anymore, however the EvalError object remains for
/// compatibility.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError
#[wasm_bindgen(constructor)]
pub fn new(message: &str) -> EvalError;
}
// Float32Array
#[wasm_bindgen]
extern "C" {
// TODO Uncomment this once TypedArray is added:
// #[wasm_bindgen(extends = Object, extends = TypedArray)]
#[derive(Clone, Debug)]
pub type Float32Array;
@ -677,6 +737,8 @@ extern "C" {
// Float64Array
#[wasm_bindgen]
extern "C" {
// TODO Uncomment this once TypedArray is added:
// #[wasm_bindgen(extends = Object, extends = TypedArray)]
#[derive(Clone, Debug)]
pub type Float64Array;
@ -729,6 +791,7 @@ extern "C" {
// Function
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type Function;
@ -1007,6 +1070,7 @@ extern "C" {
// Map
#[wasm_bindgen]
extern {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type Map;
@ -1483,6 +1547,7 @@ extern "C" {
// Date.
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type Date;
@ -1825,6 +1890,37 @@ extern "C" {
#[derive(Clone, Debug)]
pub type Object;
/// The Object.assign() method is used to copy the values of all enumerable
/// own properties from one or more source objects to a target object. It
/// will return the target object.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
#[wasm_bindgen(static_method_of = Object)]
pub fn assign(target: &Object, source: &Object) -> Object;
/// The Object.assign() method is used to copy the values of all enumerable
/// own properties from one or more source objects to a target object. It
/// will return the target object.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
#[wasm_bindgen(static_method_of = Object, js_name = assign)]
pub fn assign2(target: &Object, source1: &Object, source2: &Object) -> Object;
/// The Object.assign() method is used to copy the values of all enumerable
/// own properties from one or more source objects to a target object. It
/// will return the target object.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
#[wasm_bindgen(static_method_of = Object, js_name = assign)]
pub fn assign3(target: &Object, source1: &Object, source2: &Object, source3: &Object) -> Object;
/// The Object.create() method creates a new object, using an existing
/// object to provide the newly created object's prototype.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
#[wasm_bindgen(static_method_of = Object)]
pub fn create(prototype: &Object) -> Object;
/// The `Object.freeze()` method freezes an object: that is, prevents new
/// properties from being added to it; prevents existing properties from
/// being removed; and prevents existing properties, or their enumerability,
@ -1988,6 +2084,44 @@ extern {
pub fn revocable(target: &JsValue, handler: &Object) -> Object;
}
// RangeError
#[wasm_bindgen]
extern {
/// The RangeError object indicates an error when a value is not in the set
/// or range of allowed values.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError
#[wasm_bindgen(extends = Error)]
#[derive(Clone, Debug)]
pub type RangeError;
/// The RangeError object indicates an error when a value is not in the set
/// or range of allowed values.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError
#[wasm_bindgen(constructor)]
pub fn new(message: &str) -> RangeError;
}
// ReferenceError
#[wasm_bindgen]
extern {
/// The ReferenceError object represents an error when a non-existent
/// variable is referenced.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError
#[wasm_bindgen(extends = Error)]
#[derive(Clone, Debug)]
pub type ReferenceError;
/// The ReferenceError object represents an error when a non-existent
/// variable is referenced.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError
#[wasm_bindgen(constructor)]
pub fn new(message: &str) -> ReferenceError;
}
// Reflect
#[wasm_bindgen]
extern "C" {
@ -2146,6 +2280,20 @@ extern {
#[wasm_bindgen(static_method_of = RegExp, getter)]
pub fn input() -> JsString;
/// The lastIndex is a read/write integer property of regular expression
/// instances that specifies the index at which to start the next match.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex
#[wasm_bindgen(structural, getter = lastIndex, method)]
pub fn last_index(this: &RegExp) -> u32;
/// The lastIndex is a read/write integer property of regular expression
/// instances that specifies the index at which to start the next match.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex
#[wasm_bindgen(structural, setter = lastIndex, method)]
pub fn set_last_index(this: &RegExp, index: u32);
/// The non-standard lastMatch property is a static and read-only
/// property of regular expressions that contains the last matched
/// characters. RegExp.$& is an alias for this property.
@ -2266,6 +2414,7 @@ extern {
// Set
#[wasm_bindgen]
extern {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type Set;
@ -2347,6 +2496,46 @@ extern {
pub fn values(set: &Set) -> Iterator;
}
// SyntaxError
#[wasm_bindgen]
extern {
/// A SyntaxError is thrown when the JavaScript engine encounters tokens or
/// token order that does not conform to the syntax of the language when
/// parsing code.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError
#[wasm_bindgen(extends = Error)]
#[derive(Clone, Debug)]
pub type SyntaxError;
/// A SyntaxError is thrown when the JavaScript engine encounters tokens or
/// token order that does not conform to the syntax of the language when
/// parsing code.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError
#[wasm_bindgen(constructor)]
pub fn new(message: &str) -> SyntaxError;
}
// TypeError
#[wasm_bindgen]
extern {
/// The TypeError object represents an error when a value is not of the
/// expected type.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError
#[wasm_bindgen(extends = Error)]
#[derive(Clone, Debug)]
pub type TypeError;
/// The TypeError object represents an error when a value is not of the
/// expected type.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError
#[wasm_bindgen(constructor)]
pub fn new(message: &str) -> TypeError;
}
// Uint8Array
#[wasm_bindgen]
extern "C" {
@ -2557,9 +2746,29 @@ extern "C" {
pub fn byte_offset(this: &Uint32Array) -> u32;
}
// URIError
#[wasm_bindgen]
extern {
/// The URIError object represents an error when a global URI handling
/// function was used in a wrong way.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError
#[wasm_bindgen(extends = Error, js_name = URIError)]
#[derive(Clone, Debug)]
pub type UriError;
/// The URIError object represents an error when a global URI handling
/// function was used in a wrong way.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError
#[wasm_bindgen(constructor, js_class = "URIError")]
pub fn new(message: &str) -> UriError;
}
// WeakMap
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type WeakMap;
@ -2603,6 +2812,7 @@ extern "C" {
// WeakSet
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = Object)]
#[derive(Clone, Debug)]
pub type WeakSet;
@ -2648,6 +2858,31 @@ extern "C" {
pub fn validate(bufferSource: &JsValue) -> Result<bool, JsValue>;
}
// JSON
#[wasm_bindgen]
extern "C" {
#[derive(Clone, Debug)]
pub type JSON;
/// The `JSON.parse()` method parses a JSON string, constructing the
/// JavaScript value or object described by the string.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
#[wasm_bindgen(catch, static_method_of = JSON)]
pub fn parse(text: &str) -> Result<JsValue, JsValue>;
/// The JSON.stringify() method converts a JavaScript value to a JSON string,
/// optionally replacing values if a replacer function is specified or
/// optionally including only the specified properties if a replacer array is
/// specified.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
#[wasm_bindgen(catch, static_method_of = JSON)]
pub fn stringify(obj: &JsValue) -> Result<JsString, JsValue>;
}
// JsString
#[wasm_bindgen]
extern "C" {
@ -3052,6 +3287,14 @@ extern "C" {
#[wasm_bindgen(method, js_name = toString)]
pub fn to_string(this: &Symbol) -> JsString;
/// The Symbol.unscopables well-known symbol is used to specify an object
/// value of whose own and inherited property names are excluded from the
/// with environment bindings of the associated object.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/unscopables
#[wasm_bindgen(static_method_of = Symbol, getter, structural)]
pub fn unscopables() -> Symbol;
/// The valueOf() method returns the primitive value of a Symbol object.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/valueOf
@ -3059,19 +3302,65 @@ extern "C" {
pub fn value_of(this: &Symbol) -> Symbol;
}
// Intl
#[wasm_bindgen]
extern "C" {
#[derive(Clone, Debug)]
pub type Intl;
#[allow(non_snake_case)]
pub mod Intl {
use super::*;
/// The `Intl.getCanonicalLocales()` method returns an array containing
/// the canonical locale names. Duplicates will be omitted and elements
/// will be validated as structurally valid language tags.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales
#[wasm_bindgen(static_method_of = Intl, js_name = getCanonicalLocales)]
pub fn get_canonical_locales(s: &JsValue) -> Array;
// Intl
#[wasm_bindgen]
extern "C" {
/// The `Intl.getCanonicalLocales()` method returns an array containing
/// the canonical locale names. Duplicates will be omitted and elements
/// will be validated as structurally valid language tags.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales
#[wasm_bindgen(js_name = getCanonicalLocales, js_namespace = Intl)]
pub fn get_canonical_locales(s: &JsValue) -> Array;
}
// Intl.Collator
#[wasm_bindgen]
extern "C" {
/// The Intl.Collator object is a constructor for collators, objects
/// that enable language sensitive string comparison.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator
#[wasm_bindgen(js_namespace = Intl)]
#[derive(Clone, Debug)]
pub type Collator;
/// The Intl.Collator object is a constructor for collators, objects
/// that enable language sensitive string comparison.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator
#[wasm_bindgen(constructor, js_namespace = Intl)]
pub fn new(locales: &Array, options: &Object) -> Collator;
/// The Intl.Collator.prototype.compare property returns a function that
/// compares two strings according to the sort order of this Collator
/// object.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator/compare
#[wasm_bindgen(method, getter, js_class = "Intl.Collator")]
pub fn compare(this: &Collator) -> Function;
/// The Intl.Collator.prototype.resolvedOptions() method returns a new
/// object with properties reflecting the locale and collation options
/// computed during initialization of this Collator object.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator/resolvedOptions
#[wasm_bindgen(method, js_namespace = Intl, js_name = resolvedOptions)]
pub fn resolved_options(this: &Collator) -> Object;
/// The Intl.Collator.supportedLocalesOf() method returns an array
/// containing those of the provided locales that are supported in
/// collation without having to fall back to the runtime's default
/// locale.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator/supportedLocalesOf
#[wasm_bindgen(static_method_of = Collator, js_namespace = Intl, js_name = supportedLocalesOf)]
pub fn supported_locales_of(locales: &Array, options: &Object) -> Array;
}
}
// Promise
@ -3109,7 +3398,7 @@ extern {
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
#[wasm_bindgen(static_method_of = Promise)]
pub fn all(obj: JsValue) -> Promise;
pub fn all(obj: &JsValue) -> Promise;
/// The `Promise.race(iterable)` method returns a promise that resolves or
/// rejects as soon as one of the promises in the iterable resolves or
@ -3117,14 +3406,14 @@ extern {
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
#[wasm_bindgen(static_method_of = Promise)]
pub fn race(obj: JsValue) -> Promise;
pub fn race(obj: &JsValue) -> Promise;
/// The `Promise.reject(reason)` method returns a `Promise` object that is
/// rejected with the given reason.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject
#[wasm_bindgen(static_method_of = Promise)]
pub fn reject(obj: JsValue) -> Promise;
pub fn reject(obj: &JsValue) -> Promise;
/// The `Promise.resolve(value)` method returns a `Promise` object that is
/// resolved with the given value. If the value is a promise, that promise
@ -3134,7 +3423,7 @@ extern {
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
#[wasm_bindgen(static_method_of = Promise)]
pub fn resolve(obj: JsValue) -> Promise;
pub fn resolve(obj: &JsValue) -> Promise;
/// The `catch()` method returns a `Promise` and deals with rejected cases
/// only. It behaves the same as calling `Promise.prototype.then(undefined,

View File

@ -126,6 +126,19 @@ fn copy_within() {
assert_eq!(to_rust(&characters)[5], JsValue::from(3));
}
#[wasm_bindgen_test]
fn of() {
let a = JsValue::from("a");
let b = JsValue::from("b");
let c = JsValue::from("c");
let arr = Array::of3(&a, &b, &c);
let vec = to_rust(&arr);
assert_eq!(vec.len(), 3);
assert_eq!(vec[0], a);
assert_eq!(vec[1], b);
assert_eq!(vec[2], c);
}
#[wasm_bindgen_test]
fn pop() {
let characters = js_array![8, 5, 4, 3, 1, 2];
@ -287,6 +300,7 @@ fn for_each() {
assert_eq!(sum_indices_of_evens(&js_array![1, 3, 5, 7]), 0);
assert_eq!(sum_indices_of_evens(&js_array![3, 5, 7, 10]), 3);
}
#[wasm_bindgen_test]
fn array_inheritance() {
let array = Array::new();

View File

@ -1,5 +1,6 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
@ -34,3 +35,10 @@ fn slice_with_end() {
let slice = buf.slice_with_end(1, 2);
assert!(JsValue::from(slice).is_object());
}
#[wasm_bindgen_test]
fn arraybuffer_inheritance() {
let buf = ArrayBuffer::new(4);
assert!(buf.is_instance_of::<ArrayBuffer>());
assert!(buf.is_instance_of::<Object>());
}

View File

@ -1,5 +1,6 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
@ -11,3 +12,10 @@ fn new_undefined() {
fn new_truely() {
assert_eq!(Boolean::new(&JsValue::from("foo")).value_of(), true);
}
#[wasm_bindgen_test]
fn boolean_inheritance() {
let b = Boolean::new(&JsValue::from(true));
assert!(b.is_instance_of::<Boolean>());
assert!(b.is_instance_of::<Object>());
}

View File

@ -1,5 +1,6 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
@ -37,3 +38,16 @@ fn test() {
// TODO: figure out how to do `bytes[2]`
bytes.subarray(2, 3).for_each(&mut |x, _, _| assert_eq!(x, 42));
}
#[wasm_bindgen_test]
fn dataview_inheritance() {
let bytes = Int8Array::new(&JsValue::from(10));
// TODO: figure out how to do `bytes[2] = 2`
bytes.subarray(2, 3).fill(2, 0, 1);
let v = DataView::new(&bytes.buffer(), 2, 8);
assert!(v.is_instance_of::<DataView>());
assert!(v.is_instance_of::<Object>());
}

View File

@ -1,5 +1,6 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
@ -406,3 +407,10 @@ fn value_of() {
let date = Date::new(&Date::utc(2018f64, 6f64).into());
assert_eq!(date.value_of(), 1530403200000.0);
}
#[wasm_bindgen_test]
fn date_inheritance() {
let date = Date::new(&"August 19, 1975 23:15:30".into());
assert!(date.is_instance_of::<Date>());
assert!(date.is_instance_of::<Object>());
}

View File

@ -1,5 +1,6 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
@ -35,3 +36,10 @@ fn to_string() {
error.set_name("error_name_1");
assert_eq!(JsValue::from(error.to_string()), "error_name_1: error message 1");
}
#[wasm_bindgen_test]
fn error_inheritance() {
let error = Error::new("test");
assert!(error.is_instance_of::<Error>());
assert!(error.is_instance_of::<Object>());
}

View File

@ -0,0 +1,55 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
// Note: This error is not thrown any more, so there are no tests that will generate this error.
// Instead we just have to manually construct it
#[wasm_bindgen_test]
fn new() {
let error = EvalError::new("some message");
let base_error: &Error = error.dyn_ref().unwrap();
assert_eq!(JsValue::from(base_error.message()), "some message");
}
#[wasm_bindgen_test]
fn set_message() {
let error = EvalError::new("test");
let base_error: &Error = error.dyn_ref().unwrap();
base_error.set_message("another");
assert_eq!(JsValue::from(base_error.message()), "another");
}
#[wasm_bindgen_test]
fn name() {
let error = EvalError::new("test");
let base_error: &Error = error.dyn_ref().unwrap();
assert_eq!(JsValue::from(base_error.name()), "EvalError");
}
#[wasm_bindgen_test]
fn set_name() {
let error = EvalError::new("test");
let base_error: &Error = error.dyn_ref().unwrap();
base_error.set_name("different");
assert_eq!(JsValue::from(base_error.name()), "different");
}
#[wasm_bindgen_test]
fn to_string() {
let error = EvalError::new("error message 1");
let base_error: &Error = error.dyn_ref().unwrap();
assert_eq!(JsValue::from(base_error.to_string()), "EvalError: error message 1");
base_error.set_name("error_name_1");
assert_eq!(JsValue::from(base_error.to_string()), "error_name_1: error message 1");
}
#[wasm_bindgen_test]
fn evalerror_inheritance() {
let error = EvalError::new("some message");
assert!(error.is_instance_of::<EvalError>());
assert!(error.is_instance_of::<Error>());
assert!(error.is_instance_of::<Object>());
}

View File

@ -1,5 +1,6 @@
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen]
@ -60,3 +61,9 @@ fn name() {
fn to_string() {
assert!(MAX.to_string().length() > 0);
}
#[wasm_bindgen_test]
fn function_inheritance() {
assert!(MAX.is_instance_of::<Function>());
assert!(MAX.is_instance_of::<Object>());
}

View File

@ -1,4 +1,4 @@
use wasm_bindgen::JsValue;
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_test::*;
use js_sys::*;
@ -23,3 +23,16 @@ fn get_canonical_locales() {
assert_eq!(l, "en-US");
});
}
#[wasm_bindgen_test]
fn collator() {
let locales = Array::of1(&JsValue::from("en-US"));
let opts = Object::new();
let c = Intl::Collator::new(&locales, &opts);
assert!(c.compare().is_instance_of::<Function>());
assert!(c.resolved_options().is_instance_of::<Object>());
let a = Intl::Collator::supported_locales_of(&locales, &opts);
assert!(a.is_instance_of::<Array>());
}

View File

@ -0,0 +1,84 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
fn parse_array() {
let js_array = JSON::parse("[1, 2, 3]").unwrap();;
assert!(Array::is_array(&js_array));
let array = Array::from(&js_array);
assert_eq!(array.length(), 3);
assert_eq!(array.pop(), 3);
assert_eq!(array.pop(), 2);
assert_eq!(array.pop(), 1);
}
#[wasm_bindgen_test]
fn parse_object() {
let js_object = JSON::parse("{\"x\": 5, \"y\": true, \"z\": [\"foo\", \"bar\"]}").unwrap();
assert!(js_object.is_object());
let obj = Object::from(js_object);
let keys = Object::keys(&obj);
assert_eq!(keys.length(), 3);
assert_eq!(keys.pop().as_string().unwrap(), "z");
assert_eq!(keys.pop().as_string().unwrap(), "y");
assert_eq!(keys.pop().as_string().unwrap(), "x");
let values = Object::values(&obj);
assert_eq!(values.length(), 3);
let z = values.pop();
assert!(Array::is_array(&z));
let z_array = Array::from(&z);
assert_eq!(z_array.length(), 2);
let y = values.pop();
assert_eq!(y.as_bool(), Some(true));
let x = values.pop();
assert!(Number::is_integer(&x));
let x_num = Number::new(&x);
assert_eq!(x_num.value_of(), 5.0);
}
#[wasm_bindgen_test]
fn parse_error() {
let js_object = JSON::parse("invalid json");
assert!(js_object.is_err());
let err = js_object.unwrap_err();
assert!(err.is_instance_of::<Error>());
}
#[wasm_bindgen_test]
fn stringify() {
let arr = Array::new();
arr.push(&JsValue::from(1));
arr.push(&JsValue::from(true));
arr.push(&JsValue::from("hello"));
let str = JSON::stringify(&JsValue::from(arr)).unwrap();
let rust_str: String = From::from(str);
assert_eq!(rust_str, "[1,true,\"hello\"]");
}
#[wasm_bindgen_test]
fn stringify_error() {
let func = Function::new_no_args("throw new Error(\"rust really rocks\")");
let obj = Object::new();
Reflect::set(obj.as_ref(), &JsValue::from("toJSON"), func.as_ref());
let result = JSON::stringify(&JsValue::from(obj));
assert!(result.is_err());
let err_obj = result.unwrap_err();
assert!(err_obj.is_instance_of::<Error>());
let err: &Error = err_obj.dyn_ref().unwrap();
let err_msg: String = From::from(err.message());
assert!(err_msg.contains("rust really rocks"));
}

View File

@ -1,4 +1,5 @@
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
@ -86,3 +87,10 @@ fn size() {
map.set(&"bar".into(), &"baz".into());
assert_eq!(map.size(), 2);
}
#[wasm_bindgen_test]
fn map_inheritance() {
let map = Map::new();
assert!(map.is_instance_of::<Map>());
assert!(map.is_instance_of::<Object>());
}

View File

@ -1,17 +1,10 @@
use std::f64::{INFINITY, NAN};
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
fn number_inheritance() {
let number = Number::new(&JsValue::from(10));
assert!(number.is_instance_of::<Number>());
assert!(number.is_instance_of::<Object>());
}
#[wasm_bindgen_test]
fn is_finite() {
assert!(Number::is_finite(&42.into()));
@ -112,3 +105,10 @@ fn to_exponential() {
assert_eq!(Number::new(&123456.into()).to_exponential(2).unwrap(), "1.23e+5");
assert!(Number::new(&10.into()).to_exponential(101).is_err());
}
#[wasm_bindgen_test]
fn number_inheritance() {
let n = Number::new(&JsValue::from(42));
assert!(n.is_instance_of::<Number>());
assert!(n.is_instance_of::<Object>());
}

View File

@ -1,17 +1,18 @@
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::*;
use std::f64::NAN;
use js_sys::*;
use std::f64::NAN;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_test::*;
#[wasm_bindgen]
extern {
extern "C" {
type Foo42;
#[wasm_bindgen(method, setter, structural)]
fn set_foo(this: &Foo42, val: JsValue);
}
#[wasm_bindgen(module = "tests/wasm/Object.js")]
extern {
extern "C" {
fn map_with_symbol_key() -> Object;
fn symbol_key() -> JsValue;
@ -36,6 +37,42 @@ fn new() {
assert!(JsValue::from(Object::new()).is_object());
}
#[wasm_bindgen_test]
fn assign() {
let a = JsValue::from("a");
let b = JsValue::from("b");
let c = JsValue::from("c");
let target = Object::new();
Reflect::set(target.as_ref(), a.as_ref(), a.as_ref());
let src1 = Object::new();
Reflect::set(src1.as_ref(), &a, &c);
let src2 = Object::new();
Reflect::set(src2.as_ref(), &b, &b);
let src3 = Object::new();
Reflect::set(src3.as_ref(), &c, &c);
let res = Object::assign3(&target, &src1, &src2, &src3);
assert!(Object::is(target.as_ref(), res.as_ref()));
assert_eq!(Reflect::get(target.as_ref(), &a), c);
assert_eq!(Reflect::get(target.as_ref(), &b), b);
assert_eq!(Reflect::get(target.as_ref(), &c), c);
}
#[wasm_bindgen_test]
fn create() {
let array_proto = eval("Array.prototype")
.unwrap()
.dyn_into::<Object>()
.unwrap();
let my_array = Object::create(&array_proto);
assert!(my_array.is_instance_of::<Array>());
}
#[wasm_bindgen_test]
fn has_own_property() {
assert!(foo_42().has_own_property(&"foo".into()));

View File

@ -0,0 +1,14 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
fn range_error() {
let error = RangeError::new("out of range yo");
assert!(error.is_instance_of::<RangeError>());
assert!(error.is_instance_of::<Error>());
let base: &Error = error.as_ref();
assert_eq!(JsValue::from(base.message()), "out of range yo");
}

View File

@ -0,0 +1,14 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
fn reference_error() {
let error = ReferenceError::new("bad reference, fool");
assert!(error.is_instance_of::<ReferenceError>());
assert!(error.is_instance_of::<Error>());
let base: &Error = error.as_ref();
assert_eq!(JsValue::from(base.message()), "bad reference, fool");
}

View File

@ -56,6 +56,15 @@ fn input() {
assert_eq!(RegExp::input(), "hi there!");
}
#[wasm_bindgen_test]
fn last_index() {
let re = RegExp::new("hi", "g");
assert_eq!(re.last_index(), 0);
re.set_last_index(42);
assert_eq!(re.last_index(), 42);
}
#[wasm_bindgen_test]
fn last_match() {
let re = RegExp::new("hi", "g");

View File

@ -1,5 +1,6 @@
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
fn set2vec(s: &Set) -> Vec<JsValue> {
@ -80,3 +81,10 @@ fn size() {
set.add(&3.into());
assert_eq!(set.size(), 3);
}
#[wasm_bindgen_test]
fn set_inheritance() {
let set = Set::new(&JsValue::undefined());
assert!(set.is_instance_of::<Set>());
assert!(set.is_instance_of::<Object>());
}

View File

@ -99,6 +99,11 @@ fn to_string() {
assert_eq!(gensym("desc".into()).to_string(), "Symbol(desc)");
}
#[wasm_bindgen_test]
fn unscopables() {
assert_eq!(Symbol::unscopables().to_string(), "Symbol(Symbol.unscopables)");
}
#[wasm_bindgen_test]
fn value_of() {
let a = Symbol::for_("foo");

View File

@ -0,0 +1,14 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
fn syntax_error() {
let error = SyntaxError::new("msg");
assert!(error.is_instance_of::<SyntaxError>());
assert!(error.is_instance_of::<Error>());
let base: &Error = error.as_ref();
assert_eq!(JsValue::from(base.message()), "msg");
}

View File

@ -0,0 +1,14 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
fn type_error() {
let error = TypeError::new("msg");
assert!(error.is_instance_of::<TypeError>());
assert!(error.is_instance_of::<Error>());
let base: &Error = error.as_ref();
assert_eq!(JsValue::from(base.message()), "msg");
}

View File

@ -0,0 +1,14 @@
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen_test]
fn uri_error() {
let error = UriError::new("msg");
assert!(error.is_instance_of::<UriError>());
assert!(error.is_instance_of::<Error>());
let base: &Error = error.as_ref();
assert_eq!(JsValue::from(base.message()), "msg");
}

View File

@ -1,5 +1,6 @@
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen]
@ -50,3 +51,10 @@ fn delete() {
map.delete(&key);
assert!(!map.has(&key));
}
#[wasm_bindgen_test]
fn weakmap_inheritance() {
let map = WeakMap::new();
assert!(map.is_instance_of::<WeakMap>());
assert!(map.is_instance_of::<Object>());
}

View File

@ -1,5 +1,6 @@
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::*;
use wasm_bindgen::JsCast;
use js_sys::*;
#[wasm_bindgen]
@ -40,3 +41,10 @@ fn delete() {
assert!(!set.has(&value));
assert!(!set.delete(&value));
}
#[wasm_bindgen_test]
fn weakset_inheritance() {
let set = WeakSet::new();
assert!(set.is_instance_of::<WeakSet>());
assert!(set.is_instance_of::<Object>());
}

View File

@ -14,22 +14,29 @@ pub mod Boolean;
pub mod DataView;
pub mod Date;
pub mod Error;
pub mod EvalError;
pub mod Function;
pub mod Generator;
pub mod Intl;
pub mod JsString;
pub mod JSON;
pub mod Map;
pub mod MapIterator;
pub mod Math;
pub mod Number;
pub mod Object;
pub mod Proxy;
pub mod RangeError;
pub mod ReferenceError;
pub mod Reflect;
pub mod RegExp;
pub mod Set;
pub mod SetIterator;
pub mod Symbol;
pub mod SyntaxError;
pub mod TypeError;
pub mod TypedArray;
pub mod UriError;
pub mod WeakMap;
pub mod WeakSet;
pub mod WebAssembly;

View File

@ -166,7 +166,7 @@ impl BindgenAttrs {
}).next()
}
/// Get the first js_name attribute
/// Get the first js_class attribute
fn js_class(&self) -> Option<&str> {
self.attrs
.iter()
@ -495,6 +495,10 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
_ => bail_span!(self, "return value of constructor must be a bare path"),
};
let class_name = extract_path_ident(class_name)?;
let class_name = opts
.js_class()
.map(Into::into)
.unwrap_or_else(|| class_name.to_string());
ast::ImportFunctionKind::Method {
class: class_name.to_string(),

View File

@ -13,6 +13,8 @@ use std::path;
use std::process::{self, Command};
fn main() {
env_logger::init();
if let Err(e) = try_main() {
eprintln!("Error: {}", e);
for c in e.iter_causes() {
@ -24,11 +26,9 @@ fn main() {
fn try_main() -> Result<(), failure::Error> {
println!("cargo:rerun-if-changed=build.rs");
env_logger::init();
println!("cargo:rerun-if-changed=webidls/enabled");
let entries = fs::read_dir("webidls/enabled").context("reading webidls/enabled directory")?;
let entries = fs::read_dir("webidls/enabled").context("reading webidls/enabled directory")?;
let mut source = SourceFile::default();
for entry in entries {
let entry = entry.context("getting webidls/enabled/*.webidl entry")?;
@ -38,8 +38,7 @@ fn try_main() -> Result<(), failure::Error> {
}
println!("cargo:rerun-if-changed={}", path.display());
source = source.add_file(&path)
.with_context(|_| format!("reading contents of file \"{}\"",
path.display()))?;
.with_context(|_| format!("reading contents of file \"{}\"", path.display()))?;
}
let bindings = match wasm_bindgen_webidl::compile(&source.contents) {
@ -70,9 +69,9 @@ fn try_main() -> Result<(), failure::Error> {
let status = Command::new("rustfmt")
.arg(&out_file_path)
.status()
.context("running rustfmt")?;
.context("running rustfmt")?;
if !status.success() {
bail!("rustfmt failed: {}", status)
bail!("rustfmt failed: {}", status)
}
}

View File

@ -0,0 +1,9 @@
use wasm_bindgen_test::*;
use wasm_bindgen::prelude::*;
use web_sys::console;
#[wasm_bindgen_test]
fn test_console() {
console::time("test label");
console::time_end("test label");
}

View File

@ -14,6 +14,7 @@ pub mod anchor_element;
pub mod body_element;
pub mod br_element;
pub mod button_element;
pub mod console;
pub mod div_element;
pub mod element;
pub mod event;

View File

@ -8,5 +8,6 @@ pub mod array;
pub mod array_buffer;
pub mod consts;
pub mod enums;
pub mod namespace;
pub mod simple;
pub mod throws;

View File

@ -0,0 +1,9 @@
global.math_test = {
pow(base, exp) {
return Math.pow(base, exp);
},
add_one(val) {
return val + 1;
},
};

View File

@ -0,0 +1,10 @@
use wasm_bindgen_test::*;
include!(concat!(env!("OUT_DIR"), "/namespace.rs"));
#[wasm_bindgen_test]
fn simple_namespace_test() {
assert_eq!(math_test::add_one(1), 2);
assert_eq!(math_test::pow(1.0, 100.0), 1.0);
assert_eq!(math_test::pow(10.0, 2.0), 100.0);
}

4
crates/webidl-tests/namespace.webidl vendored Normal file
View File

@ -0,0 +1,4 @@
namespace math_test {
long add_one(long val);
double pow(double base, double exponent);
};

View File

@ -12,7 +12,8 @@ use std::collections::{BTreeMap, BTreeSet};
use weedle::argument::Argument;
use weedle::attribute::ExtendedAttribute;
use weedle::interface::StringifierOrStatic;
use weedle::mixin::MixinMembers;
use weedle::mixin::MixinMember;
use weedle::namespace::NamespaceMember;
use weedle;
use super::Result;
@ -28,6 +29,7 @@ pub(crate) struct FirstPassRecord<'src> {
/// The mixins, mapping their name to the webidl ast node for the mixin.
pub(crate) mixins: BTreeMap<&'src str, MixinData<'src>>,
pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>,
pub(crate) namespaces: BTreeMap<&'src str, NamespaceData<'src>>,
pub(crate) includes: BTreeMap<&'src str, BTreeSet<&'src str>>,
}
@ -46,7 +48,16 @@ pub(crate) struct InterfaceData<'src> {
pub(crate) struct MixinData<'src> {
/// Whether only partial mixins were encountered
pub(crate) partial: bool,
pub(crate) members: Vec<&'src MixinMembers<'src>>,
pub(crate) members: Vec<&'src MixinMember<'src>>,
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
}
/// We need to collect namespace data during the first pass, to be used later.
#[derive(Default)]
pub(crate) struct NamespaceData<'src> {
/// Whether only partial namespaces were encountered
pub(crate) partial: bool,
pub(crate) members: Vec<&'src NamespaceMember<'src>>,
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
}
@ -94,6 +105,8 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> {
PartialInterface(interface) => interface.first_pass(record, ()),
InterfaceMixin(mixin) => mixin.first_pass(record, ()),
PartialInterfaceMixin(mixin) => mixin.first_pass(record, ()),
Namespace(namespace) => namespace.first_pass(record, ()),
PartialNamespace(namespace) => namespace.first_pass(record, ()),
Typedef(typedef) => typedef.first_pass(record, ()),
_ => {
// Other definitions aren't currently used in the first pass
@ -147,9 +160,16 @@ impl<'src> FirstPass<'src, ()> for weedle::IncludesStatementDefinition<'src> {
}
}
#[derive(Clone, Copy)]
enum FirstPassOperationType {
Interface,
Mixin,
Namespace,
}
fn first_pass_operation<'src>(
record: &mut FirstPassRecord<'src>,
mixin: bool,
first_pass_operation_type: FirstPassOperationType,
self_name: &'src str,
id: OperationId<'src>,
arguments: &[Argument<'src>],
@ -161,22 +181,32 @@ fn first_pass_operation<'src>(
Argument::Variadic(variadic) => names.push(variadic.identifier.0),
}
}
if mixin {
&mut record
.mixins
.get_mut(self_name)
.expect(&format!("not found {} mixin", self_name))
.operations
} else {
&mut record
.interfaces
.get_mut(self_name)
.expect(&format!("not found {} interface", self_name))
.operations
match first_pass_operation_type{
FirstPassOperationType::Interface => {
&mut record
.interfaces
.get_mut(self_name)
.expect(&format!("not found {} interface", self_name))
.operations
},
FirstPassOperationType::Mixin => {
&mut record
.mixins
.get_mut(self_name)
.expect(&format!("not found {} mixin", self_name))
.operations
},
FirstPassOperationType::Namespace => {
&mut record
.namespaces
.get_mut(self_name)
.expect(&format!("not found {} namesace", self_name))
.operations
},
}
.entry(id)
.and_modify(|operation_data| operation_data.overloaded = true)
.or_insert_with(Default::default)
.or_default()
.argument_names_same
.entry(names)
.and_modify(|same_argument_names| *same_argument_names = true)
@ -195,7 +225,7 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
let interface_data = record
.interfaces
.entry(self.identifier.0)
.or_insert_with(Default::default);
.or_default();
interface_data.partial = false;
interface_data.superclass = self.inheritance.map(|s| s.identifier.0);
}
@ -246,7 +276,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => {
first_pass_operation(
record,
false,
FirstPassOperationType::Interface,
self_name,
OperationId::Constructor,
&list.args.body.list,
@ -255,7 +285,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => {
first_pass_operation(
record,
false,
FirstPassOperationType::Interface,
self_name,
OperationId::Constructor,
&[],
@ -266,7 +296,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
{
first_pass_operation(
record,
false,
FirstPassOperationType::Interface,
self_name,
OperationId::Constructor,
&list.args.body.list,
@ -312,15 +342,15 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM
}
first_pass_operation(
record,
false,
FirstPassOperationType::Interface,
self_name,
match self.identifier.map(|s| s.0) {
None => match self.specials.get(0) {
None => OperationId::Operation(None),
Some(weedle::interface::Special::Getter(weedle::term::Getter)) => OperationId::IndexingGetter,
Some(weedle::interface::Special::Setter(weedle::term::Setter)) => OperationId::IndexingSetter,
Some(weedle::interface::Special::Deleter(weedle::term::Deleter)) => OperationId::IndexingDeleter,
Some(weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller)) => return Ok(()),
Some(weedle::interface::Special::Getter(_)) => OperationId::IndexingGetter,
Some(weedle::interface::Special::Setter(_)) => OperationId::IndexingSetter,
Some(weedle::interface::Special::Deleter(_)) => OperationId::IndexingDeleter,
Some(weedle::interface::Special::LegacyCaller(_)) => return Ok(()),
},
Some(ref name) => OperationId::Operation(Some(name.clone())),
},
@ -341,7 +371,7 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{
.entry(self.identifier.0)
.or_insert_with(Default::default);
mixin_data.partial = false;
mixin_data.members.push(&self.members.body);
mixin_data.members.extend(&self.members.body);
}
for member in &self.members.body {
@ -369,7 +399,7 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src>
},
)
.members
.push(&self.members.body);
.extend(&self.members.body);
for member in &self.members.body {
member.first_pass(record, self.identifier.0)?;
@ -402,7 +432,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s
}
first_pass_operation(
record,
true,
FirstPassOperationType::Mixin,
self_name,
OperationId::Operation(self.identifier.map(|s| s.0.clone())),
&self.args.body.list,
@ -424,6 +454,82 @@ impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> {
}
}
impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(())
}
record
.namespaces
.entry(self.identifier.0)
.and_modify(|namespace_data| namespace_data.partial = false)
.or_insert_with(||
NamespaceData {
partial: true,
members: Default::default(),
operations: Default::default(),
},
)
.members
.extend(&self.members.body);
for member in &self.members.body {
member.first_pass(record, self.identifier.0)?;
}
Ok(())
}
}
impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(())
}
record
.namespaces
.entry(self.identifier.0)
.or_default()
.members
.extend(&self.members.body);
for member in &self.members.body {
member.first_pass(record, self.identifier.0)?;
}
Ok(())
}
}
impl<'src> FirstPass<'src, &'src str> for weedle::namespace::NamespaceMember<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
match self {
weedle::namespace::NamespaceMember::Operation(op) => {
op.first_pass(record, self_name)
}
_ => Ok(()),
}
}
}
impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(())
}
first_pass_operation(
record,
FirstPassOperationType::Namespace,
self_name,
OperationId::Operation(self.identifier.map(|s| s.0.clone())),
&self.args.body.list,
)
}
}
impl<'a> FirstPassRecord<'a> {
pub fn all_superclasses<'me>(&'me self, interface: &str)
-> impl Iterator<Item = String> + 'me

View File

@ -37,9 +37,9 @@ use std::path::Path;
use backend::TryToTokens;
use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports};
use backend::util::{ident_ty, rust_ident, wrap_import_function};
use backend::util::{ident_ty, rust_ident, raw_ident, wrap_import_function};
use failure::ResultExt;
use heck::{ShoutySnakeCase};
use heck::{ShoutySnakeCase, SnakeCase};
use proc_macro2::{Ident, Span};
use weedle::argument::Argument;
use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList};
@ -175,24 +175,24 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> {
weedle::Definition::PartialInterface(interface) => {
interface.webidl_parse(program, first_pass, ())?
}
weedle::Definition::Typedef(_) |
weedle::Definition::InterfaceMixin(_) |
weedle::Definition::PartialInterfaceMixin(_) => {
// handled in the first pass
}
weedle::Definition::IncludesStatement(..) => {
| weedle::Definition::Typedef(_)
| weedle::Definition::InterfaceMixin(_)
| weedle::Definition::PartialInterfaceMixin(_)
| weedle::Definition::IncludesStatement(..)
| weedle::Definition::PartialNamespace(..)=> {
// handled in the first pass
}
weedle::Definition::Implements(..) => {
// nothing to do for this, ignore it
}
weedle::Definition::Namespace(namespace) => {
namespace.webidl_parse(program, first_pass, ())?
}
// TODO
weedle::Definition::Callback(..)
| weedle::Definition::Callback(..)
| weedle::Definition::CallbackInterface(..)
| weedle::Definition::Dictionary(..)
| weedle::Definition::PartialDictionary(..)
| weedle::Definition::Namespace(..)
| weedle::Definition::PartialNamespace(..) => {
| weedle::Definition::PartialDictionary(..) => {
warn!("Unsupported WebIDL definition: {:?}", self)
}
}
@ -250,10 +250,8 @@ impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> {
mixin_name: &str,
) -> Result<()> {
if let Some(mixin_data) = first_pass.mixins.get(mixin_name) {
for members in &mixin_data.members {
for member in *members {
member.webidl_parse(program, first_pass, self_name)?;
}
for member in &mixin_data.members {
member.webidl_parse(program, first_pass, self_name)?;
}
}
if let Some(mixin_names) = first_pass.includes.get(mixin_name) {
@ -316,6 +314,7 @@ impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for Extend
arguments,
&::first_pass::OperationId::Constructor,
interface.identifier.0,
false,
);
let self_ty = ident_ty(rust_ident(camel_case_ident(interface.identifier.0).as_str()));
@ -566,16 +565,16 @@ fn member_attribute<'src>(
let is_structural = util::is_structural(attrs);
let throws = util::throws(attrs);
for import_function in first_pass.create_getter(
identifier,
&type_.type_,
self_name,
is_static,
is_structural,
throws,
) {
program.imports.push(wrap_import_function(import_function));
}
for import_function in first_pass.create_getter(
identifier,
&type_.type_,
self_name,
is_static,
is_structural,
throws,
) {
program.imports.push(wrap_import_function(import_function));
}
if !readonly {
for import_function in first_pass.create_setter(
@ -666,10 +665,10 @@ fn member_operation<'src>(
match identifier.map(|s| s.0) {
None if specials.is_empty() => ::first_pass::OperationId::Operation(None),
None if specials.len() == 1 => match specials[0] {
weedle::interface::Special::Getter(weedle::term::Getter) => ::first_pass::OperationId::IndexingGetter,
weedle::interface::Special::Setter(weedle::term::Setter) => ::first_pass::OperationId::IndexingSetter,
weedle::interface::Special::Deleter(weedle::term::Deleter) => ::first_pass::OperationId::IndexingDeleter,
weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller) => return Ok(()),
weedle::interface::Special::Getter(_) => ::first_pass::OperationId::IndexingGetter,
weedle::interface::Special::Setter(_) => ::first_pass::OperationId::IndexingSetter,
weedle::interface::Special::Deleter(_) => ::first_pass::OperationId::IndexingDeleter,
weedle::interface::Special::LegacyCaller(_) => return Ok(()),
},
Some(ref name) if specials.is_empty() => ::first_pass::OperationId::Operation(Some(name.clone())),
_ => {
@ -757,8 +756,8 @@ impl<'src> WebidlParse<'src, ()> for weedle::EnumDefinition<'src> {
variants: variants
.iter()
.map(|v| {
if !v.0.is_empty() {
rust_ident(camel_case_ident(&v.0).as_str())
if !v.0.is_empty() {
rust_ident(camel_case_ident(&v.0).as_str())
} else {
rust_ident("None")
}
@ -777,14 +776,14 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src>
fn webidl_parse(
&'src self,
program: &mut backend::ast::Program,
record: &FirstPassRecord<'src>,
first_pass: &FirstPassRecord<'src>,
self_name: &'src str,
) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
let idl_type = match self.const_type.to_idl_type(record) {
let idl_type = match self.const_type.to_idl_type(first_pass) {
None => return Ok(()),
Some(idl_type) => idl_type,
};
@ -805,3 +804,100 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src>
Ok(())
}
}
impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> {
fn webidl_parse(
&'src self,
program: &mut backend::ast::Program,
first_pass: &FirstPassRecord<'src>,
(): (),
) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
if let Some(attrs) = &self.attributes {
for attr in &attrs.body.list {
attr.webidl_parse(program, first_pass, self)?;
}
}
let mut module = backend::ast::Module {
vis: public(),
name: rust_ident(self.identifier.0.to_snake_case().as_str()),
imports: Default::default(),
};
if let Some(namespace_data) = first_pass.namespaces.get(&self.identifier.0) {
for member in &namespace_data.members {
member.webidl_parse(program, first_pass, (&self.identifier.0, &mut module))?;
}
}
program.modules.push(module);
Ok(())
}
}
impl<'src> WebidlParse<'src, &'src weedle::NamespaceDefinition<'src>> for ExtendedAttribute<'src> {
fn webidl_parse(
&'src self,
_program: &mut backend::ast::Program,
_first_pass: &FirstPassRecord<'src>,
_namespace: &'src weedle::NamespaceDefinition<'src>,
) -> Result<()> {
warn!("Unsupported WebIDL extended attribute: {:?}", self);
Ok(())
}
}
impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::NamespaceMember<'src> {
fn webidl_parse(
&'src self,
program: &mut backend::ast::Program,
first_pass: &FirstPassRecord<'src>,
(self_name, module): (&'src str, &mut backend::ast::Module),
) -> Result<()> {
match self {
weedle::namespace::NamespaceMember::Operation(op) => {
op.webidl_parse(program, first_pass, (self_name, module))?;
}
weedle::namespace::NamespaceMember::Attribute(_) => {
warn!("Attribute namespace members are not supported")
}
}
Ok(())
}
}
impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::OperationNamespaceMember<'src> {
fn webidl_parse(
&'src self,
_program: &mut backend::ast::Program,
first_pass: &FirstPassRecord<'src>,
(self_name, module): (&'src str, &mut backend::ast::Module),
) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
for import_function in first_pass.create_namespace_operation(
&self.args.body.list,
self.identifier.as_ref().map(|id| id.0),
&self.return_type,
self_name,
util::throws(&self.attributes)
) {
module.imports.push(
backend::ast::Import {
module: None,
js_namespace: Some(raw_ident(self_name)),
kind: backend::ast::ImportKind::Function(import_function),
}
);
};
Ok(())
}
}

View File

@ -11,7 +11,7 @@ use weedle::attribute::{ExtendedAttributeList, ExtendedAttribute};
use weedle::argument::Argument;
use weedle::literal::{ConstValue, FloatLit, IntegerLit};
use first_pass::FirstPassRecord;
use first_pass::{self, FirstPassRecord};
use idl_type::{IdlType, ToIdlType, flatten};
/// Take a type and create an immutable shared reference to that type.
@ -367,7 +367,7 @@ impl<'src> FirstPassRecord<'src> {
pub fn create_basic_method(
&self,
arguments: &[weedle::argument::Argument],
operation_id: ::first_pass::OperationId,
operation_id: first_pass::OperationId,
return_type: &weedle::types::ReturnType,
self_name: &str,
is_static: bool,
@ -378,20 +378,21 @@ impl<'src> FirstPassRecord<'src> {
arguments,
&operation_id,
self_name,
false,
);
let name = match &operation_id {
::first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
::first_pass::OperationId::Operation(name) => match name {
first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
first_pass::OperationId::Operation(name) => match name {
None => {
warn!("Operations without a name are unsupported");
return Vec::new();
}
Some(name) => name.to_string(),
},
::first_pass::OperationId::IndexingGetter => "get".to_string(),
::first_pass::OperationId::IndexingSetter => "set".to_string(),
::first_pass::OperationId::IndexingDeleter => "delete".to_string(),
first_pass::OperationId::IndexingGetter => "get".to_string(),
first_pass::OperationId::IndexingSetter => "set".to_string(),
first_pass::OperationId::IndexingDeleter => "delete".to_string(),
};
let kind = backend::ast::ImportFunctionKind::Method {
@ -400,11 +401,11 @@ impl<'src> FirstPassRecord<'src> {
kind: backend::ast::MethodKind::Operation(backend::ast::Operation {
is_static,
kind: match &operation_id {
::first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
::first_pass::OperationId::Operation(_) => backend::ast::OperationKind::Regular,
::first_pass::OperationId::IndexingGetter => backend::ast::OperationKind::IndexingGetter,
::first_pass::OperationId::IndexingSetter => backend::ast::OperationKind::IndexingSetter,
::first_pass::OperationId::IndexingDeleter => backend::ast::OperationKind::IndexingDeleter,
first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
first_pass::OperationId::Operation(_) => backend::ast::OperationKind::Regular,
first_pass::OperationId::IndexingGetter => backend::ast::OperationKind::IndexingGetter,
first_pass::OperationId::IndexingSetter => backend::ast::OperationKind::IndexingSetter,
first_pass::OperationId::IndexingDeleter => backend::ast::OperationKind::IndexingDeleter,
},
}),
};
@ -415,17 +416,17 @@ impl<'src> FirstPassRecord<'src> {
};
let doc_comment = match &operation_id {
::first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
::first_pass::OperationId::Operation(_) => Some(
first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
first_pass::OperationId::Operation(_) => Some(
format!(
"The `{}()` method\n\n{}",
name,
mdn_doc(self_name, Some(&name))
)
),
::first_pass::OperationId::IndexingGetter => Some("The indexing getter\n\n".to_string()),
::first_pass::OperationId::IndexingSetter => Some("The indexing setter\n\n".to_string()),
::first_pass::OperationId::IndexingDeleter => Some("The indexing deleter\n\n".to_string()),
first_pass::OperationId::IndexingGetter => Some("The indexing getter\n\n".to_string()),
first_pass::OperationId::IndexingSetter => Some("The indexing setter\n\n".to_string()),
first_pass::OperationId::IndexingDeleter => Some("The indexing deleter\n\n".to_string()),
};
let arguments = match self.convert_arguments(arguments) {
@ -451,23 +452,24 @@ impl<'src> FirstPassRecord<'src> {
pub fn get_operation_overloading(
&self,
arguments: &[weedle::argument::Argument],
id: &::first_pass::OperationId,
operation_id: &first_pass::OperationId,
self_name: &str,
namespace: bool,
) -> (bool, bool) {
fn get_operation_data<'src>(
record: &'src FirstPassRecord,
id: &'src ::first_pass::OperationId,
operation_id: &'src ::first_pass::OperationId,
self_name: &str,
mixin_name: &str,
) -> Option<&'src ::first_pass::OperationData<'src>> {
if let Some(mixin_data) = record.mixins.get(mixin_name) {
if let Some(operation_data) = mixin_data.operations.get(id) {
if let Some(operation_data) = mixin_data.operations.get(operation_id) {
return Some(operation_data);
}
}
if let Some(mixin_names) = record.includes.get(mixin_name) {
for mixin_name in mixin_names {
if let Some(operation_data) = get_operation_data(record, id, self_name, mixin_name) {
if let Some(operation_data) = get_operation_data(record, operation_id, self_name, mixin_name) {
return Some(operation_data);
}
}
@ -475,20 +477,28 @@ impl<'src> FirstPassRecord<'src> {
None
}
let operation_data = self
.interfaces
.get(self_name)
.and_then(|interface_data| interface_data.operations.get(id))
.unwrap_or_else(||
get_operation_data(self, id, self_name, self_name)
.expect(&format!("not found operation {:?} in interface {}", id, self_name))
);
let operation_data = if !namespace {
self
.interfaces
.get(self_name)
.and_then(|interface_data| interface_data.operations.get(operation_id))
.unwrap_or_else(||
get_operation_data(self, operation_id, self_name, self_name)
.expect(&format!("not found operation {:?} in interface {}", operation_id, self_name))
)
} else {
self
.namespaces
.get(self_name)
.and_then(|interface_data| interface_data.operations.get(operation_id))
.expect(&format!("not found operation {:?} in namespace {}", operation_id, self_name))
};
let mut names = Vec::with_capacity(arguments.len());
for arg in arguments {
match arg {
Argument::Single(arg) => names.push(arg.identifier.0),
Argument::Variadic(_) => return (false, false),
for argument in arguments {
match argument {
Argument::Single(single) => names.push(single.identifier.0),
Argument::Variadic(variadic) => names.push(variadic.identifier.0),
}
}
(
@ -500,6 +510,62 @@ impl<'src> FirstPassRecord<'src> {
)
}
/// Create a wasm-bindgen operation (free function with no `self` type), if possible.
pub fn create_namespace_operation(
&self,
arguments: &[weedle::argument::Argument],
operation_name: Option<&str>,
return_type: &weedle::types::ReturnType,
self_name: &str,
catch: bool,
) -> Vec<backend::ast::ImportFunction> {
let (overloaded, same_argument_names) = self.get_operation_overloading(
arguments,
&first_pass::OperationId::Operation(operation_name),
self_name,
true,
);
let name = match operation_name {
Some(name) => name.to_string(),
None => {
warn!("Operations without a name are unsupported");
return Vec::new();
}
};
let ret = match return_type.to_idl_type(self) {
None => return Vec::new(),
Some(idl_type) => idl_type,
};
let doc_comment = Some(
format!(
"The `{}.{}()` function\n\n{}",
self_name,
name,
mdn_doc(self_name, Some(&name))
)
);
let arguments = match self.convert_arguments(arguments) {
None => return Vec::new(),
Some(arguments) => arguments
};
self.create_function(
&name,
overloaded,
same_argument_names,
&arguments,
ret,
backend::ast::ImportFunctionKind::Normal,
false,
catch,
doc_comment,
)
}
/// Create a wasm-bindgen getter method, if possible.
pub fn create_getter(
&self,

View File

@ -0,0 +1,3 @@
package-lock.json
guide_supported_types_examples.js
guide_supported_types_examples_bg.wasm

View File

@ -0,0 +1,10 @@
[package]
name = "guide-supported-types-examples"
version = "0.1.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = { path = "../.." }

View File

@ -0,0 +1,61 @@
# Adding Numbers
[View this example online](https://webassembly.studio/?f=612vwsrmwft)
This directory is an example of using the `#[wasm_bindgen]` macro to simply add
two numbers. The neat part about this is that it's an example of how to generate
the smallest wasm-bindgen binary.
You can build the example with:
```
$ ./build.sh
```
(or running the commands on Windows manually)
Currently this generates a 651 byte wasm binary:
```
$ ls -alh add_bg.wasm
-rw-rw-r-- 1 alex alex 651 Apr 20 22:16 add_bg.wasm
```
If you run [wasm-opt], a C++ tool for optimize WebAssembly, you can make it even
smaller too!
```
$ wasm-opt -Os add_bg.wasm -o add.wasm
$ ls -alh add.wasm
-rw-rw-r-- 1 alex alex 100 Apr 20 22:19 add.wasm
```
And sure enough, using the [wasm2wat] tool it's quite small!
```
$ wasm2wat add.wasm
(module
(type (;0;) (func (param i32 i32) (result i32)))
(func (;0;) (type 0) (param i32 i32) (result i32)
get_local 1
get_local 0
i32.add)
(memory (;0;) 2)
(export "memory" (memory 0))
(export "add" (func 0))
(data (i32.const 1545) "invalid malloc request"))
```
Note that it's important to point out that the size reductions here are because
the wasm is compiled in release mode by the build script and this crate's
workspace has the following configuration
```toml
[profile.release]
lto = true
opt-level = 's'
panic = 'abort'
```
[wasm2wat]: https://github.com/webassembly/wabt
[wasm-opt]: https://github.com/webassembly/binaryen

View File

@ -0,0 +1,2 @@
import * as imported_types from './imported_types.js';
import * as exported_types from './exported_types.js';

View File

@ -0,0 +1,12 @@
#!/bin/sh
# For more coments about what's going on here, see the `hello_world` example
set -ex
cargo +nightly build --target wasm32-unknown-unknown --release
cargo +nightly run --manifest-path ../../crates/cli/Cargo.toml \
--bin wasm-bindgen -- \
../../target/wasm32-unknown-unknown/release/guide_supported_types_examples.wasm --out-dir .
npm install
npm run serve

View File

@ -0,0 +1 @@
console.log("todo")

View File

@ -0,0 +1,26 @@
import {
imported_type_by_value,
imported_type_by_shared_ref,
return_imported_type,
take_option_imported_type,
return_option_imported_type,
} from './guide_supported_types_examples';
imported_type_by_value(new SomeJsType());
imported_type_by_shared_ref(new SomeJsType());
let x = return_imported_type();
console.log(x instanceof SomeJsType);
// true
take_option_imported_type(null);
take_option_imported_type(undefined);
take_option_imported_type(new SomeJsType());
let y = return_option_imported_type();
if (y == null) {
// ...
} else {
console.log(y instanceof SomeJsType);
// true
}

View File

@ -0,0 +1,8 @@
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head>
<body>
<script src='./index.js'></script>
</body>
</html>

View File

@ -0,0 +1,5 @@
// For more comments about what's going on here, check out the `hello_world`
// example
import('./bootstrap').then(() => {
console.log("done");
});

View File

@ -0,0 +1,10 @@
{
"scripts": {
"serve": "webpack-dev-server"
},
"devDependencies": {
"webpack": "^4.11.1",
"webpack-cli": "^2.0.10",
"webpack-dev-server": "^3.1.0"
}
}

View File

@ -0,0 +1,6 @@
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RustType {
inner: u32,
}

View File

@ -0,0 +1,25 @@
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
pub type SomeJsType;
}
#[wasm_bindgen]
pub fn imported_type_by_value(x: SomeJsType) { /* ... */ }
#[wasm_bindgen]
pub fn imported_type_by_shared_ref(x: &SomeJsType) { /* ... */ }
#[wasm_bindgen]
pub fn return_imported_type() -> SomeJsType {
unimplemented!()
}
#[wasm_bindgen]
pub fn take_option_imported_type(x: Option<SomeJsType>) { /* ... */ }
#[wasm_bindgen]
pub fn return_option_imported_type() -> Option<SomeJsType> {
unimplemented!()
}

View File

@ -0,0 +1,7 @@
#![feature(use_extern_macros)]
#![allow(unused_variables, dead_code)]
extern crate wasm_bindgen;
pub mod imported_types;
pub mod exported_types;

View File

@ -0,0 +1,10 @@
const path = require('path');
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
},
mode: 'development'
};

View File

@ -89,7 +89,7 @@ pub fn receive_example_from_js(val: &JsValue) {
## JavaScript Usage
In the `JsValue` that JavaScript gets, `fied1` will be an `Object` (not a
In the `JsValue` that JavaScript gets, `field1` will be an `Object` (not a
JavaScript `Map`), `field2` will be a JavaScript `Array` whose members are
`Array`s of numbers, and `field3` will be an `Array` of numbers.

View File

@ -1,21 +1,122 @@
# Supported Types
# Supported Rust Types and their JavaScript Representations
The table below provides an overview of all the types that `wasm-bindgen` can
send and receive across the WebAssembly ABI boundary.
This section provides an overview of all the types that `wasm-bindgen` can send
and receive across the WebAssembly ABI boundary, and how they translate into
JavaScript.
| Type | `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value |
|:---:|:---:|:---:|:---:|:---:|:---:|
| Imported `extern Whatever;` JavaScript Types | Yes | Yes | Yes | Yes | Yes | Yes |
| Exported `struct Whatever` Rust Types | Yes | Yes | Yes | Yes | Yes | Yes |
| `str` | No | Yes | No | Yes | Yes | No |
| `String` | Yes | No | No | Yes | Yes | Yes |
| `char` | Yes | No | No | Yes | No | No |
| `bool` | Yes | No | No | Yes | No | No |
| `JsValue` | Yes | Yes | Yes | Yes | No | No |
| `Box<[JsValue]>` | Yes | No | No | Yes | Yes | yes |
| `*const T` | Yes | No | No | Yes | No | No |
| `*mut T` | Yes | No | No | Yes | No | No |
| `u8` `i8` `u16` `i16` `u64` `i64` `isize` `size` | Yes | No | No | Yes | No | No |
| `u32` `i32` `f32` `f64` | Yes | Yes | Yes | Yes | No | No |
| `Box<[u8]>` `Box<[i8]>` `Box<[u16]>` `Box<[i16]>` `Box<[u32]>` `Box<[i32]>` `Box<[u64]>` `Box<[i64]>` `Box<[f32]>` `Box<[f64]`> | Yes | No | No | Yes | Yes | Yes |
| `[u8]` `[i8]` `[u16]` `[i16]` `[u32]` `[i32]` `[u64]` `[i64]` `[f32]` `[f64]` | No | Yes | Yes | No | Yes | No |
## Imported `extern Whatever;` JavaScript Types
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | Yes | No | Yes | Yes | Yes | Instances of the extant `Whatever` JavaScript class / prototype constructor |
### Example Rust Usage
```rust
{{#include ../../../examples/guide-supported-types-examples/src/imported_types.rs}}
```
### Example JavaScript Usage
```js
{{#include ../../../examples/guide-supported-types-examples/imported_types.js}}
```
## Exported `struct Whatever` Rust Types
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | Yes | Yes | Yes | Yes | Yes | Instances of a `wasm-bindgen`-generated JavaScript `class Whatever { ... }` |
### Example Rust Usage
```rust
{{#include ../../../examples/guide-supported-types-examples/src/exported_types.rs}}
```
### Example JavaScript Usage
```js
{{#include ../../../examples/guide-supported-types-examples/exported_types.js}}
```
## `str`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| No | Yes | No | Yes | Yes | No | JavaScript string value |
Copies the string's contents back and forth between the JavaScript
garbage-collected heap and the Wasm linear memory with `TextDecoder` and
`TextEncoder`. If you don't want to perform this copy, and would rather work
with handles to JavaScript string values, use the `js_sys::JsString` type.
## `String`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | No | No | Yes | Yes | Yes | JavaScript string value |
Copies the string's contents back and forth between the JavaScript
garbage-collected heap and the Wasm linear memory with `TextDecoder` and
`TextEncoder`
## `char`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | No | No | Yes | No | No | A JavaScript string value |
## `bool`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | No | No | Yes | No | No | A JavaScript boolean value |
## `wasm_bindgen::JsValue`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | Yes | Yes | Yes | No | No | Any JavaScript value |
## `Box<[JsValue]>`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | No | No | Yes | Yes | Yes | A JavaScript `Array` object |
## `*const T` `*mut T`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | No | No | Yes | No | No | A JavaScript number value |
## `u8` `i8` `u16` `i16` `u64` `i64` `isize` `size`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | No | No | Yes | No | No | A JavaScript number value |
## `u32` `i32` `f32` `f64`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | Yes | Yes | Yes | No | No | A JavaScript number value |
## `Box<[u8]>` `Box<[i8]>` `Box<[u16]>` `Box<[i16]>` `Box<[u32]>` `Box<[i32]>` `Box<[u64]>` `Box<[i64]>` `Box<[f32]>` `Box<[f64]>`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| Yes | No | No | Yes | Yes | Yes | A JavaScript `TypedArray` view of the Wasm memory for the boxed slice of the appropriate type (`Int32Array`, `Uint8Array`, etc) |
Note that this does ***not*** copy the whole slice of memory back and forth into
the JavaScript heap from the Wasm linear memory.
## `[u8]` `[i8]` `[u16]` `[i16]` `[u32]` `[i32]` `[u64]` `[i64]` `[f32]` `[f64]`
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| No | Yes | Yes | No | Yes | No | A JavaScript `TypedArray` view of the Wasm memory for the boxed slice of the appropriate type (`Int32Array`, `Uint8Array`, etc) |
Note that this does ***not*** copy the whole slice of memory back and forth into
the JavaScript heap from the Wasm linear memory.

View File

@ -26,7 +26,7 @@ cargo test
## The Web IDL Frontend's Tests
```
cargo test -p wasm-bindgen-webidl
cargo test -p webidl-tests --target wasm32-unknown-unknown
```
## The Macro UI Tests

View File

@ -790,7 +790,7 @@ pub mod __rt {
/// above. That means if this function is called and referenced we'll pull
/// in the object file and link the intrinsics.
///
/// Note that this is an #[inline] function to remove the function call
/// Note that this is an `#[inline]` function to remove the function call
/// overhead we inject in functions, but right now it's unclear how to do
/// this in a zero-cost fashion. The lowest cost seems to be generating a
/// store that can't be optimized away (to a global), which is listed below.