Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move all methods on Window back to methods #847

Merged
merged 10 commits into from
Sep 18, 2018
12 changes: 1 addition & 11 deletions crates/backend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,6 @@ pub enum ImportFunctionKind {
ty: syn::Type,
kind: MethodKind,
},
ScopedMethod {
ty: syn::Type,
operation: Operation,
},
Normal,
}

Expand Down Expand Up @@ -432,16 +428,10 @@ impl ImportFunction {
}
};
Some(shared::MethodData {
class: Some(class.clone()),
class: class.clone(),
kind,
})
}
ImportFunctionKind::ScopedMethod { ref operation, .. } => {
Some(shared::MethodData {
class: None,
kind: shared::MethodKind::Operation(shared_operation(operation)),
})
}
ImportFunctionKind::Normal => None,
};

Expand Down
9 changes: 3 additions & 6 deletions crates/backend/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,9 +779,6 @@ impl TryToTokens for ast::ImportFunction {
}
class_ty = Some(ty);
}
ast::ImportFunctionKind::ScopedMethod { ref ty, .. } => {
class_ty = Some(ty);
}
ast::ImportFunctionKind::Normal => {}
}
let vis = &self.function.rust_vis;
Expand Down Expand Up @@ -1068,15 +1065,15 @@ impl ToTokens for ast::Const {
// again no suffix
// panics on +-inf, nan
FloatLiteral(f) => {
let f = Literal::f64_unsuffixed(f);
let f = Literal::f64_suffixed(f);
quote!(#f)
},
SignedIntegerLiteral(i) => {
let i = Literal::i64_unsuffixed(i);
let i = Literal::i64_suffixed(i);
quote!(#i)
},
UnsignedIntegerLiteral(i) => {
let i = Literal::u64_unsuffixed(i);
let i = Literal::u64_suffixed(i);
quote!(#i)
},
Null => unimplemented!(),
Expand Down
1 change: 0 additions & 1 deletion crates/backend/src/defined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ impl ImportedTypes for ast::ImportFunctionKind {
{
match self {
ast::ImportFunctionKind::Method { ty, .. } => ty.imported_types(f),
ast::ImportFunctionKind::ScopedMethod { ty, .. } => ty.imported_types(f),
ast::ImportFunctionKind::Normal => {}
}
}
Expand Down
25 changes: 1 addition & 24 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2001,30 +2001,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
}
};

let class = match &method_data.class {
Some(class) => self.import_name(info, class)?,
None => {
let op = match &method_data.kind {
shared::MethodKind::Operation(op) => op,
shared::MethodKind::Constructor => {
bail!("\"no class\" methods cannot be constructors")
}
};
match &op.kind {
shared::OperationKind::Regular => {
return Ok(import.function.name.to_string())
}
shared::OperationKind::Getter(g) => {
return Ok(format!("(() => {})", g));
}
shared::OperationKind::Setter(g) => {
return Ok(format!("(v => {} = v)", g));
}
_ => bail!("\"no class\" methods must be regular/getter/setter"),
}

}
};
let class = self.import_name(info, &method_data.class)?;
let op = match &method_data.kind {
shared::MethodKind::Constructor => return Ok(format!("new {}", class)),
shared::MethodKind::Operation(op) => op,
Expand Down
53 changes: 53 additions & 0 deletions crates/js-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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()
}
1 change: 0 additions & 1 deletion crates/macro-support/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,6 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn

let shim = {
let ns = match kind {
ast::ImportFunctionKind::ScopedMethod { .. } |
ast::ImportFunctionKind::Normal => (0, "n"),
ast::ImportFunctionKind::Method { ref class, .. } => (1, &class[..]),
};
Expand Down
2 changes: 1 addition & 1 deletion crates/shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub struct ImportFunction {

#[derive(Deserialize, Serialize)]
pub struct MethodData {
pub class: Option<String>,
pub class: String,
pub kind: MethodKind,
}

Expand Down
52 changes: 6 additions & 46 deletions crates/test/src/rt/detect.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Runtime detection of whether we're in node.js or a browser.

use wasm_bindgen::prelude::*;
use js_sys::Function;
use wasm_bindgen::JsCast;
use js_sys;

#[wasm_bindgen]
extern {
Expand All @@ -13,49 +14,8 @@ extern {
/// Returns whether it's likely we're executing in a browser environment, as
/// opposed to node.js.
pub fn is_browser() -> bool {
// This is a bit tricky to define. The basic crux of this is that we want to
// test if the `self` identifier is defined. That is defined in browsers
// (and web workers!) but not in Node. To that end you might expect:
//
// #[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()
// Test whether we're in a browser by seeing if the `self` property is
// defined on the global object, which should in turn only be true in
// browsers.
js_sys::global().unchecked_into::<This>().self_() != JsValue::undefined()
}
8 changes: 8 additions & 0 deletions crates/web-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@

extern crate wasm_bindgen;
extern crate js_sys;

use js_sys::Object;

#[cfg(feature = "Window")]
pub fn window() -> Option<Window> {
use wasm_bindgen::JsCast;

js_sys::global().dyn_into::<Window>().ok()
}

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
24 changes: 14 additions & 10 deletions crates/web-sys/tests/wasm/location.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,60 @@
use wasm_bindgen_test::*;
use web_sys::Window;
use web_sys::{self, Location};

fn location() -> Location {
web_sys::window().unwrap().location()
}

#[wasm_bindgen_test]
fn href() {
let loc = Window::location();
let loc = location();
loc.href().unwrap();
}

#[wasm_bindgen_test]
fn origin() {
let loc = Window::location();
let loc = location();
loc.origin().unwrap();
}

#[wasm_bindgen_test]
fn protocol() {
let loc = Window::location();
let loc = location();
loc.protocol().unwrap();
}

#[wasm_bindgen_test]
fn host() {
let loc = Window::location();
let loc = location();
loc.host().unwrap();
}

#[wasm_bindgen_test]
fn hostname() {
let loc = Window::location();
let loc = location();
loc.hostname().unwrap();
}

#[wasm_bindgen_test]
fn port() {
let loc = Window::location();
let loc = location();
loc.port().unwrap();
}

#[wasm_bindgen_test]
fn pathname() {
let loc = Window::location();
let loc = location();
loc.pathname().unwrap();
}

#[wasm_bindgen_test]
fn search() {
let loc = Window::location();
let loc = location();
loc.search().unwrap();
}

#[wasm_bindgen_test]
fn hash() {
let loc = Window::location();
let loc = location();
loc.hash().unwrap();
}
10 changes: 7 additions & 3 deletions crates/webidl-tests/global.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
global.global_no_args = () => 3;
global.global_with_args = (a, b) => a + b;
global.global_attribute = 'x';
const map = {
global_no_args: () => 3,
global_with_args: (a, b) => a + b,
global_attribute: 'x',
};

global.get_global = () => map;

17 changes: 12 additions & 5 deletions crates/webidl-tests/global.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
use js_sys::Object;
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::*;

include!(concat!(env!("OUT_DIR"), "/global.rs"));

#[wasm_bindgen]
extern {
fn get_global() -> Global;
}

#[wasm_bindgen_test]
fn works() {
assert_eq!(Global::global_no_args(), 3);
assert_eq!(Global::global_with_args("a", "b"), "ab");
assert_eq!(Global::global_attribute(), "x");
Global::set_global_attribute("y");
assert_eq!(Global::global_attribute(), "y");
let x = get_global();
assert_eq!(x.global_no_args(), 3);
assert_eq!(x.global_with_args("a", "b"), "ab");
assert_eq!(x.global_attribute(), "x");
x.set_global_attribute("y");
assert_eq!(x.global_attribute(), "y");
}
6 changes: 5 additions & 1 deletion crates/webidl-tests/simple.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ global.Unforgeable = class Unforgeable {
}
};

global.m = () => 123;
global.GlobalMethod = class {
m() { return 123; }
};

global.get_global_method = () => new GlobalMethod();

global.Indexing = function () {
return new Proxy({}, {
Expand Down
9 changes: 8 additions & 1 deletion crates/webidl-tests/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,16 @@ fn nullable_method() {
assert!(f.opt(None) == None);
}

#[wasm_bindgen]
extern {
fn get_global_method() -> GlobalMethod;
}


#[wasm_bindgen_test]
fn global_method() {
assert_eq!(GlobalMethod::m(), 123);
let x = get_global_method();
assert_eq!(x.m(), 123);
}

#[wasm_bindgen_test]
Expand Down
Loading