diff --git a/src/js.rs b/src/js.rs index 1d3305729..bc3503f83 100644 --- a/src/js.rs +++ b/src/js.rs @@ -1071,6 +1071,114 @@ extern { pub fn revocable(target: &JsValue, handler: &Object) -> Object; } +// Reflect +#[wasm_bindgen] +extern "C" { + pub type Reflect; + + /// The static Reflect.apply() method calls a target function with arguments as specified. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply + #[wasm_bindgen(static_method_of = Reflect)] + pub fn apply(target: &Function, this_argument: &JsValue, arguments_list: &Array) -> JsValue; + + /// The static Reflect.construct() method acts like the new operator, but as a function. + /// It is equivalent to calling new target(...args). It gives also the added option to + /// specify a different prototype. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct + #[wasm_bindgen(static_method_of = Reflect)] + pub fn construct(target: &Function, arguments_list: &Array) -> JsValue; + #[wasm_bindgen(static_method_of = Reflect, js_name = construct)] + pub fn construct_with_new_target(target: &Function, arguments_list: &Array, new_target: &Function) -> JsValue; + + /// The static Reflect.defineProperty() method is like Object.defineProperty() + /// but returns a Boolean. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/defineProperty + #[wasm_bindgen(static_method_of = Reflect, js_name = defineProperty)] + pub fn define_property(target: &Object, property_key: &JsValue, attributes: &Object) -> bool; + + /// The static Reflect.deleteProperty() method allows to delete properties. + /// It is like the delete operator as a function. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/deleteProperty + #[wasm_bindgen(static_method_of = Reflect, js_name = deleteProperty)] + pub fn delete_property(target: &Object, key: &JsValue) -> bool; + + /// The static Reflect.get() method works like getting a property from + /// an object (target[propertyKey]) as a function. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get + #[wasm_bindgen(static_method_of = Reflect)] + pub fn get(target: &Object, key: &JsValue) -> JsValue; + + /// The static Reflect.getOwnPropertyDescriptor() method is similar to + /// Object.getOwnPropertyDescriptor(). It returns a property descriptor + /// of the given property if it exists on the object, undefined otherwise. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getOwnPropertyDescriptor + #[wasm_bindgen(static_method_of = Reflect, js_name = getOwnPropertyDescriptor)] + pub fn get_own_property_descriptor(target: &Object, property_key: &JsValue) -> JsValue; + + /// The static Reflect.getPrototypeOf() method is almost the same + /// method as Object.getPrototypeOf(). It returns the prototype + /// (i.e. the value of the internal [[Prototype]] property) of + /// the specified object. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf + #[wasm_bindgen(static_method_of = Reflect, js_name = getPrototypeOf)] + pub fn get_prototype_of(target: &Object) -> Object; + + /// The static Reflect.has() method works like the in operator as a function. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/has + #[wasm_bindgen(static_method_of = Reflect)] + pub fn has(target: &Object, property_key: &JsValue) -> bool; + + /// The static Reflect.isExtensible() method determines if an object is extensible + /// (whether it can have new properties added to it). It is similar to + /// Object.isExtensible(), but with some differences. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/isExtensible + #[wasm_bindgen(static_method_of = Reflect, js_name = isExtensible)] + pub fn is_extensible(target: &Object) -> bool; + + /// The static Reflect.ownKeys() method returns an array of the + /// target object's own property keys. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys + #[wasm_bindgen(static_method_of = Reflect, js_name = ownKeys)] + pub fn own_keys(target: &Object) -> Array; + + /// The static Reflect.preventExtensions() method prevents new + /// properties from ever being added to an object (i.e. prevents + /// future extensions to the object). It is similar to + /// Object.preventExtensions(), but with some differences. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/preventExtensions + #[wasm_bindgen(static_method_of = Reflect, js_name = preventExtensions)] + pub fn prevent_extensions(target: &Object) -> bool; + + /// The static Reflect.set() method works like setting a + /// property on an object. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set + #[wasm_bindgen(static_method_of = Reflect)] + pub fn set(target: &Object, property_key: &JsValue, value: &JsValue) -> bool; + #[wasm_bindgen(static_method_of = Reflect, js_name = set)] + pub fn set_with_receiver(target: &Object, property_key: &JsValue, value: &JsValue, receiver: &Object) -> bool; + + /// The static Reflect.setPrototypeOf() method is the same + /// method as Object.setPrototypeOf(). It sets the prototype + /// (i.e., the internal [[Prototype]] property) of a specified + /// object to another object or to null. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf + #[wasm_bindgen(static_method_of = Reflect, js_name = setPrototypeOf)] + pub fn set_prototype_of(target: &Object, prototype: &JsValue) -> bool; +} + // Set #[wasm_bindgen] extern { diff --git a/tests/all/js_globals/Reflect.rs b/tests/all/js_globals/Reflect.rs new file mode 100644 index 000000000..a95572d52 --- /dev/null +++ b/tests/all/js_globals/Reflect.rs @@ -0,0 +1,601 @@ +#![allow(non_snake_case)] + +use project; + +#[test] +fn apply() { + 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 apply(target: &js::Function, this_argument: &JsValue, arguments_list: &js::Array) -> JsValue { + js::Reflect::apply(target, this_argument, arguments_list) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + assert.equal(wasm.apply("".charAt, "ponies", [3]), "i"); + } + "#, + ) + .test() +} + +#[test] +fn construct() { + 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 construct(target: &js::Function, arguments_list: &js::Array) -> JsValue { + js::Reflect::construct(target, arguments_list) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + class Rectangle { + constructor(x, y){ + this.x = x, + this.y = y + } + + static eq(x, y) { + return x === y; + } + + } + + const args = [10, 10]; + + assert.equal(wasm.construct(Rectangle, args).x, 10); + } + "#, + ) + .test() +} + +#[test] +fn construct_with_new_target() { + 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 construct_with_new_target(target: &js::Function, arguments_list: &js::Array, new_target: &js::Function) -> JsValue { + js::Reflect::construct_with_new_target(target, arguments_list, new_target) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + class Rectangle { + constructor(x, y){ + this.x = x, + this.y = y + } + + static eq(x, y) { + return x === y; + } + + } + + class Rectangle2 { + constructor(x, y){ + this.x = x, + this.y = y + } + + static eq(x, y) { + return x === y; + } + + } + + const args = [10, 10]; + + assert.equal(wasm.construct_with_new_target(Rectangle, args, Rectangle2).x, 10); + } + "#, + ) + .test() +} + +#[test] +fn define_property() { + 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 define_property(target: &js::Object, property_key: &JsValue, attributes: &js::Object) -> bool { + js::Reflect::define_property(target, property_key, attributes) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = {}; + + assert.equal(wasm.define_property(object, "key", { value: 42}), true) + } + "#, + ) + .test() +} + +#[test] +fn delete_property() { + 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 delete_property(target: &js::Object, property_key: &JsValue) -> bool { + js::Reflect::delete_property(target, property_key) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = { + property: 42 + }; + + wasm.delete_property(object, "property"); + + assert.equal(object.property, undefined); + + const array = [1, 2, 3, 4, 5]; + wasm.delete_property(array, 3); + + assert.equal(array[3], undefined); + } + "#, + ) + .test() +} + +#[test] +fn get() { + 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 get(target: &js::Object, property_key: &JsValue) -> JsValue { + js::Reflect::get(target, property_key) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = { + property: 42 + }; + + assert.equal(wasm.get(object, "property"), 42); + + const array = [1, 2, 3, 4, 5]; + + assert.equal(wasm.get(array, 3), 4); + } + "#, + ) + .test() +} + +#[test] +fn get_own_property_descriptor() { + 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 get_own_property_descriptor(target: &js::Object, property_key: &JsValue) -> JsValue { + js::Reflect::get_own_property_descriptor(target, property_key) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = { + property: 42 + }; + + assert.equal(wasm.get_own_property_descriptor(object, "property").value, 42); + assert.equal(wasm.get_own_property_descriptor(object, "property1"), undefined); + } + "#, + ) + .test() +} + +#[test] +fn get_prototype_of() { + 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 get_prototype_of(target: &js::Object) -> js::Object { + js::Reflect::get_prototype_of(target) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = { + property: 42 + }; + const array = [1, 2, 3]; + + assert.equal(wasm.get_prototype_of(object), Object.prototype); + assert.equal(wasm.get_prototype_of(array), Array.prototype); + } + "#, + ) + .test() +} + +#[test] +fn has() { + 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 has(target: &js::Object, property_key: &JsValue) -> bool { + js::Reflect::has(target, property_key) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = { + property: 42 + }; + const array = [1, 2, 3, 4] + + assert.equal(wasm.has(object, "property"), true); + assert.equal(wasm.has(object, "foo"), false); + assert.equal(wasm.has(array, 3), true); + assert.equal(wasm.has(array, 10), false); + } + "#, + ) + .test() +} + +#[test] +fn is_extensible() { + 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 is_extensible(target: &js::Object) -> bool { + js::Reflect::is_extensible(target) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = { + property: 42 + }; + + assert.equal(wasm.is_extensible(object), true); + + Reflect.preventExtensions(object); + + assert.equal(wasm.is_extensible(object), false); + + const object2 = Object.seal({}); + + assert.equal(wasm.is_extensible(object2), false); + } + "#, + ) + .test() +} + +#[test] +fn own_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 own_keys(target: &js::Object) -> js::Array { + js::Reflect::own_keys(target) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = { + property: 42 + }; + const array = []; + + assert.equal(wasm.own_keys(object)[0], "property"); + assert.equal(wasm.own_keys(array)[0], "length"); + } + "#, + ) + .test() +} + +#[test] +fn prevent_extensions() { + 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 prevent_extensions(target: &js::Object) -> bool { + js::Reflect::prevent_extensions(target) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object1 = {}; + + wasm.prevent_extensions(object1); + + assert.equal(Reflect.isExtensible(object1), false); + } + "#, + ) + .test() +} + +#[test] +fn set() { + 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 set(target: &js::Object, property_key: &JsValue, value: &JsValue) -> bool { + js::Reflect::set(target, property_key, value) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = {}; + const array = [1, 2, 3, 4]; + assert.equal(wasm.set(object, "key", "value"), true); + assert.equal(wasm.set(array, 0, 100), true); + + assert.equal(Reflect.get(object, "key"), "value"); + assert.equal(array[0], 100); + } + "#, + ) + .test() +} + +#[test] +fn set_with_receiver() { + 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 set_with_receiver(target: &js::Object, property_key: &JsValue, value: &JsValue, receiver: &js::Object) -> bool { + js::Reflect::set_with_receiver(target, property_key, value, receiver) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = {}; + const array = [1, 2, 3, 4]; + assert.equal(wasm.set_with_receiver({}, "key", "value", object), true); + assert.equal(wasm.set_with_receiver([], 0, 100, array), true); + + assert.equal(Reflect.get(object, "key"), "value"); + assert.equal(array[0], 100); + } + "#, + ) + .test() +} + +#[test] +fn set_prototype_of() { + 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 set_prototype_of(target: &js::Object, prototype: &JsValue) -> bool { + js::Reflect::set_prototype_of(target, prototype) + } + "#, + ) + .file( + "test.js", + r#" + import * as assert from "assert"; + import * as wasm from "./out"; + + export function test() { + const object = {}; + assert.equal(wasm.set_prototype_of(object, Object.prototype), true); + assert.equal(Object.getPrototypeOf(object), Object.prototype); + assert.equal(wasm.set_prototype_of(object, null), true); + assert.equal(Object.getPrototypeOf(object), null); + } + "#, + ) + .test() +} \ No newline at end of file diff --git a/tests/all/js_globals/mod.rs b/tests/all/js_globals/mod.rs index a41542f31..238adad7d 100644 --- a/tests/all/js_globals/mod.rs +++ b/tests/all/js_globals/mod.rs @@ -17,6 +17,7 @@ mod Math; mod Number; mod Object; mod Proxy; +mod Reflect; mod Set; mod SetIterator; mod Symbol;