mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-05-29 13:41:21 +00:00
Add js_sys::global
This commit is contained in:
parent
604ecd9529
commit
a83d561bb3
@ -23,6 +23,8 @@ extern crate wasm_bindgen;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering::SeqCst};
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
// When adding new imports:
|
// When adding new imports:
|
||||||
@ -4100,3 +4102,54 @@ extern {
|
|||||||
#[wasm_bindgen(method)]
|
#[wasm_bindgen(method)]
|
||||||
pub fn finally(this: &Promise, cb: &Closure<FnMut()>) -> Promise;
|
pub fn finally(this: &Promise, cb: &Closure<FnMut()>) -> Promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a handle to the global scope object.
|
||||||
|
///
|
||||||
|
/// This allows access to the global properties and global names by accessing
|
||||||
|
/// the `Object` returned.
|
||||||
|
pub fn global() -> Object {
|
||||||
|
// Cached `Box<JsValue>`, if we've already executed this.
|
||||||
|
//
|
||||||
|
// 0 = not calculated
|
||||||
|
// n = Some(n) == Some(Box<JsValue>)
|
||||||
|
static GLOBAL: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||||
|
|
||||||
|
match GLOBAL.load(SeqCst) {
|
||||||
|
0 => {}
|
||||||
|
n => return unsafe { (*(n as *const JsValue)).clone().unchecked_into() },
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok we don't have a cached value, let's load one!
|
||||||
|
//
|
||||||
|
// According to StackOverflow you can access the global object via:
|
||||||
|
//
|
||||||
|
// const global = Function('return this')();
|
||||||
|
//
|
||||||
|
// I think that's because the manufactured function isn't in "strict" mode.
|
||||||
|
// It also turns out that non-strict functions will ignore `undefined`
|
||||||
|
// values for `this` when using the `apply` function.
|
||||||
|
//
|
||||||
|
// As a result we use the equivalent of this snippet to get a handle to the
|
||||||
|
// global object in a sort of roundabout way that should hopefully work in
|
||||||
|
// all contexts like ESM, node, browsers, etc.
|
||||||
|
let this = Function::new_no_args("return this")
|
||||||
|
.call0(&JsValue::undefined())
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// Note that we avoid `unwrap()` on `call0` to avoid code size bloat, we
|
||||||
|
// just handle the `Err` case as returning a different object.
|
||||||
|
debug_assert!(this.is_some());
|
||||||
|
let this = match this {
|
||||||
|
Some(this) => this,
|
||||||
|
None => return JsValue::undefined().unchecked_into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ptr: *mut JsValue = Box::into_raw(Box::new(this.clone()));
|
||||||
|
match GLOBAL.compare_exchange(0, ptr as usize, SeqCst, SeqCst) {
|
||||||
|
// We stored out value, relinquishing ownership of `ptr`
|
||||||
|
Ok(_) => {}
|
||||||
|
// Another thread one, drop our value
|
||||||
|
Err(_) => unsafe { drop(Box::from_raw(ptr)) },
|
||||||
|
}
|
||||||
|
this.unchecked_into()
|
||||||
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
//! Runtime detection of whether we're in node.js or a browser.
|
//! Runtime detection of whether we're in node.js or a browser.
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use js_sys::Function;
|
use wasm_bindgen::JsCast;
|
||||||
|
use js_sys;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern {
|
extern {
|
||||||
@ -13,49 +14,8 @@ extern {
|
|||||||
/// Returns whether it's likely we're executing in a browser environment, as
|
/// Returns whether it's likely we're executing in a browser environment, as
|
||||||
/// opposed to node.js.
|
/// opposed to node.js.
|
||||||
pub fn is_browser() -> bool {
|
pub fn is_browser() -> bool {
|
||||||
// This is a bit tricky to define. The basic crux of this is that we want to
|
// Test whether we're in a browser by seeing if the `self` property is
|
||||||
// test if the `self` identifier is defined. That is defined in browsers
|
// defined on the global object, which should in turn only be true in
|
||||||
// (and web workers!) but not in Node. To that end you might expect:
|
// browsers.
|
||||||
//
|
js_sys::global().unchecked_into::<This>().self_() != JsValue::undefined()
|
||||||
// #[wasm_bindgen]
|
|
||||||
// extern {
|
|
||||||
// #[wasm_bindgen(js_name = self)]
|
|
||||||
// static SELF: JsValue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// *SELF != JsValue::undefined()
|
|
||||||
//
|
|
||||||
// this currently, however, throws a "not defined" error in JS because the
|
|
||||||
// generated function looks like `function() { return self; }` which throws
|
|
||||||
// an error in Node because `self` isn't defined.
|
|
||||||
//
|
|
||||||
// To work around this limitation we instead lookup the value of `self`
|
|
||||||
// through the `this` object, basically generating `this.self`.
|
|
||||||
//
|
|
||||||
// Unfortunately that's also hard to do! In ESM modes the top-level `this`
|
|
||||||
// object is undefined, meaning that we can't just generate a function that
|
|
||||||
// returns `this.self` as it'll throw "can't access field `self` of
|
|
||||||
// `undefined`" whenever ESMs are being used.
|
|
||||||
//
|
|
||||||
// So finally we reach the current implementation. According to
|
|
||||||
// StackOverflow you can access the global object via:
|
|
||||||
//
|
|
||||||
// const global = Function('return this')();
|
|
||||||
//
|
|
||||||
// I think that's because the manufactured function isn't in "strict" mode.
|
|
||||||
// It also turns out that non-strict functions will ignore `undefined`
|
|
||||||
// values for `this` when using the `apply` function. Add it all up, and you
|
|
||||||
// get the below code:
|
|
||||||
//
|
|
||||||
// * Manufacture a function
|
|
||||||
// * Call `apply` where we specify `this` but the function ignores it
|
|
||||||
// * Once we have `this`, use a structural getter to get the value of `self`
|
|
||||||
// * Last but not least, test whether `self` is defined or not.
|
|
||||||
//
|
|
||||||
// Whew!
|
|
||||||
let this = Function::new_no_args("return this")
|
|
||||||
.call0(&JsValue::undefined())
|
|
||||||
.unwrap();
|
|
||||||
assert!(this != JsValue::undefined());
|
|
||||||
This::from(this).self_() != JsValue::undefined()
|
|
||||||
}
|
}
|
||||||
|
@ -15,52 +15,14 @@
|
|||||||
|
|
||||||
extern crate wasm_bindgen;
|
extern crate wasm_bindgen;
|
||||||
extern crate js_sys;
|
extern crate js_sys;
|
||||||
|
|
||||||
use js_sys::Object;
|
use js_sys::Object;
|
||||||
|
|
||||||
#[cfg(feature = "Window")]
|
#[cfg(feature = "Window")]
|
||||||
pub fn window() -> Option<Window> {
|
pub fn window() -> Option<Window> {
|
||||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering::SeqCst};
|
use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen::{JsValue, JsCast};
|
|
||||||
|
|
||||||
// Cached `Box<JsValue>`, if we've already executed this.
|
js_sys::global().dyn_into::<Window>().ok()
|
||||||
//
|
|
||||||
// 0 = not calculated
|
|
||||||
// 1 = `None`
|
|
||||||
// n = Some(n) == Some(Box<JsValue>)
|
|
||||||
static WINDOW: AtomicUsize = ATOMIC_USIZE_INIT;
|
|
||||||
|
|
||||||
match WINDOW.load(SeqCst) {
|
|
||||||
0 => {}
|
|
||||||
1 => return None,
|
|
||||||
n => return unsafe { Some((*(n as *const JsValue)).clone().unchecked_into()) },
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ok we don't have a cached value, let's load one! Manufacture a function
|
|
||||||
// to get access to the `this` context and see if it's an instance of
|
|
||||||
// `Window`.
|
|
||||||
//
|
|
||||||
// Note that we avoid `unwrap()` on `call0` to avoid code size bloat, we
|
|
||||||
// just handle the `Err` case as returning `None`.
|
|
||||||
let window = js_sys::Function::new_no_args("return this")
|
|
||||||
.call0(&JsValue::undefined())
|
|
||||||
.ok()
|
|
||||||
.and_then(|w| w.dyn_into::<Window>().ok());
|
|
||||||
|
|
||||||
match &window {
|
|
||||||
None => WINDOW.store(1, SeqCst),
|
|
||||||
Some(window) => {
|
|
||||||
let window: &JsValue = window.as_ref();
|
|
||||||
let ptr: *mut JsValue = Box::into_raw(Box::new(window.clone()));
|
|
||||||
match WINDOW.compare_exchange(0, ptr as usize, SeqCst, SeqCst) {
|
|
||||||
// We stored out value, relinquishing ownership of `ptr`
|
|
||||||
Ok(_) => {}
|
|
||||||
// Another thread one, drop our value
|
|
||||||
Err(_) => unsafe { drop(Box::from_raw(ptr)) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window
|
|
||||||
}
|
}
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user