From f9c804db20a635de68bd9bbb9575878c4e58237c Mon Sep 17 00:00:00 2001
From: Satoshi Amemiya <amemiya@protonmail.com>
Date: Fri, 29 Jun 2018 22:46:27 +0900
Subject: [PATCH] Add support for js::Error

---
 src/js.rs                     |  37 +++++++
 tests/all/js_globals/Error.rs | 197 ++++++++++++++++++++++++++++++++++
 tests/all/js_globals/mod.rs   |   1 +
 3 files changed, 235 insertions(+)
 create mode 100644 tests/all/js_globals/Error.rs

diff --git a/src/js.rs b/src/js.rs
index b6c5598f..ced94305 100644
--- a/src/js.rs
+++ b/src/js.rs
@@ -276,6 +276,43 @@ extern "C" {
     pub fn value_of(this: &Boolean) -> bool;
 }
 
+// Error
+#[wasm_bindgen]
+extern "C" {
+    pub type Error;
+
+    /// The Error constructor creates an error object.
+    /// Instances of Error objects are thrown when runtime errors occur.
+    /// The Error object can also be used as a base object for user-defined exceptions.
+    /// See below for standard built-in error types.
+    /// 
+    /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
+    #[wasm_bindgen(constructor)]
+    pub fn new(message: &JsString) -> Error;
+
+    /// The message property is a human-readable description of the error.
+    ///
+    /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message
+    #[wasm_bindgen(method, getter, structural)]
+    pub fn message(this: &Error) -> JsString;
+    #[wasm_bindgen(method, setter, structural)]
+    pub fn set_message(this: &Error, message: &JsString);
+
+    /// The name property represents a name for the type of error. The initial value is "Error".
+    ///
+    /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/name
+    #[wasm_bindgen(method, getter, structural)]
+    pub fn name(this: &Error) -> JsString;
+    #[wasm_bindgen(method, setter, structural)]
+    pub fn set_name(this: &Error, name: &JsString);
+
+    /// The toString() method returns a string representing the specified Error object
+    ///
+    /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
+    #[wasm_bindgen(method, js_name = toString)]
+    pub fn to_string(this: &Error) -> JsString;
+}
+
 // Function
 #[wasm_bindgen]
 extern "C" {
diff --git a/tests/all/js_globals/Error.rs b/tests/all/js_globals/Error.rs
new file mode 100644
index 00000000..bf2918a0
--- /dev/null
+++ b/tests/all/js_globals/Error.rs
@@ -0,0 +1,197 @@
+#![allow(non_snake_case)]
+
+use project;
+
+#[test]
+fn new() {
+    project()
+        .file("src/lib.rs", r#"
+            #![feature(proc_macro, wasm_custom_section)]
+
+            extern crate wasm_bindgen;
+            use wasm_bindgen::prelude::*;
+            use wasm_bindgen::js;
+            use wasm_bindgen::js::Error;
+
+            #[wasm_bindgen]
+            pub fn new_error(message: &js::JsString) -> Error {
+                Error::new(message)
+            }
+        "#)
+        .file("test.ts", r#"
+            import * as assert from "assert";
+            import * as wasm from "./out";
+
+            export function test() {
+                const message = 'any error message';
+                const error = wasm.new_error(message);
+
+                assert.equal(error.message, message);
+            }
+        "#)
+        .test()
+}
+
+#[test]
+fn message() {
+    project()
+        .file("src/lib.rs", r#"
+            #![feature(proc_macro, wasm_custom_section)]
+
+            extern crate wasm_bindgen;
+            use wasm_bindgen::prelude::*;
+            use wasm_bindgen::js;
+            use wasm_bindgen::js::Error;
+
+            #[wasm_bindgen]
+            pub fn error_message(this: &Error) -> js::JsString {
+                this.message()
+            }
+        "#)
+        .file("test.ts", r#"
+            import * as assert from "assert";
+            import * as wasm from "./out";
+
+            export function test() {
+                const message = 'any error message';
+                const error = new Error(message);
+
+                assert.equal(wasm.error_message(error), message);
+            }
+        "#)
+        .test()
+}
+
+#[test]
+fn set_message() {
+    project()
+        .file("src/lib.rs", r#"
+            #![feature(proc_macro, wasm_custom_section)]
+
+            extern crate wasm_bindgen;
+            use wasm_bindgen::prelude::*;
+            use wasm_bindgen::js;
+            use wasm_bindgen::js::Error;
+
+            #[wasm_bindgen]
+            pub fn error_set_message(this: &Error, message: &js::JsString) {
+                this.set_message(message);
+            }
+        "#)
+        .file("test.ts", r#"
+            import * as assert from "assert";
+            import * as wasm from "./out";
+
+            export function test() {
+                const message = 'any error message';
+                const error = new Error();
+                wasm.error_set_message(error, message);
+
+                assert.equal(error.message, message);
+            }
+        "#)
+        .test()
+}
+
+#[test]
+fn name() {
+    project()
+        .file("src/lib.rs", r#"
+            #![feature(proc_macro, wasm_custom_section)]
+
+            extern crate wasm_bindgen;
+            use wasm_bindgen::prelude::*;
+            use wasm_bindgen::js;
+            use wasm_bindgen::js::Error;
+
+            #[wasm_bindgen]
+            pub fn error_name(this: &Error) -> js::JsString {
+                this.name()
+            }
+        "#)
+        .file("test.ts", r#"
+            import * as assert from "assert";
+            import * as wasm from "./out";
+
+            export function test() {
+                const name = 'any error name';
+                const error = new Error();
+                error.name = name;
+
+                assert.equal(wasm.error_name(error), name);
+            }
+        "#)
+        .test()
+}
+
+#[test]
+fn set_name() {
+    project()
+        .file("src/lib.rs", r#"
+            #![feature(proc_macro, wasm_custom_section)]
+
+            extern crate wasm_bindgen;
+            use wasm_bindgen::prelude::*;
+            use wasm_bindgen::js;
+            use wasm_bindgen::js::Error;
+
+            #[wasm_bindgen]
+            pub fn error_set_name(this: &Error, name: &js::JsString) {
+                this.set_name(name);
+            }
+        "#)
+        .file("test.ts", r#"
+            import * as assert from "assert";
+            import * as wasm from "./out";
+
+            export function test() {
+                const name = 'any error name';
+                const error = new Error();
+                wasm.error_set_name(error, name);
+
+                assert.equal(error.name, name);
+            }
+        "#)
+        .test()
+}
+
+#[test]
+fn to_string() {
+    project()
+        .file("src/lib.rs", r#"
+            #![feature(proc_macro, wasm_custom_section)]
+
+            extern crate wasm_bindgen;
+            use wasm_bindgen::prelude::*;
+            use wasm_bindgen::js;
+            use wasm_bindgen::js::Error;
+
+            #[wasm_bindgen]
+            pub fn error_to_string(this: &Error) -> js::JsString {
+                this.to_string()
+            }
+        "#)
+        .file("test.ts", r#"
+            import * as assert from "assert";
+            import * as wasm from "./out";
+
+            export function test() {
+                const error = new Error('error message 1');
+
+                assert.equal(wasm.error_to_string(error), 'Error: error message 1');
+
+                (error.name as any) = undefined;
+                assert.equal(wasm.error_to_string(error), 'Error: error message 1');
+
+                error.name = 'error_name_1';
+                assert.equal(wasm.error_to_string(error), 'error_name_1: error message 1');
+
+                (error.message as any) = undefined;
+                assert.equal(wasm.error_to_string(error), 'error_name_1');
+
+                error.name = 'error_name_2';
+                assert.equal(wasm.error_to_string(error), 'error_name_2');
+            }
+        "#)
+        .test()
+}
diff --git a/tests/all/js_globals/mod.rs b/tests/all/js_globals/mod.rs
index 8685aeb7..e33d9d6d 100644
--- a/tests/all/js_globals/mod.rs
+++ b/tests/all/js_globals/mod.rs
@@ -7,6 +7,7 @@ mod ArrayIterator;
 mod Boolean;
 mod Date;
 mod Function;
+mod Error;
 mod JsString;
 mod Map;
 mod MapIterator;