Skip to content

Attribute for static methods #296

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

Merged
merged 5 commits into from
Jun 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion crates/backend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use quote::ToTokens;
use shared;
use syn;
use util;

#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
#[derive(Default)]
Expand Down Expand Up @@ -412,6 +413,11 @@ impl Program {
ty: class.clone(),
kind: MethodKind::Normal,
}
} else if let Some(cls) = wasm.opts.static_method_of() {
let class = cls.to_string();
let kind = MethodKind::Static;
let ty = util::ident_ty(cls.clone());
ImportFunctionKind::Method { class, ty, kind }
} else if wasm.opts.constructor() {
let class = match wasm.ret {
Some(ref ty) => ty,
Expand Down Expand Up @@ -794,7 +800,7 @@ impl Struct {
ty: field.ty.clone(),
getter: Ident::new(&getter, Span::call_site()),
setter: Ident::new(&setter, Span::call_site()),
comments
comments,
});
}
}
Expand Down Expand Up @@ -888,6 +894,16 @@ impl BindgenAttrs {
})
}

fn static_method_of(&self) -> Option<&Ident> {
self.attrs
.iter()
.filter_map(|a| match a {
BindgenAttr::StaticMethodOf(c) => Some(c),
_ => None,
})
.next()
}

fn method(&self) -> bool {
self.attrs.iter().any(|a| match a {
BindgenAttr::Method => true,
Expand Down Expand Up @@ -980,6 +996,7 @@ pub enum BindgenAttr {
Catch,
Constructor,
Method,
StaticMethodOf(Ident),
JsNamespace(Ident),
Module(String),
Version(String),
Expand All @@ -999,6 +1016,13 @@ impl syn::synom::Synom for BindgenAttr {
|
call!(term, "method") => { |_| BindgenAttr::Method }
|
do_parse!(
call!(term, "static_method_of") >>
punct!(=) >>
cls: call!(term2ident) >>
(cls)
)=> { BindgenAttr::StaticMethodOf }
|
do_parse!(
call!(term, "getter") >>
val: option!(do_parse!(
Expand Down
1 change: 1 addition & 0 deletions crates/backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ extern crate wasm_bindgen_shared as shared;

pub mod ast;
mod codegen;
pub mod util;
69 changes: 69 additions & 0 deletions crates/backend/src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::iter::FromIterator;

use ast;
use proc_macro2::{self, Ident};
use syn;

fn is_rust_keyword(name: &str) -> bool {
match name {
"abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue"
| "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if"
| "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut"
| "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" | "return"
| "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" | "true"
| "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while"
| "yield" | "bool" | "_" => true,
_ => false,
}
}

// Create an `Ident`, possibly mangling it if it conflicts with a Rust keyword.
pub fn rust_ident(name: &str) -> Ident {
if is_rust_keyword(name) {
Ident::new(&format!("{}_", name), proc_macro2::Span::call_site())
} else {
raw_ident(name)
}
}

// Create an `Ident` without checking to see if it conflicts with a Rust
// keyword.
pub fn raw_ident(name: &str) -> Ident {
Ident::new(name, proc_macro2::Span::call_site())
}

/// Create a path type from the given segments. For example an iterator yielding
/// the idents `[foo, bar, baz]` will result in the path type `foo::bar::baz`.
pub fn simple_path_ty<I>(segments: I) -> syn::Type
where
I: IntoIterator<Item = Ident>,
{
let segments: Vec<_> = segments
.into_iter()
.map(|i| syn::PathSegment {
ident: i,
arguments: syn::PathArguments::None,
})
.collect();

syn::TypePath {
qself: None,
path: syn::Path {
leading_colon: None,
segments: syn::punctuated::Punctuated::from_iter(segments),
},
}.into()
}

pub fn ident_ty(ident: Ident) -> syn::Type {
simple_path_ty(Some(ident))
}

pub fn wrap_import_function(function: ast::ImportFunction) -> ast::Import {
ast::Import {
module: None,
version: None,
js_namespace: None,
kind: ast::ImportKind::Function(function),
}
}
10 changes: 5 additions & 5 deletions crates/webidl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ extern crate syn;
extern crate wasm_bindgen_backend as backend;
extern crate webidl;

mod util;

use std::fs;
use std::io::{self, Read};
use std::iter;
use std::path::Path;

use backend::util::{ident_ty, rust_ident, wrap_import_function};
use failure::ResultExt;
use quote::ToTokens;

mod util;

use util::{
create_basic_method, create_function, create_getter, create_setter, ident_ty, rust_ident,
webidl_ty_to_syn_ty, wrap_import_function, TypePosition,
create_basic_method, create_function, create_getter, create_setter, webidl_ty_to_syn_ty,
TypePosition,
};

/// Either `Ok(t)` or `Err(failure::Error)`.
Expand Down
67 changes: 3 additions & 64 deletions crates/webidl/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,64 +1,12 @@
use std::iter::{self, FromIterator};
use std::iter;

use backend;
use backend::util::{ident_ty, raw_ident, rust_ident, simple_path_ty};
use heck::SnakeCase;
use proc_macro2::{self, Ident};
use proc_macro2::Ident;
use syn;
use webidl;

fn is_rust_keyword(name: &str) -> bool {
match name {
"abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue"
| "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if"
| "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut"
| "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" | "return"
| "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" | "true"
| "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while"
| "yield" | "bool" | "_" => true,
_ => false,
}
}

// Create an `Ident`, possibly mangling it if it conflicts with a Rust keyword.
pub fn rust_ident(name: &str) -> Ident {
if is_rust_keyword(name) {
Ident::new(&format!("{}_", name), proc_macro2::Span::call_site())
} else {
raw_ident(name)
}
}

// Create an `Ident` without checking to see if it conflicts with a Rust
// keyword.
fn raw_ident(name: &str) -> Ident {
Ident::new(name, proc_macro2::Span::call_site())
}

fn simple_path_ty<I>(segments: I) -> syn::Type
where
I: IntoIterator<Item = Ident>,
{
let segments: Vec<_> = segments
.into_iter()
.map(|i| syn::PathSegment {
ident: i,
arguments: syn::PathArguments::None,
})
.collect();

syn::TypePath {
qself: None,
path: syn::Path {
leading_colon: None,
segments: syn::punctuated::Punctuated::from_iter(segments),
},
}.into()
}

pub fn ident_ty(ident: Ident) -> syn::Type {
simple_path_ty(Some(ident))
}

fn shared_ref(ty: syn::Type) -> syn::Type {
syn::TypeReference {
and_token: Default::default(),
Expand Down Expand Up @@ -337,12 +285,3 @@ pub fn create_setter(
vec![backend::ast::BindgenAttr::Setter(Some(raw_ident(name)))],
)
}

pub fn wrap_import_function(function: backend::ast::ImportFunction) -> backend::ast::Import {
backend::ast::Import {
module: None,
version: None,
js_namespace: None,
kind: backend::ast::ImportKind::Function(function),
}
}
47 changes: 27 additions & 20 deletions src/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,12 +278,6 @@ extern {
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).
Expand All @@ -292,34 +286,47 @@ extern {
#[wasm_bindgen(method, js_name = hasOwnProperty)]
pub fn has_own_property(this: &Object, property: &JsValue) -> bool;

/// The toLocaleString() method returns a string representing the object.
/// This method is meant to be overridden by derived objects for locale-specific
/// purposes.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toLocaleString
#[wasm_bindgen(method, js_name = toLocaleString)]
pub fn to_locale_string(this: &Object) -> JsString;

/// The toString() method returns a string representing the object.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
#[wasm_bindgen(method, js_name = toString)]
pub fn to_string(this: &Object) -> JsString;

/// The isPrototypeOf() method checks if an object exists in another
/// object's prototype chain.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf
#[wasm_bindgen(method, js_name = isPrototypeOf)]
pub fn is_prototype_of(this: &Object, value: &JsValue) -> bool;

/// The Object.keys() method returns an array of a given object's property
/// names, in the same order as we get with a normal loop.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
#[wasm_bindgen(static_method_of = Object)]
pub fn keys(object: &Object) -> Array;

/// 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 propertyIsEnumerable() method returns a Boolean indicating
/// whether the specified property is enumerable.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable
#[wasm_bindgen(method, js_name = propertyIsEnumerable)]
pub fn property_is_enumerable(this: &Object, property: &JsValue) -> bool;

/// The toLocaleString() method returns a string representing the object.
/// This method is meant to be overridden by derived objects for locale-specific
/// purposes.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toLocaleString
#[wasm_bindgen(method, js_name = toLocaleString)]
pub fn to_locale_string(this: &Object) -> JsString;

/// The toString() method returns a string representing the object.
///
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
#[wasm_bindgen(method, js_name = toString)]
pub fn to_string(this: &Object) -> JsString;

/// The valueOf() method returns the primitive value of the
/// specified object.
///
Expand Down
27 changes: 27 additions & 0 deletions tests/all/js_globals/Object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,33 @@ fn is_prototype_of() {
.test()
}

#[test]
fn keys() {
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 keys(obj: &js::Object) -> js::Array {
js::Object::keys(obj)
}
"#)
.file("test.ts", r#"
import * as assert from "assert";
import * as wasm from "./out";

export function test() {
const obj = { a: 1, b: 2, c: 3 };
assert.deepStrictEqual(wasm.keys(obj), ["a", "b", "c"]);
}
"#)
.test()
}

#[test]
fn property_is_enumerable() {
project()
Expand Down