From 4b1107719ee2be84516cd63c295fb0a322a1ac2c Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 5 Dec 2024 11:58:40 +0100 Subject: [PATCH 1/6] Support undeclared static imports --- CHANGELOG.md | 7 ++- crates/cli-support/src/js/mod.rs | 17 ++++- crates/cli-support/src/wit/mod.rs | 7 ++- crates/cli-support/src/wit/nonstandard.rs | 2 +- crates/cli/tests/reference/static.d.ts | 3 + crates/cli/tests/reference/static.js | 77 +++++++++++++++++++++++ crates/cli/tests/reference/static.rs | 24 +++++++ crates/cli/tests/reference/static.wat | 16 +++++ crates/js-sys/src/lib.rs | 35 +++++------ guide/src/reference/static-js-objects.md | 14 +++++ 10 files changed, 173 insertions(+), 29 deletions(-) create mode 100644 crates/cli/tests/reference/static.d.ts create mode 100644 crates/cli/tests/reference/static.js create mode 100644 crates/cli/tests/reference/static.rs create mode 100644 crates/cli/tests/reference/static.wat diff --git a/CHANGELOG.md b/CHANGELOG.md index dece4a6063d..7181e5310c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,14 +8,17 @@ * Add support for multi-threading in Node.js. [#4318](https://github.com/rustwasm/wasm-bindgen/pull/4318) -### Changed - * Add clear error message to communicate new feature resolver version requirements. [#4312](https://github.com/rustwasm/wasm-bindgen/pull/4312) * Remove `once_cell/critical-section` requirement for `no_std` with atomics. [#4322](https://github.com/rustwasm/wasm-bindgen/pull/4322) +### Changed + +* `static FOO: Option` now returns `None` if undeclared in JS instead of throwing an error in JS. + [#4319](https://github.com/rustwasm/wasm-bindgen/pull/4319) + ### Fixed * Fix macro-hygiene for calls to `std::thread_local!`. diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 21139dddab3..f08bd05190a 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -2730,7 +2730,7 @@ __wbg_set_wasm(wasm);" | AuxImport::Value(AuxValue::Setter(js, ..)) | AuxImport::ValueWithThis(js, ..) | AuxImport::Instanceof(js) - | AuxImport::Static(js) + | AuxImport::Static { js, .. } | AuxImport::StructuralClassGetter(js, ..) | AuxImport::StructuralClassSetter(js, ..) | AuxImport::IndexingGetterOfClass(js) @@ -3265,11 +3265,22 @@ __wbg_set_wasm(wasm);" Ok("result".to_owned()) } - AuxImport::Static(js) => { + AuxImport::Static { js, optional } => { assert!(kind == AdapterJsImportKind::Normal); assert!(!variadic); assert_eq!(args.len(), 0); - self.import_name(js) + let js = self.import_name(js)?; + + if *optional { + writeln!( + prelude, + "const result = typeof {js} === 'undefined' ? null : {js};" + ) + .unwrap(); + Ok("result".to_owned()) + } else { + Ok(js) + } } AuxImport::String(string) => { diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index 2f1d7f52691..07fb5090a78 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -788,6 +788,7 @@ impl<'a> Context<'a> { None => return Ok(()), Some(d) => d, }; + let optional = matches!(descriptor, Descriptor::Option(_)); // Register the signature of this imported shim let id = self.import_adapter( @@ -803,8 +804,10 @@ impl<'a> Context<'a> { // And then save off that this function is is an instanceof shim for an // imported item. - let import = self.determine_import(import, static_.name)?; - self.aux.import_map.insert(id, AuxImport::Static(import)); + let js = self.determine_import(import, static_.name)?; + self.aux + .import_map + .insert(id, AuxImport::Static { js, optional }); Ok(()) } diff --git a/crates/cli-support/src/wit/nonstandard.rs b/crates/cli-support/src/wit/nonstandard.rs index 35f30a74497..1e9579dc8ba 100644 --- a/crates/cli-support/src/wit/nonstandard.rs +++ b/crates/cli-support/src/wit/nonstandard.rs @@ -233,7 +233,7 @@ pub enum AuxImport { /// This import is expected to be a shim that returns the JS value named by /// `JsImport`. - Static(JsImport), + Static { js: JsImport, optional: bool }, /// This import is expected to be a shim that returns an exported `JsString`. String(String), diff --git a/crates/cli/tests/reference/static.d.ts b/crates/cli/tests/reference/static.d.ts new file mode 100644 index 00000000000..091dbcc51a6 --- /dev/null +++ b/crates/cli/tests/reference/static.d.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +/* eslint-disable */ +export function exported(): void; diff --git a/crates/cli/tests/reference/static.js b/crates/cli/tests/reference/static.js new file mode 100644 index 00000000000..1f9b56f3b69 --- /dev/null +++ b/crates/cli/tests/reference/static.js @@ -0,0 +1,77 @@ +let wasm; +export function __wbg_set_wasm(val) { + wasm = val; +} + + +function isLikeNone(x) { + return x === undefined || x === null; +} + +function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_export_1.set(idx, obj); + return idx; +} + +const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; + +let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +let cachedUint8ArrayMemory0 = null; + +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +export function exported() { + wasm.exported(); +} + +export function __wbg_static_accessor_NAMESPACE_OPTIONAL_c9a4344c544120f4() { + const result = typeof test.NAMESPACE_OPTIONAL === 'undefined' ? null : test.NAMESPACE_OPTIONAL; + const ret = result; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); +}; + +export function __wbg_static_accessor_NAMESPACE_PLAIN_784c8d7f5bbac62a() { + const ret = test.NAMESPACE_PLAIN; + return ret; +}; + +export function __wbg_static_accessor_OPTIONAL_ade71b6402851d0c() { + const result = typeof OPTIONAL === 'undefined' ? null : OPTIONAL; + const ret = result; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); +}; + +export function __wbg_static_accessor_PLAIN_c0f08eb2f0db194c() { + const ret = PLAIN; + return ret; +}; + +export function __wbindgen_init_externref_table() { + const table = wasm.__wbindgen_export_1; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + ; +}; + +export function __wbindgen_throw(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +}; + diff --git a/crates/cli/tests/reference/static.rs b/crates/cli/tests/reference/static.rs new file mode 100644 index 00000000000..ea6b37cbc22 --- /dev/null +++ b/crates/cli/tests/reference/static.rs @@ -0,0 +1,24 @@ +// DEPENDENCY: js-sys = { path = '{root}/crates/js-sys' } + +use wasm_bindgen::prelude::*; +use js_sys::Number; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(thread_local_v2)] + static PLAIN: JsValue; + #[wasm_bindgen(thread_local_v2)] + static OPTIONAL: Option; + #[wasm_bindgen(thread_local_v2, js_namespace = test)] + static NAMESPACE_PLAIN: JsValue; + #[wasm_bindgen(thread_local_v2, js_namespace = test)] + static NAMESPACE_OPTIONAL: Option; +} + +#[wasm_bindgen] +pub fn exported() { + let _ = PLAIN.with(JsValue::clone); + let _ = OPTIONAL.with(Option::clone); + let _ = NAMESPACE_PLAIN.with(JsValue::clone); + let _ = NAMESPACE_OPTIONAL.with(Option::clone); +} diff --git a/crates/cli/tests/reference/static.wat b/crates/cli/tests/reference/static.wat new file mode 100644 index 00000000000..b4ea3e1c9e8 --- /dev/null +++ b/crates/cli/tests/reference/static.wat @@ -0,0 +1,16 @@ +(module $reference_test.wasm + (type (;0;) (func)) + (type (;1;) (func (result i32))) + (import "./reference_test_bg.js" "__wbindgen_init_externref_table" (func (;0;) (type 0))) + (func $__externref_table_alloc (;1;) (type 1) (result i32)) + (func $exported (;2;) (type 0)) + (table (;0;) 128 externref) + (memory (;0;) 17) + (export "memory" (memory 0)) + (export "exported" (func $exported)) + (export "__externref_table_alloc" (func $__externref_table_alloc)) + (export "__wbindgen_export_1" (table 0)) + (export "__wbindgen_start" (func 0)) + (@custom "target_features" (after code) "\04+\0amultivalue+\0fmutable-globals+\0freference-types+\08sign-ext") +) + diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 57b01303cd4..aa2f3a07b3b 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -6055,14 +6055,6 @@ pub fn global() -> Object { } fn get_global_object() -> Object { - // This is a bit wonky, but we're basically using `#[wasm_bindgen]` - // attributes to synthesize imports so we can access properties of the - // form: - // - // * `globalThis.globalThis` - // * `self.self` - // * ... (etc) - // // Accessing the global object is not an easy thing to do, and what we // basically want is `globalThis` but we can't rely on that existing // everywhere. In the meantime we've got the fallbacks mentioned in: @@ -6076,26 +6068,27 @@ pub fn global() -> Object { extern "C" { type Global; - #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = globalThis, js_name = globalThis)] - fn get_global_this() -> Result; + #[wasm_bindgen(thread_local_v2, js_name = globalThis)] + static GLOBAL_THIS: Option; - #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = self, js_name = self)] - fn get_self() -> Result; + #[wasm_bindgen(thread_local_v2, js_name = self)] + static SELF: Option; - #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = window, js_name = window)] - fn get_window() -> Result; + #[wasm_bindgen(thread_local_v2, js_name = window)] + static WINDOW: Option; - #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = global, js_name = global)] - fn get_global() -> Result; + #[wasm_bindgen(thread_local_v2, js_name = global)] + static GLOBAL: Option; } // The order is important: in Firefox Extension Content Scripts `globalThis` // is a Sandbox (not Window), so `globalThis` must be checked after `window`. - let static_object = Global::get_self() - .or_else(|_| Global::get_window()) - .or_else(|_| Global::get_global_this()) - .or_else(|_| Global::get_global()); - if let Ok(obj) = static_object { + let static_object = SELF + .with(Option::clone) + .or_else(|| WINDOW.with(Option::clone)) + .or_else(|| GLOBAL_THIS.with(Option::clone)) + .or_else(|| GLOBAL.with(Option::clone)); + if let Some(obj) = static_object { if !obj.is_undefined() { return obj; } diff --git a/guide/src/reference/static-js-objects.md b/guide/src/reference/static-js-objects.md index b6aee687dc8..42eef4af286 100644 --- a/guide/src/reference/static-js-objects.md +++ b/guide/src/reference/static-js-objects.md @@ -65,6 +65,20 @@ extern "C" { } ``` +## Undeclared + +When accessing an in JS undeclared value, it will throw in JS. This can be accounted for by using `Option`. + +```rust +extern "C" { + type Crypto; + #[wasm_bindgen(thread_local_v2, js_name = crypto)] + static CRYPTO: Option; +} +``` + +If `crypto` is undeclared in JS, it will simply return `None` in Rust. + ## Static strings Strings can be imported to avoid going through `TextDecoder/Encoder` when requiring just a `JsString`. This can be useful when dealing with environments where `TextDecoder/Encoder` is not available, like in audio worklets. From 40b557c65a53586ba491ebe6f0ce0441deb37d59 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 5 Dec 2024 16:27:08 +0100 Subject: [PATCH 2/6] Fix namespaced statics --- crates/cli-support/src/js/mod.rs | 8 +++++++- crates/cli/tests/reference/static.js | 30 ++++++++++++++++++++++++++-- crates/cli/tests/reference/static.rs | 6 ++++++ tests/wasm/imports.rs | 16 +++++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index f08bd05190a..1a4f904bf76 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -3274,7 +3274,13 @@ __wbg_set_wasm(wasm);" if *optional { writeln!( prelude, - "const result = typeof {js} === 'undefined' ? null : {js};" + "\ + let result; + try {{ + result = {js}; + }} catch (_) {{ + result = null; + }}", ) .unwrap(); Ok("result".to_owned()) diff --git a/crates/cli/tests/reference/static.js b/crates/cli/tests/reference/static.js index 1f9b56f3b69..86549f8bb6d 100644 --- a/crates/cli/tests/reference/static.js +++ b/crates/cli/tests/reference/static.js @@ -39,7 +39,12 @@ export function exported() { } export function __wbg_static_accessor_NAMESPACE_OPTIONAL_c9a4344c544120f4() { - const result = typeof test.NAMESPACE_OPTIONAL === 'undefined' ? null : test.NAMESPACE_OPTIONAL; + let result; + try { + result = test.NAMESPACE_OPTIONAL; + } catch (_) { + result = null; + } const ret = result; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; @@ -49,8 +54,29 @@ export function __wbg_static_accessor_NAMESPACE_PLAIN_784c8d7f5bbac62a() { return ret; }; +export function __wbg_static_accessor_NESTED_NAMESPACE_OPTIONAL_a414abbeb018a35a() { + let result; + try { + result = test1.test2.NESTED_NAMESPACE_OPTIONAL; + } catch (_) { + result = null; + } + const ret = result; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); +}; + +export function __wbg_static_accessor_NESTED_NAMESPACE_PLAIN_1121b285cb8479df() { + const ret = test1.test2.NESTED_NAMESPACE_PLAIN; + return ret; +}; + export function __wbg_static_accessor_OPTIONAL_ade71b6402851d0c() { - const result = typeof OPTIONAL === 'undefined' ? null : OPTIONAL; + let result; + try { + result = OPTIONAL; + } catch (_) { + result = null; + } const ret = result; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; diff --git a/crates/cli/tests/reference/static.rs b/crates/cli/tests/reference/static.rs index ea6b37cbc22..4437f550e7d 100644 --- a/crates/cli/tests/reference/static.rs +++ b/crates/cli/tests/reference/static.rs @@ -13,6 +13,10 @@ extern "C" { static NAMESPACE_PLAIN: JsValue; #[wasm_bindgen(thread_local_v2, js_namespace = test)] static NAMESPACE_OPTIONAL: Option; + #[wasm_bindgen(thread_local_v2, js_namespace = ["test1", "test2"])] + static NESTED_NAMESPACE_PLAIN: JsValue; + #[wasm_bindgen(thread_local_v2, js_namespace = ["test1", "test2"])] + static NESTED_NAMESPACE_OPTIONAL: Option; } #[wasm_bindgen] @@ -21,4 +25,6 @@ pub fn exported() { let _ = OPTIONAL.with(Option::clone); let _ = NAMESPACE_PLAIN.with(JsValue::clone); let _ = NAMESPACE_OPTIONAL.with(Option::clone); + let _ = NESTED_NAMESPACE_PLAIN.with(JsValue::clone); + let _ = NESTED_NAMESPACE_OPTIONAL.with(Option::clone); } diff --git a/tests/wasm/imports.rs b/tests/wasm/imports.rs index a6eab0c5e32..119aefef487 100644 --- a/tests/wasm/imports.rs +++ b/tests/wasm/imports.rs @@ -90,6 +90,15 @@ extern "C" { #[wasm_bindgen(js_name = "\"string'literal\nbreakers\r")] fn string_literal_breakers() -> u32; + + #[wasm_bindgen(thread_local_v2)] + static UNDECLARED: Option; + + #[wasm_bindgen(thread_local_v2, js_namespace = test)] + static UNDECLARED_NAMESPACE: Option; + + #[wasm_bindgen(thread_local_v2, js_namespace = ["test1", "test2"])] + static UNDECLARED_NESTED_NAMESPACE: Option; } #[wasm_bindgen(module = "tests/wasm/imports_2.js")] @@ -336,3 +345,10 @@ fn invalid_idents() { assert_eq!(kebab_case(), 42); assert_eq!(string_literal_breakers(), 42); } + +#[wasm_bindgen_test] +fn undeclared() { + assert_eq!(UNDECLARED.with(Option::clone), None); + assert_eq!(UNDECLARED_NAMESPACE.with(Option::clone), None); + assert_eq!(UNDECLARED_NESTED_NAMESPACE.with(Option::clone), None); +} From 8eac1d51088a92bf25aad99b1f9edf9ca738d798 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 5 Dec 2024 17:34:43 +0100 Subject: [PATCH 3/6] Add note about getters --- guide/src/reference/static-js-objects.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/guide/src/reference/static-js-objects.md b/guide/src/reference/static-js-objects.md index 42eef4af286..ab96c0708af 100644 --- a/guide/src/reference/static-js-objects.md +++ b/guide/src/reference/static-js-objects.md @@ -4,8 +4,14 @@ JavaScript modules will often export arbitrary static objects for use with their provided interfaces. These objects can be accessed from Rust by declaring a named `static` in the `extern` block with an `#[wasm_bindgen(thread_local_v2)]` attribute. `wasm-bindgen` will bind a -`JsThreadLocal` for these objects, which can be cloned into a `JsValue`. For -example, given the following JavaScript: +`JsThreadLocal` for these objects, which can be cloned into a `JsValue`. + +These values are cached in a thread-local and are meant to bind static values +or objects only. Binding getters or expecting a new value or object when +changed in JS is not supported. For these use +[getters](attributes/on-js-imports/getter-and-setter.md). + +For example, given the following JavaScript: ```js let COLORS = { From c627a4f05a34a201814d2d62ea869b6da000696f Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 5 Dec 2024 23:43:51 +0100 Subject: [PATCH 4/6] Handle potentially throwing getters --- crates/cli-support/src/js/mod.rs | 43 ++++++++++++++---------- crates/cli/tests/reference/static.js | 24 ++----------- guide/src/reference/static-js-objects.md | 5 ++- 3 files changed, 30 insertions(+), 42 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 1a4f904bf76..d90ca2a1a39 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -2578,6 +2578,30 @@ __wbg_set_wasm(wasm);" Ok(name) } + fn import_static(&mut self, import: &JsImport, optional: bool) -> Result { + let mut name = self.import_name(&JsImport { + name: import.name.clone(), + fields: Vec::new(), + })?; + + // After we've got an actual name handle field projections + if optional { + name = format!("typeof {name} === 'undefined' ? null: {name}"); + + for field in import.fields.iter() { + name.push_str("?."); + name.push_str(field); + } + } else { + for field in import.fields.iter() { + name.push('.'); + name.push_str(field); + } + } + + Ok(name) + } + /// If a start function is present, it removes it from the `start` section /// of the Wasm module and then moves it to an exported function, named /// `__wbindgen_start`. @@ -3269,24 +3293,7 @@ __wbg_set_wasm(wasm);" assert!(kind == AdapterJsImportKind::Normal); assert!(!variadic); assert_eq!(args.len(), 0); - let js = self.import_name(js)?; - - if *optional { - writeln!( - prelude, - "\ - let result; - try {{ - result = {js}; - }} catch (_) {{ - result = null; - }}", - ) - .unwrap(); - Ok("result".to_owned()) - } else { - Ok(js) - } + self.import_static(js, *optional) } AuxImport::String(string) => { diff --git a/crates/cli/tests/reference/static.js b/crates/cli/tests/reference/static.js index 86549f8bb6d..fcf7e92fa19 100644 --- a/crates/cli/tests/reference/static.js +++ b/crates/cli/tests/reference/static.js @@ -39,13 +39,7 @@ export function exported() { } export function __wbg_static_accessor_NAMESPACE_OPTIONAL_c9a4344c544120f4() { - let result; - try { - result = test.NAMESPACE_OPTIONAL; - } catch (_) { - result = null; - } - const ret = result; + const ret = typeof test === 'undefined' ? null: test?.NAMESPACE_OPTIONAL; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; @@ -55,13 +49,7 @@ export function __wbg_static_accessor_NAMESPACE_PLAIN_784c8d7f5bbac62a() { }; export function __wbg_static_accessor_NESTED_NAMESPACE_OPTIONAL_a414abbeb018a35a() { - let result; - try { - result = test1.test2.NESTED_NAMESPACE_OPTIONAL; - } catch (_) { - result = null; - } - const ret = result; + const ret = typeof test1 === 'undefined' ? null: test1?.test2?.NESTED_NAMESPACE_OPTIONAL; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; @@ -71,13 +59,7 @@ export function __wbg_static_accessor_NESTED_NAMESPACE_PLAIN_1121b285cb8479df() }; export function __wbg_static_accessor_OPTIONAL_ade71b6402851d0c() { - let result; - try { - result = OPTIONAL; - } catch (_) { - result = null; - } - const ret = result; + const ret = typeof OPTIONAL === 'undefined' ? null: OPTIONAL; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; diff --git a/guide/src/reference/static-js-objects.md b/guide/src/reference/static-js-objects.md index ab96c0708af..6604eee8994 100644 --- a/guide/src/reference/static-js-objects.md +++ b/guide/src/reference/static-js-objects.md @@ -7,9 +7,8 @@ a named `static` in the `extern` block with an `JsThreadLocal` for these objects, which can be cloned into a `JsValue`. These values are cached in a thread-local and are meant to bind static values -or objects only. Binding getters or expecting a new value or object when -changed in JS is not supported. For these use -[getters](attributes/on-js-imports/getter-and-setter.md). +or objects only. For getters which can change their return value or throw see +[how to import getters](attributes/on-js-imports/getter-and-setter.md). For example, given the following JavaScript: From 1f81bc102ff83eb700548a72b58f56a6f65d82d2 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 6 Dec 2024 16:44:49 +0100 Subject: [PATCH 5/6] Adjust uncommon JS style Co-Authored-By: Michael Schmidt --- crates/cli-support/src/js/mod.rs | 2 +- crates/cli/tests/reference/static.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index d90ca2a1a39..e26f7a6d2ca 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -2586,7 +2586,7 @@ __wbg_set_wasm(wasm);" // After we've got an actual name handle field projections if optional { - name = format!("typeof {name} === 'undefined' ? null: {name}"); + name = format!("typeof {name} === 'undefined' ? null : {name}"); for field in import.fields.iter() { name.push_str("?."); diff --git a/crates/cli/tests/reference/static.js b/crates/cli/tests/reference/static.js index fcf7e92fa19..34aa7975e7a 100644 --- a/crates/cli/tests/reference/static.js +++ b/crates/cli/tests/reference/static.js @@ -39,7 +39,7 @@ export function exported() { } export function __wbg_static_accessor_NAMESPACE_OPTIONAL_c9a4344c544120f4() { - const ret = typeof test === 'undefined' ? null: test?.NAMESPACE_OPTIONAL; + const ret = typeof test === 'undefined' ? null : test?.NAMESPACE_OPTIONAL; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; @@ -49,7 +49,7 @@ export function __wbg_static_accessor_NAMESPACE_PLAIN_784c8d7f5bbac62a() { }; export function __wbg_static_accessor_NESTED_NAMESPACE_OPTIONAL_a414abbeb018a35a() { - const ret = typeof test1 === 'undefined' ? null: test1?.test2?.NESTED_NAMESPACE_OPTIONAL; + const ret = typeof test1 === 'undefined' ? null : test1?.test2?.NESTED_NAMESPACE_OPTIONAL; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; @@ -59,7 +59,7 @@ export function __wbg_static_accessor_NESTED_NAMESPACE_PLAIN_1121b285cb8479df() }; export function __wbg_static_accessor_OPTIONAL_ade71b6402851d0c() { - const ret = typeof OPTIONAL === 'undefined' ? null: OPTIONAL; + const ret = typeof OPTIONAL === 'undefined' ? null : OPTIONAL; return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); }; From 2b5ebdc93a718f6dfa9128c6bbcbba958696f501 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 6 Dec 2024 16:45:00 +0100 Subject: [PATCH 6/6] Improve documentation Co-Authored-By: Michael Schmidt --- guide/src/reference/static-js-objects.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/guide/src/reference/static-js-objects.md b/guide/src/reference/static-js-objects.md index 6604eee8994..92b699d57c6 100644 --- a/guide/src/reference/static-js-objects.md +++ b/guide/src/reference/static-js-objects.md @@ -70,9 +70,10 @@ extern "C" { } ``` -## Undeclared +## Optional statics -When accessing an in JS undeclared value, it will throw in JS. This can be accounted for by using `Option`. +If you expect the JavaScript value you're trying to access to not always be +available you can use `Option` to handle this: ```rust extern "C" { @@ -82,7 +83,9 @@ extern "C" { } ``` -If `crypto` is undeclared in JS, it will simply return `None` in Rust. +If `crypto` is not declared or nullish (`null` or `undefined`) in JavaScript, +it will simply return `None` in Rust. This will also account for namespaces: it +will return `Some(T)` only if all parts are declared and not nullish. ## Static strings