From 6e4948d9831774905a1e6e8652f58c6614c3d9b5 Mon Sep 17 00:00:00 2001 From: Satoshi Amemiya Date: Fri, 6 Jul 2018 17:07:43 +0900 Subject: [PATCH] Add support for static props of Symbol --- src/js.rs | 66 ++++++ tests/all/js_globals/Symbol.rs | 376 +++++++++++++++++++++++++++++++++ 2 files changed, 442 insertions(+) diff --git a/src/js.rs b/src/js.rs index ff2d5eae..3fcb446c 100644 --- a/src/js.rs +++ b/src/js.rs @@ -1401,4 +1401,70 @@ extern "C" { /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance #[wasm_bindgen(static_method_of = Symbol, getter, structural, js_name = hasInstance)] pub fn has_instance() -> Symbol; + + /// The Symbol.isConcatSpreadable well-known symbol is used to configure if an object should be flattened to + /// its array elements when using the Array.prototype.concat() method. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/isConcatSpreadable + #[wasm_bindgen(static_method_of = Symbol, getter, structural, js_name = isConcatSpreadable)] + pub fn is_concat_spreadable() -> Symbol; + + /// The Symbol.iterator well-known symbol specifies the default iterator for an object. + /// Used by for...of. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator + #[wasm_bindgen(static_method_of = Symbol, getter, structural)] + pub fn iterator() -> Symbol; + + /// The Symbol.match well-known symbol specifies the matching of a regular expression against a string. + /// This function is called by the String.prototype.match() method. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/match + #[wasm_bindgen(static_method_of = Symbol, getter, structural, js_name = match)] + pub fn match_() -> Symbol; + + /// The Symbol.replace well-known symbol specifies the method that replaces matched substrings of a string. + /// This function is called by the String.prototype.replace() method. + /// + /// For more information, see RegExp.prototype[@@replace]() and String.prototype.replace(). + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/replace + #[wasm_bindgen(static_method_of = Symbol, getter, structural)] + pub fn replace() -> Symbol; + + /// The Symbol.search well-known symbol specifies the method that returns the index within a string that matches the regular expression. + /// This function is called by the String.prototype.search() method. + /// + /// For more information, see RegExp.prototype[@@search]() and String.prototype.search(). + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/search + #[wasm_bindgen(static_method_of = Symbol, getter, structural)] + pub fn search() -> Symbol; + + /// The well-known symbol Symbol.species specifies a function-valued property that the constructor function uses to create derived objects. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/species + #[wasm_bindgen(static_method_of = Symbol, getter, structural)] + pub fn species() -> Symbol; + + /// The Symbol.split well-known symbol specifies the method that splits a string at the indices that match a regular expression. + /// This function is called by the String.prototype.split() method. + /// + /// For more information, see RegExp.prototype[@@split]() and String.prototype.split(). + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/split + #[wasm_bindgen(static_method_of = Symbol, getter, structural)] + pub fn split() -> Symbol; + + /// The Symbol.toPrimitive is a symbol that specifies a function valued property that is called to convert an object to a corresponding primitive value. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive + #[wasm_bindgen(static_method_of = Symbol, getter, structural, js_name = toPrimitive)] + pub fn to_primitive() -> Symbol; + + /// The Symbol.toStringTag well-known symbol is a string valued property that is used in the creation of the default string description of an object. + /// It is accessed internally by the Object.prototype.toString() method. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString + #[wasm_bindgen(static_method_of = Symbol, getter, structural, js_name = toStringTag)] + pub fn to_string_tag() -> Symbol; } diff --git a/tests/all/js_globals/Symbol.rs b/tests/all/js_globals/Symbol.rs index 74003089..539759e1 100644 --- a/tests/all/js_globals/Symbol.rs +++ b/tests/all/js_globals/Symbol.rs @@ -40,3 +40,379 @@ fn has_instance() { ) .test() } + +#[test] +fn is_concat_spreadable() { + 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 symbol_is_cancat_spreadable() -> js::Symbol { + js::Symbol::is_concat_spreadable() + } + + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const alpha = ['a', 'b', 'c']; + const numeric = [1, 2, 3]; + let alphaNumeric = alpha.concat(numeric); + + assert.deepEqual(alphaNumeric, ["a", "b", "c", 1, 2, 3]); + + numeric[wasm.symbol_is_cancat_spreadable()] = false; + alphaNumeric = alpha.concat(numeric); + + assert.deepEqual(alphaNumeric, ["a", "b", "c", [1, 2, 3]]); + } + "#, + ) + .test(); +} + +#[test] +fn iterator() { + 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 symbol_iterator() -> js::Symbol { + js::Symbol::iterator() + } + + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const iterable1 = new Object(); + + iterable1[wasm.symbol_iterator()] = function* () { + yield 1; + yield 2; + yield 3; + }; + + assert.deepEqual([...iterable1], [1, 2, 3]); + } + "#, + ) + .test(); +} + +#[test] +fn match_() { + 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 symbol_match() -> js::Symbol { + js::Symbol::match_() + } + + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const regexp1 = /foo/; + assert.throws(() => '/foo/'.startsWith(regexp1)); + + regexp1[wasm.symbol_match()] = false; + + assert.ok('/foo/'.startsWith(regexp1)); + + assert.equal('/baz/'.endsWith(regexp1), false); + } + "#, + ) + .test(); +} + +#[test] +fn replace() { + 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 symbol_replace() -> js::Symbol { + js::Symbol::replace() + } + + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + class Replace1 { + constructor(value) { + this.value = value; + } + [Symbol.replace](string) { + return `s/${string}/${this.value}/g`; + } + } + + export function test() { + assert.equal('foo'.replace(new Replace1('bar')), 's/foo/bar/g'); + } + "#, + ) + .test(); +} + +#[test] +fn search() { + 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 symbol_search() -> js::Symbol { + js::Symbol::search() + } + + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + class Search1 { + constructor(value) { + this.value = value; + } + + [wasm.symbol_search()](string) { + return string.indexOf(this.value); + } + } + + export function test() { + assert.equal('foobar'.search(new Search1('bar')), 3); + } + "#, + ) + .test(); +} + +#[test] +fn species() { + 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 symbol_species() -> js::Symbol { + js::Symbol::species() + } + + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + class Array1 extends Array { + static get [wasm.symbol_species()]() { return Array; } + } + + export function test() { + const a = new Array1(1, 2, 3); + const mapped = a.map(x => x * x); + + assert.equal(mapped instanceof Array1, false); + + assert.ok(mapped instanceof Array); + } + "#, + ) + .test(); +} + +#[test] +fn split() { + 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 symbol_split() -> js::Symbol { + js::Symbol::split() + } + + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + class Split1 { + constructor(value) { + this.value = value; + } + + [wasm.symbol_split()](string) { + var index = string.indexOf(this.value); + return this.value + string.substr(0, index) + "/" + + string.substr(index + this.value.length); + } + } + + export function test() { + assert.equal('foobar'.split(new Split1('foo')), 'foo/bar'); + } + "#, + ) + .test(); +} + +#[test] +fn to_primitive() { + 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 symbol_to_primitive() -> js::Symbol { + js::Symbol::to_primitive() + } + + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object1 = { + [wasm.symbol_to_primitive()](hint) { + if (hint == 'number') { + return 42; + } + return null; + } + }; + + assert.equal(+object1, 42); + } + "#, + ) + .test(); +} + +#[test] +fn to_string_tag() { + 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 symbol_to_string_tag() -> js::Symbol { + js::Symbol::to_string_tag() + } + + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + class ValidatorClass { + get [Symbol.toStringTag]() { + return 'Validator'; + } + } + + export function test() { + assert.equal(Object.prototype.toString.call(new ValidatorClass()), '[object Validator]'); + } + "#, + ) + .test(); +}