Add js_sys::global

This commit is contained in:
Alex Crichton
2018-09-18 15:34:40 -07:00
parent 604ecd9529
commit a83d561bb3
3 changed files with 62 additions and 87 deletions

View File

@ -23,6 +23,8 @@ extern crate wasm_bindgen;
use std::mem;
use std::fmt;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering::SeqCst};
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
// When adding new imports:
@ -4100,3 +4102,54 @@ extern {
#[wasm_bindgen(method)]
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()
}