diff --git a/Cargo.toml b/Cargo.toml index 7cd2b5e87..40ff56ac9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,8 @@ test = false doctest = false [features] -default = ["spans", "std"] +default = ["spans", "std", "js_globals"] +js_globals = [] spans = ["wasm-bindgen-macro/spans"] std = [] serde-serialize = ["serde", "serde_json", "std"] diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a397aa4b1..7e1b3634f 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -266,6 +266,7 @@ impl ToTokens for ast::StructField { } #[no_mangle] + #[doc(hidden)] pub extern fn #desc() { use wasm_bindgen::describe::*; <#ty as WasmDescribe>::describe(); @@ -445,6 +446,7 @@ impl ToTokens for ast::Export { // binary along with anything it references. #[no_mangle] #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + #[doc(hidden)] pub extern fn #descriptor_name() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -727,6 +729,7 @@ impl<'a> ToTokens for DescribeImport<'a> { (quote! { #[no_mangle] #[allow(non_snake_case)] + #[doc(hidden)] pub extern fn #describe_name() { use wasm_bindgen::describe::*; inform(FUNCTION); diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 300d2a151..7ed67ad20 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -29,7 +29,7 @@ pub struct Context<'a> { pub imported_names: HashSet, pub exported_classes: HashMap, pub function_table_needed: bool, - pub run_descriptor: &'a Fn(&str) -> Vec, + pub run_descriptor: &'a Fn(&str) -> Option>, pub module_versions: Vec<(String, String)>, } @@ -474,7 +474,10 @@ impl<'a> Context<'a> { for field in class.fields.iter() { let wasm_getter = shared::struct_field_get(name, &field.name); let wasm_setter = shared::struct_field_set(name, &field.name); - let descriptor = self.describe(&wasm_getter); + let descriptor = match self.describe(&wasm_getter) { + None => continue, + Some(d) => d, + }; let set = { let mut cx = Js2Rust::new(&field.name, self); @@ -1393,10 +1396,9 @@ impl<'a> Context<'a> { Ok(()) } - fn describe(&self, name: &str) -> Descriptor { + fn describe(&self, name: &str) -> Option { let name = format!("__wbindgen_describe_{}", name); - let ret = (self.run_descriptor)(&name); - Descriptor::decode(&ret) + (self.run_descriptor)(&name).map(|d| Descriptor::decode(&d)) } fn global(&mut self, s: &str) { @@ -1474,7 +1476,12 @@ impl<'a, 'b> SubContext<'a, 'b> { if let Some(ref class) = export.class { return self.generate_export_for_class(class, export); } - let descriptor = self.cx.describe(&export.function.name); + + let descriptor = match self.cx.describe(&export.function.name) { + None => return Ok(()), + Some(d) => d, + }; + let (js, ts) = Js2Rust::new(&export.function.name, self.cx) .process(descriptor.unwrap_function())? .finish("function", &format!("wasm.{}", export.function.name)); @@ -1492,7 +1499,12 @@ impl<'a, 'b> SubContext<'a, 'b> { export: &shared::Export, ) -> Result<(), Error> { let wasm_name = shared::struct_function_export_name(class_name, &export.function.name); - let descriptor = self.cx.describe(&wasm_name); + + let descriptor = match self.cx.describe(&wasm_name) { + None => return Ok(()), + Some(d) => d, + }; + let (js, ts) = Js2Rust::new(&export.function.name, self.cx) .method(export.method) .process(descriptor.unwrap_function())? @@ -1603,7 +1615,10 @@ impl<'a, 'b> SubContext<'a, 'b> { import: &shared::ImportFunction) -> Result<(), Error> { - let descriptor = self.cx.describe(&import.shim); + let descriptor = match self.cx.describe(&import.shim) { + None => return Ok(()), + Some(d) => d, + }; let target = match &import.method { Some(shared::MethodData { class, kind, getter, setter }) => { diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index 8f80a0870..092f8f2ce 100644 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -140,11 +140,19 @@ impl Bindgen { module_versions: Default::default(), run_descriptor: &|name| { let mut v = MyExternals(Vec::new()); - let ret = instance - .invoke_export(name, &[], &mut v) - .expect("failed to run export"); - assert!(ret.is_none()); - v.0 + match instance.invoke_export(name, &[], &mut v) { + Ok(None) => Some(v.0), + Ok(Some(_)) => { + unreachable!( + "there is only one export, and we only return None from it" + ) + }, + // Allow missing exported describe functions. This can + // happen when a nested dependency crate exports things + // but the root crate doesn't use them. + Err(wasmi::Error::Function(_)) => None, + Err(e) => panic!("unexpected error running descriptor: {}", e), + } }, }; for program in programs.iter() { @@ -342,6 +350,7 @@ impl wasmi::ImportResolver for MyResolver { } struct MyExternals(Vec); + #[derive(Debug)] struct MyError(String); diff --git a/crates/webidl/tests/expected/Event.rs b/crates/webidl/tests/expected/Event.rs index 51807c4db..2bcf0b3fe 100644 --- a/crates/webidl/tests/expected/Event.rs +++ b/crates/webidl/tests/expected/Event.rs @@ -55,6 +55,7 @@ impl From for ::wasm_bindgen::JsValue { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_new_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -104,6 +105,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_event_phase_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -147,6 +149,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_stop_propagation_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -186,6 +189,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_stop_immediate_propagation_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -225,6 +229,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_bubbles_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -268,6 +273,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_cancelable_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -311,6 +317,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_prevent_default_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -350,6 +357,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_default_prevented_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -393,6 +401,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_default_prevented_by_chrome_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -436,6 +445,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_default_prevented_by_content_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -479,6 +489,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_composed_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -522,6 +533,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_is_trusted_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -565,6 +577,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_time_stamp_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -608,6 +621,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_init_event_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -661,6 +675,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_cancel_bubble_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -704,6 +719,7 @@ impl Event { } #[no_mangle] #[allow(non_snake_case)] +#[doc(hidden)] pub extern "C" fn __wbindgen_describe___widl_f_set_cancel_bubble_Event() { use wasm_bindgen::describe::*; inform(FUNCTION); @@ -753,4 +769,4 @@ pub type DOMHighResTimeStamp = f64; pub type EventInit = bool; #[allow(non_upper_case_globals)] #[wasm_custom_section = "__wasm_bindgen_unstable"] -const __WASM_BINDGEN_GENERATED_wasm_bindgen_webidl_0_2_11_0 : [ u8 ; 4451usize ] = * b"_\x11\0\0{\"exports\":[],\"enums\":[],\"imports\":[{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"type\"}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_new_Event\",\"catch\":false,\"method\":false,\"js_new\":true,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"new\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_event_phase_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"eventPhase\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"eventPhase\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_stop_propagation_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"stopPropagation\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_stop_immediate_propagation_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"stopImmediatePropagation\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_bubbles_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"bubbles\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"bubbles\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_cancelable_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"cancelable\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"cancelable\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_prevent_default_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"preventDefault\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_default_prevented_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"defaultPrevented\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"defaultPrevented\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_default_prevented_by_chrome_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"defaultPreventedByChrome\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"defaultPreventedByChrome\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_default_prevented_by_content_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"defaultPreventedByContent\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"defaultPreventedByContent\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_composed_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"composed\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"composed\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_is_trusted_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"isTrusted\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"isTrusted\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_time_stamp_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"timeStamp\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"timeStamp\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_init_event_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"initEvent\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_cancel_bubble_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"cancelBubble\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"cancelBubble\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_set_cancel_bubble_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":\"cancelBubble\",\"class\":\"Event\",\"function\":{\"name\":\"set_CancelBubble\"}}}],\"structs\":[],\"version\":\"0.2.11 (0cd767c9d)\",\"schema_version\":\"4\"}" ; +const __WASM_BINDGEN_GENERATED_wasm_bindgen_webidl_0_2_11_0 : [ u8 ; 4451usize ] = * b"_\x11\0\0{\"exports\":[],\"enums\":[],\"imports\":[{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"type\"}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_new_Event\",\"catch\":false,\"method\":false,\"js_new\":true,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"new\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_event_phase_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"eventPhase\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"eventPhase\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_stop_propagation_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"stopPropagation\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_stop_immediate_propagation_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"stopImmediatePropagation\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_bubbles_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"bubbles\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"bubbles\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_cancelable_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"cancelable\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"cancelable\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_prevent_default_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"preventDefault\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_default_prevented_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"defaultPrevented\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"defaultPrevented\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_default_prevented_by_chrome_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"defaultPreventedByChrome\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"defaultPreventedByChrome\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_default_prevented_by_content_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"defaultPreventedByContent\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"defaultPreventedByContent\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_composed_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"composed\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"composed\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_is_trusted_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"isTrusted\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"isTrusted\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_time_stamp_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"timeStamp\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"timeStamp\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_init_event_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"initEvent\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_cancel_bubble_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":\"cancelBubble\",\"setter\":null,\"class\":\"Event\",\"function\":{\"name\":\"cancelBubble\"}}},{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"function\",\"shim\":\"__widl_f_set_cancel_bubble_Event\",\"catch\":false,\"method\":true,\"js_new\":false,\"structural\":false,\"getter\":null,\"setter\":\"cancelBubble\",\"class\":\"Event\",\"function\":{\"name\":\"set_CancelBubble\"}}}],\"structs\":[],\"version\":\"0.2.11 (161fce9d5)\",\"schema_version\":\"4\"}" ; diff --git a/src/js.rs b/src/js.rs new file mode 100644 index 000000000..98e57928e --- /dev/null +++ b/src/js.rs @@ -0,0 +1,88 @@ +//! Bindings to JavaScript's standard, built-in objects, including their methods +//! and properties. +//! +//! This does *not* include any Web, Node, or any other JS environment +//! APIs. Only the things that are guaranteed to exist in the global scope by +//! the ECMAScript standard. +//! +//! https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects +//! +//! ## A Note About `camelCase`, `snake_case`, and Naming Conventions +//! +//! JavaScript's global objects use `camelCase` naming conventions for functions +//! and methods, but Rust style is to use `snake_case`. These bindings expose +//! the Rust style `snake_case` name. Additionally, acronyms within a method +//! name are all lower case, where as in JavaScript they are all upper case. For +//! example, `decodeURI` in JavaScript is exposed as `decode_uri` in these +//! bindings. + +use wasm_bindgen_macro::*; +use JsValue; +if_std! { + use std::prelude::v1::*; +} + +// When adding new imports: +// +// * 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. +// +// * Include the one sentence summary of the import from the MDN link in the +// 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)]`. +// +// * Add a new `#[test]` to the `tests/all/js_globals.rs` file. If the imported +// function or method can throw an exception, make sure to also add test +// coverage for that case. + +#[wasm_bindgen] +extern { + /// The `decodeURI()` function decodes a Uniform Resource Identifier (URI) + /// previously created by `encodeURI` or by a similar routine. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI + #[cfg(feature = "std")] + #[wasm_bindgen(catch, js_name = decodeURI)] + pub fn decode_uri(encoded: &str) -> Result; + + /// The `encodeURI()` function encodes a Uniform Resource Identifier (URI) + /// by replacing each instance of certain characters by one, two, three, or + /// four escape sequences representing the UTF-8 encoding of the character + /// (will only be four escape sequences for characters composed of two + /// "surrogate" characters). + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI + #[cfg(feature = "std")] + #[wasm_bindgen(js_name = encodeURI)] + pub fn encode_uri(decoded: &str) -> String; + + /// The `eval()` function evaluates JavaScript code represented as a string. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval + #[wasm_bindgen(catch)] + pub fn eval(js_source_text: &str) -> Result; +} + +// Object. +#[wasm_bindgen] +extern { + pub type Object; + + /// The Object constructor creates an object wrapper. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object + #[wasm_bindgen(constructor)] + pub fn new() -> Object; + + /// The `hasOwnProperty()` method returns a boolean indicating whether the + /// object has the specified property as its own property (as opposed to + /// inheriting it). + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty + #[wasm_bindgen(method, js_name = hasOwnProperty)] + pub fn has_own_property(this: &Object, property: &str) -> bool; +} diff --git a/src/lib.rs b/src/lib.rs index 055e7e3be..c1f3b266f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ //! interface. #![feature(use_extern_macros, wasm_import_module, try_reserve, unsize)] +#![cfg_attr(feature = "js_globals", feature(proc_macro, wasm_custom_section))] #![no_std] #[cfg(feature = "serde-serialize")] @@ -43,6 +44,13 @@ pub mod prelude { pub mod convert; pub mod describe; +#[cfg(feature = "js_globals")] +pub mod js; + +#[cfg(feature = "js_globals")] +mod wasm_bindgen { + pub use super::*; +} if_std! { extern crate std; @@ -67,6 +75,18 @@ const JSIDX_FALSE: u32 = 6; const JSIDX_RESERVED: u32 = 8; impl JsValue { + /// The `null` JS value constant. + pub const NULL: JsValue = JsValue { idx: JSIDX_NULL }; + + /// The `undefined` JS value constant. + pub const UNDEFINED: JsValue = JsValue { idx: JSIDX_UNDEFINED }; + + /// The `true` JS value constant. + pub const TRUE: JsValue = JsValue { idx: JSIDX_TRUE }; + + /// The `false` JS value constant. + pub const FALSE: JsValue = JsValue { idx: JSIDX_FALSE }; + /// Creates a new JS value which is a string. /// /// The utf-8 string provided is copied to the JS heap and the string will diff --git a/tests/all/js_globals/Object.rs b/tests/all/js_globals/Object.rs new file mode 100755 index 000000000..0c2f8311b --- /dev/null +++ b/tests/all/js_globals/Object.rs @@ -0,0 +1,56 @@ +#![allow(non_snake_case)] + +use project; + +#[test] +fn new() { + project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section)] + + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + use wasm_bindgen::js; + + #[wasm_bindgen] + pub fn new_object() -> js::Object { + js::Object::new() + } + "#) + .file("test.ts", r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + assert.equal(typeof wasm.new_object(), "object"); + } + "#) + .test() +} + +#[test] +fn has_own_property() { + project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section)] + + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + use wasm_bindgen::js; + + #[wasm_bindgen] + pub fn has_own_foo_property(obj: &js::Object) -> bool { + obj.has_own_property("foo") + } + "#) + .file("test.ts", r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + assert.ok(wasm.has_own_foo_property({ foo: 42 })); + assert.ok(!wasm.has_own_foo_property({})); + } + "#) + .test() +} diff --git a/tests/all/js_globals/mod.rs b/tests/all/js_globals/mod.rs new file mode 100644 index 000000000..a7eaaeebd --- /dev/null +++ b/tests/all/js_globals/mod.rs @@ -0,0 +1,73 @@ +// Keep these tests in alphabetical order, just like the imports in `src/js.rs`. + +use super::project; + +mod Object; + +#[test] +#[cfg(feature = "std")] +fn decode_uri() { + project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section)] + + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + use wasm_bindgen::js; + + #[wasm_bindgen] + pub fn test() { + let x = js::decode_uri("https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B") + .ok() + .expect("should decode URI OK"); + assert_eq!(x, "https://mozilla.org/?x=шеллы"); + + assert!(js::decode_uri("%E0%A4%A").is_err()); + } + "#) + .test(); +} + +#[test] +#[cfg(feature = "std")] +fn encode_uri() { + project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section)] + + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + use wasm_bindgen::js; + + #[wasm_bindgen] + pub fn test() { + let x = js::encode_uri("ABC abc 123"); + assert_eq!(x, "ABC%20abc%20123"); + } + "#) + .test(); +} + +#[test] +fn eval() { + project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section)] + + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + use wasm_bindgen::js; + + #[wasm_bindgen] + pub fn test() { + let x = js::eval("42").ok().expect("should eval OK"); + assert_eq!(x.as_f64().unwrap(), 42.0); + + let err = js::eval("(function () { throw 42; }())") + .err() + .expect("eval should throw"); + assert_eq!(err.as_f64().unwrap(), 42.0); + } + "#) + .test(); +} diff --git a/tests/all/main.rs b/tests/all/main.rs index b909788a9..4f88bad8a 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -528,6 +528,7 @@ mod enums; mod import_class; mod imports; mod jsobjects; +#[cfg(feature = "js_globals")] mod js_globals; mod math; mod node; mod non_debug;