From 56fa901442e6f826b01e4976bc5aa8a80c83f727 Mon Sep 17 00:00:00 2001
From: Nick Fitzgerald <fitzgen@gmail.com>
Date: Fri, 22 Jun 2018 14:11:07 -0700
Subject: [PATCH 1/6] js::Object: Sort methods alphabetically

---
 src/js.rs | 40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/src/js.rs b/src/js.rs
index c5f0d51e..58d0cdd3 100644
--- a/src/js.rs
+++ b/src/js.rs
@@ -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).
@@ -292,6 +286,26 @@ extern {
     #[wasm_bindgen(method, js_name = hasOwnProperty)]
     pub fn has_own_property(this: &Object, property: &JsValue) -> bool;
 
+    /// 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 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.
@@ -306,20 +320,6 @@ extern {
     #[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 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 valueOf() method returns the primitive value of the
     /// specified object.
     ///

From 911a32c0d5354157aa65a9687ef2965d459e9c4d Mon Sep 17 00:00:00 2001
From: Nick Fitzgerald <fitzgen@gmail.com>
Date: Fri, 22 Jun 2018 14:42:48 -0700
Subject: [PATCH 2/6] Add the `#[wasm_bindgen(static_method_of = Class)]`
 attribute

This is similar to `js_namespace` but translates into a static method on `Class`
rather than a free function. This allows us to have bindings to things like
`Object.keys` as `Object::keys`.
---
 crates/backend/src/ast.rs | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs
index 60d7bc8f..0120d987 100644
--- a/crates/backend/src/ast.rs
+++ b/crates/backend/src/ast.rs
@@ -1,6 +1,7 @@
 use proc_macro2::{Ident, Span, TokenStream, TokenTree};
 use quote::ToTokens;
 use shared;
+use std::iter::FromIterator;
 use syn;
 
 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@@ -412,6 +413,24 @@ 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 segments = syn::punctuated::Punctuated::from_iter(Some(syn::PathSegment {
+                ident: cls.clone(),
+                arguments: syn::PathArguments::None,
+            }));
+
+            let ty = syn::Type::Path(syn::TypePath {
+                qself: None,
+                path: syn::Path {
+                    leading_colon: None,
+                    segments,
+                },
+            });
+
+            ImportFunctionKind::Method { class, ty, kind }
         } else if wasm.opts.constructor() {
             let class = match wasm.ret {
                 Some(ref ty) => ty,
@@ -888,6 +907,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,
@@ -980,6 +1009,7 @@ pub enum BindgenAttr {
     Catch,
     Constructor,
     Method,
+    StaticMethodOf(Ident),
     JsNamespace(Ident),
     Module(String),
     Version(String),
@@ -999,6 +1029,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!(

From 21fa3beabd6942709d1f3a7d838d62451c18a830 Mon Sep 17 00:00:00 2001
From: Nick Fitzgerald <fitzgen@gmail.com>
Date: Fri, 22 Jun 2018 14:45:09 -0700
Subject: [PATCH 3/6] backend: Add some trailing commas that rustfmt prefers

---
 crates/backend/src/ast.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs
index 0120d987..aa15a486 100644
--- a/crates/backend/src/ast.rs
+++ b/crates/backend/src/ast.rs
@@ -813,7 +813,7 @@ impl Struct {
                     ty: field.ty.clone(),
                     getter: Ident::new(&getter, Span::call_site()),
                     setter: Ident::new(&setter, Span::call_site()),
-                    comments
+                    comments,
                 });
             }
         }
@@ -912,7 +912,7 @@ impl BindgenAttrs {
             .iter()
             .filter_map(|a| match a {
                 BindgenAttr::StaticMethodOf(c) => Some(c),
-                _ => None
+                _ => None,
             })
             .next()
     }

From eb04d15a653becf0ed16ca0bbf61b444756a6a80 Mon Sep 17 00:00:00 2001
From: Nick Fitzgerald <fitzgen@gmail.com>
Date: Fri, 22 Jun 2018 14:45:33 -0700
Subject: [PATCH 4/6] js: Add bindings to `Object.keys`

---
 src/js.rs                      |  7 +++++++
 tests/all/js_globals/Object.rs | 27 +++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/src/js.rs b/src/js.rs
index 58d0cdd3..bebe3cb9 100644
--- a/src/js.rs
+++ b/src/js.rs
@@ -293,6 +293,13 @@ extern {
     #[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
diff --git a/tests/all/js_globals/Object.rs b/tests/all/js_globals/Object.rs
index e4fc92e7..2ca6a093 100755
--- a/tests/all/js_globals/Object.rs
+++ b/tests/all/js_globals/Object.rs
@@ -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()

From 8fbf478058f54a8fc81384f36a12bb890c3c81ae Mon Sep 17 00:00:00 2001
From: Nick Fitzgerald <fitzgen@gmail.com>
Date: Mon, 25 Jun 2018 10:41:33 -0700
Subject: [PATCH 5/6] Move some utility functions from the webidl crate into
 the backend crate

---
 crates/backend/src/ast.rs  | 17 ++--------
 crates/backend/src/lib.rs  |  1 +
 crates/backend/src/util.rs | 69 ++++++++++++++++++++++++++++++++++++++
 crates/webidl/src/lib.rs   | 10 +++---
 crates/webidl/src/util.rs  | 67 ++----------------------------------
 5 files changed, 80 insertions(+), 84 deletions(-)
 create mode 100644 crates/backend/src/util.rs

diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs
index aa15a486..f62a3ebd 100644
--- a/crates/backend/src/ast.rs
+++ b/crates/backend/src/ast.rs
@@ -1,8 +1,8 @@
 use proc_macro2::{Ident, Span, TokenStream, TokenTree};
 use quote::ToTokens;
 use shared;
-use std::iter::FromIterator;
 use syn;
+use util;
 
 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
 #[derive(Default)]
@@ -416,20 +416,7 @@ impl Program {
         } else if let Some(cls) = wasm.opts.static_method_of() {
             let class = cls.to_string();
             let kind = MethodKind::Static;
-
-            let segments = syn::punctuated::Punctuated::from_iter(Some(syn::PathSegment {
-                ident: cls.clone(),
-                arguments: syn::PathArguments::None,
-            }));
-
-            let ty = syn::Type::Path(syn::TypePath {
-                qself: None,
-                path: syn::Path {
-                    leading_colon: None,
-                    segments,
-                },
-            });
-
+            let ty = util::ident_ty(cls.clone());
             ImportFunctionKind::Method { class, ty, kind }
         } else if wasm.opts.constructor() {
             let class = match wasm.ret {
diff --git a/crates/backend/src/lib.rs b/crates/backend/src/lib.rs
index 19ad1957..60a513da 100755
--- a/crates/backend/src/lib.rs
+++ b/crates/backend/src/lib.rs
@@ -12,3 +12,4 @@ extern crate wasm_bindgen_shared as shared;
 
 pub mod ast;
 mod codegen;
+pub mod util;
diff --git a/crates/backend/src/util.rs b/crates/backend/src/util.rs
new file mode 100644
index 00000000..8773c4ab
--- /dev/null
+++ b/crates/backend/src/util.rs
@@ -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),
+    }
+}
diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs
index c024cd93..950207eb 100755
--- a/crates/webidl/src/lib.rs
+++ b/crates/webidl/src/lib.rs
@@ -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)`.
diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs
index c8d2eb5a..5b197dde 100644
--- a/crates/webidl/src/util.rs
+++ b/crates/webidl/src/util.rs
@@ -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(),
@@ -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),
-    }
-}

From d28d81f38dc4447cc7a5b9de5810b9f9edbe66b9 Mon Sep 17 00:00:00 2001
From: Lachezar Lechev <8925621+elpiel@users.noreply.github.com>
Date: Mon, 25 Jun 2018 20:24:44 +0200
Subject: [PATCH 6/6] Add basic support for String.prototype.charAt() (#306)

* String - charAt() implementation

* String - charAt() - add js_class
---
 src/js.rs                        |  7 +++++++
 tests/all/js_globals/JsString.rs | 29 +++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/src/js.rs b/src/js.rs
index bebe3cb9..df26bd7b 100644
--- a/src/js.rs
+++ b/src/js.rs
@@ -347,6 +347,13 @@ extern {
     /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
     #[wasm_bindgen(method, js_class = "String")]
     pub fn slice(this: &JsString, start: u32, end: u32) -> JsString;
+
+    /// The String object's charAt() method returns a new string consisting of the single
+    /// UTF-16 code unit located at the specified offset into the string.
+    ///
+    /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt
+    #[wasm_bindgen(method, js_class = "String", js_name = charAt)]
+    pub fn char_at(this: &JsString, index: u32) -> JsString;
 }
 
 impl<'a> From<&'a str> for JsString {
diff --git a/tests/all/js_globals/JsString.rs b/tests/all/js_globals/JsString.rs
index 112f2d2b..7d687252 100755
--- a/tests/all/js_globals/JsString.rs
+++ b/tests/all/js_globals/JsString.rs
@@ -2,6 +2,35 @@
 
 use project;
 
+#[test]
+fn char_at() {
+    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 string_char_at(this: &js::JsString, index: u32) -> js::JsString {
+                this.char_at(index)
+            }
+        "#)
+        .file("test.ts", r#"
+            import * as assert from "assert";
+            import * as wasm from "./out";
+
+            var anyString = 'Brave new world';
+
+            export function test() {
+                assert.equal(wasm.string_char_at(anyString, 0), "B");
+                assert.equal(wasm.string_char_at(anyString, 999), "");
+            }
+        "#)
+        .test()
+}
+
 #[test]
 fn slice() {
     project()