diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 778af3178317..1967f608c29c 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -312,6 +312,7 @@ selfhosting_srcs := \ $(srcdir)/builtin/Map.js \ $(srcdir)/builtin/Number.js \ $(srcdir)/builtin/Object.js \ + $(srcdir)/builtin/Reflect.js \ $(srcdir)/builtin/RegExp.js \ $(srcdir)/builtin/String.js \ $(srcdir)/builtin/Set.js \ diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 51ef738be001..a4cffb1d08f1 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -390,25 +390,6 @@ js::obj_valueOf(JSContext* cx, unsigned argc, Value* vp) return true; } -// ES6 draft rev27 (2014/08/24) 19.1.2.9 Object.getPrototypeOf(O) -bool -js::obj_getPrototypeOf(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - /* Steps 1-2. */ - RootedObject obj(cx, ToObject(cx, args.get(0))); - if (!obj) - return false; - - /* Step 3. */ - RootedObject proto(cx); - if (!GetPrototype(cx, obj, &proto)) - return false; - args.rval().setObjectOrNull(proto); - return true; -} - static bool obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) { @@ -711,7 +692,7 @@ js::obj_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp) // Steps 3-4. RootedId id(cx); - if (!ValueToId(cx, args.get(1), &id)) + if (!ToPropertyKey(cx, args.get(1), &id)) return false; // Steps 5-7. @@ -758,11 +739,9 @@ js::IdToStringOrSymbol(JSContext* cx, HandleId id, MutableHandleValue result) return true; } -namespace js { - /* ES6 draft rev 25 (2014 May 22) 19.1.2.8.1 */ bool -GetOwnPropertyKeys(JSContext* cx, const JS::CallArgs& args, unsigned flags) +js::GetOwnPropertyKeys(JSContext* cx, const JS::CallArgs& args, unsigned flags) { // Steps 1-2. RootedObject obj(cx, ToObject(cx, args.get(0))); @@ -794,8 +773,6 @@ GetOwnPropertyKeys(JSContext* cx, const JS::CallArgs& args, unsigned flags) return true; } -} // namespace js - bool js::obj_getOwnPropertyNames(JSContext* cx, unsigned argc, Value* vp) { @@ -865,25 +842,6 @@ obj_defineProperties(JSContext* cx, unsigned argc, Value* vp) return DefineProperties(cx, obj, props); } -// ES6 draft rev27 (2014/08/24) 19.1.2.11 Object.isExtensible(O) -static bool -obj_isExtensible(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1. - bool extensible = false; - - // Step 2. - if (args.get(0).isObject()) { - RootedObject obj(cx, &args.get(0).toObject()); - if (!IsExtensible(cx, obj, &extensible)) - return false; - } - args.rval().setBoolean(extensible); - return true; -} - // ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O) static bool obj_preventExtensions(JSContext* cx, unsigned argc, Value* vp) @@ -1067,23 +1025,23 @@ static const JSPropertySpec object_properties[] = { }; static const JSFunctionSpec object_static_methods[] = { - JS_SELF_HOSTED_FN("assign", "ObjectStaticAssign", 2,JSPROP_DEFINE_LATE), - JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0), - JS_FN("setPrototypeOf", obj_setPrototypeOf, 2,0), - JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0), - JS_FN("keys", obj_keys, 1,0), - JS_FN("is", obj_is, 2,0), - JS_FN("defineProperty", obj_defineProperty, 3,0), - JS_FN("defineProperties", obj_defineProperties, 2,0), - JS_FN("create", obj_create, 2,0), - JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0), - JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols, 1,0), - JS_FN("isExtensible", obj_isExtensible, 1,0), - JS_FN("preventExtensions", obj_preventExtensions, 1,0), - JS_FN("freeze", obj_freeze, 1,0), - JS_FN("isFrozen", obj_isFrozen, 1,0), - JS_FN("seal", obj_seal, 1,0), - JS_FN("isSealed", obj_isSealed, 1,0), + JS_SELF_HOSTED_FN("assign", "ObjectStaticAssign", 2, JSPROP_DEFINE_LATE), + JS_SELF_HOSTED_FN("getPrototypeOf", "ObjectGetPrototypeOf", 1, JSPROP_DEFINE_LATE), + JS_FN("setPrototypeOf", obj_setPrototypeOf, 2, 0), + JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2, 0), + JS_FN("keys", obj_keys, 1, 0), + JS_FN("is", obj_is, 2, 0), + JS_FN("defineProperty", obj_defineProperty, 3, 0), + JS_FN("defineProperties", obj_defineProperties, 2, 0), + JS_FN("create", obj_create, 2, 0), + JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1, 0), + JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols, 1, 0), + JS_SELF_HOSTED_FN("isExtensible", "ObjectIsExtensible", 1, JSPROP_DEFINE_LATE), + JS_FN("preventExtensions", obj_preventExtensions, 1, 0), + JS_FN("freeze", obj_freeze, 1, 0), + JS_FN("isFrozen", obj_isFrozen, 1, 0), + JS_FN("seal", obj_seal, 1, 0), + JS_FN("isSealed", obj_isSealed, 1, 0), JS_FS_END }; diff --git a/js/src/builtin/Object.h b/js/src/builtin/Object.h index 2efd552fe23f..889701482757 100644 --- a/js/src/builtin/Object.h +++ b/js/src/builtin/Object.h @@ -54,6 +54,9 @@ obj_getPrototypeOf(JSContext* cx, unsigned argc, JS::Value* vp); bool obj_hasOwnProperty(JSContext* cx, unsigned argc, JS::Value* vp); +bool +obj_isExtensible(JSContext* cx, unsigned argc, JS::Value* vp); + // Exposed so SelfHosting.cpp can use it in the OwnPropertyKeys intrinsic bool GetOwnPropertyKeys(JSContext* cx, const JS::CallArgs& args, unsigned flags); diff --git a/js/src/builtin/Object.js b/js/src/builtin/Object.js index 048513017faf..f2316b89c665 100644 --- a/js/src/builtin/Object.js +++ b/js/src/builtin/Object.js @@ -41,6 +41,16 @@ function ObjectStaticAssign(target, firstSource) { return to; } +/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.9. */ +function ObjectGetPrototypeOf(obj) { + return std_Reflect_getPrototypeOf(ToObject(obj)); +} + +/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.11. */ +function ObjectIsExtensible(obj) { + return IsObject(obj) && std_Reflect_isExtensible(obj); +} + function ObjectDefineSetter(name, setter) { var object; if (this === null || this === undefined) @@ -96,7 +106,7 @@ function ObjectLookupSetter(name) { return desc.set; return undefined; } - object = std_Object_getPrototypeOf(object); + object = std_Reflect_getPrototypeOf(object); } while (object !== null); } @@ -111,6 +121,7 @@ function ObjectLookupGetter(name) { return desc.get; return undefined; } - object = std_Object_getPrototypeOf(object); + object = std_Reflect_getPrototypeOf(object); } while (object !== null); } + diff --git a/js/src/builtin/Reflect.cpp b/js/src/builtin/Reflect.cpp index 6ef13d0b9462..a9f784fdfa44 100644 --- a/js/src/builtin/Reflect.cpp +++ b/js/src/builtin/Reflect.cpp @@ -6,15 +6,366 @@ #include "builtin/Reflect.h" +#include "jscntxt.h" + +#include "vm/Stack.h" + using namespace js; + +/*** Reflect methods *****************************************************************************/ + +/* + * ES draft rev 32 (2015 Feb 2) 7.3.17 CreateListFromArrayLike. + * The elementTypes argument is not supported. The result list is + * pushed to *args. + */ +static bool +InitArgsFromArrayLike(JSContext* cx, HandleValue v, InvokeArgs* args, bool construct) +{ + // Step 3. + RootedObject obj(cx, NonNullObject(cx, v)); + if (!obj) + return false; + + // Steps 4-5. + uint32_t len; + if (!GetLengthProperty(cx, obj, &len)) + return false; + + // Allocate space for the arguments. + if (len > ARGS_LENGTH_MAX) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TOO_MANY_FUN_APPLY_ARGS); + return false; + } + if (!args->init(len, construct)) + return false; + + // Steps 6-8. + for (uint32_t index = 0; index < len; index++) { + if (!GetElement(cx, obj, obj, index, (*args)[index])) + return false; + } + + // Step 9. + return true; +} + +/* ES6 26.1.1 Reflect.apply(target, thisArgument, argumentsList) */ +static bool +Reflect_apply(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + if (!IsCallable(args.get(0))) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, + "Reflect.apply argument"); + return false; + } + + // Steps 2-3. + FastInvokeGuard fig(cx, args.get(0)); + InvokeArgs& invokeArgs = fig.args(); + if (!InitArgsFromArrayLike(cx, args.get(2), &invokeArgs, false)) + return false; + invokeArgs.setCallee(args.get(0)); + invokeArgs.setThis(args.get(1)); + + // Steps 4-5. This is specified to be a tail call, but isn't. + if (!fig.invoke(cx)) + return false; + args.rval().set(invokeArgs.rval()); + return true; +} + +/* ES6 26.1.3 Reflect.defineProperty(target, propertyKey, attributes) */ +static bool +Reflect_defineProperty(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + RootedObject obj(cx, NonNullObject(cx, args.get(0))); + if (!obj) + return false; + + // Steps 2-3. + RootedValue propertyKey(cx, args.get(1)); + RootedId key(cx); + if (!ToPropertyKey(cx, propertyKey, &key)) + return false; + + // Steps 4-5. + Rooted desc(cx); + if (!ToPropertyDescriptor(cx, args.get(2), true, &desc)) + return false; + + // Step 6. + ObjectOpResult result; + if (!DefineProperty(cx, obj, key, desc, result)) + return false; + args.rval().setBoolean(bool(result)); + return true; +} + +/* ES6 26.1.4 Reflect.deleteProperty (target, propertyKey) */ +static bool +Reflect_deleteProperty(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + RootedObject target(cx, NonNullObject(cx, args.get(0))); + if (!target) + return false; + + // Steps 2-3. + RootedValue propertyKey(cx, args.get(1)); + RootedId key(cx); + if (!ToPropertyKey(cx, propertyKey, &key)) + return false; + + // Step 4. + ObjectOpResult result; + if (!DeleteProperty(cx, target, key, result)) + return false; + args.rval().setBoolean(bool(result)); + return true; +} + +#if 0 +/* + * ES6 26.1.5 Reflect.enumerate(target) + * + * TODO: + * - redefine enumeration in terms of iterators without losing performance + * - support iterators in Proxies + */ +static bool +Reflect_enumerate(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + RootedObject obj(cx, NonNullObject(cx, args.get(0))); + if (!obj) + return false; + + // Step 2. + RootedObject iterator(cx); + if (!Enumerate(cx, obj, &iterator)) + return false; + args.rval().setObject(*iterator); + return true; +} +#endif + +/* + * ES6 26.1.6 Reflect.get(target, propertyKey [, receiver]) + * + * Primitive receivers are not supported yet (see bug 603201). + */ +static bool +Reflect_get(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + RootedObject obj(cx, NonNullObject(cx, args.get(0))); + if (!obj) + return false; + + // Steps 2-3. + RootedValue propertyKey(cx, args.get(1)); + RootedId key(cx); + if (!ToPropertyKey(cx, propertyKey, &key)) + return false; + + // Step 4. + RootedValue receiver(cx, argc > 2 ? args[2] : args.get(0)); + + // Non-standard hack: Throw a TypeError if the receiver isn't an object. + // See bug 603201. + RootedObject receiverObj(cx, NonNullObject(cx, receiver)); + if (!receiverObj) + return false; + + // Step 5. + return GetProperty(cx, obj, receiverObj, key, args.rval()); +} + +/* ES6 26.1.7 Reflect.getOwnPropertyDescriptor(target, propertyKey) */ +static bool +Reflect_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp) +{ + // Step 1. + CallArgs args = CallArgsFromVp(argc, vp); + if (!NonNullObject(cx, args.get(0))) + return false; + + // The other steps are identical to ES6 draft rev 32 (2015 Feb 2) 19.1.2.6 + // Object.getOwnPropertyDescriptor. + return js::obj_getOwnPropertyDescriptor(cx, argc, vp); +} + +/* ES6 26.1.8 Reflect.getPrototypeOf(target) */ +bool +js::Reflect_getPrototypeOf(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + RootedObject target(cx, NonNullObject(cx, args.get(0))); + if (!target) + return false; + + // Step 2. + RootedObject proto(cx); + if (!GetPrototype(cx, target, &proto)) + return false; + args.rval().setObjectOrNull(proto); + return true; +} + +/* ES6 draft 26.1.10 Reflect.isExtensible(target) */ +bool +js::Reflect_isExtensible(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + RootedObject target(cx, NonNullObject(cx, args.get(0))); + if (!target) + return false; + + // Step 2. + bool extensible; + if (!IsExtensible(cx, target, &extensible)) + return false; + args.rval().setBoolean(extensible); + return true; +} + +/* ES6 26.1.11 Reflect.ownKeys(target) */ +static bool +Reflect_ownKeys(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + if (!NonNullObject(cx, args.get(0))) + return false; + + // Steps 2-4. + return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS); +} + +/* ES6 26.1.12 Reflect.preventExtensions(target) */ +static bool +Reflect_preventExtensions(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + RootedObject target(cx, NonNullObject(cx, args.get(0))); + if (!target) + return false; + + // Step 2. + ObjectOpResult result; + if (!PreventExtensions(cx, target, result)) + return false; + args.rval().setBoolean(bool(result)); + return true; +} + +/* ES6 26.1.13 Reflect.set(target, propertyKey, V [, receiver]) */ +static bool +Reflect_set(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + RootedObject target(cx, NonNullObject(cx, args.get(0))); + if (!target) + return false; + + // Steps 2-3. + RootedValue propertyKey(cx, args.get(1)); + RootedId key(cx); + if (!ToPropertyKey(cx, propertyKey, &key)) + return false; + + // Step 4. + RootedValue receiver(cx, argc > 3 ? args[3] : args.get(0)); + + // Step 5. + ObjectOpResult result; + RootedValue value(cx, args.get(2)); + if (!SetProperty(cx, target, key, value, receiver, result)) + return false; + args.rval().setBoolean(bool(result)); + return true; +} + +/* + * ES6 26.1.3 Reflect.setPrototypeOf(target, proto) + * + * The specification is not quite similar enough to Object.setPrototypeOf to + * share code. + */ +static bool +Reflect_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + RootedObject obj(cx, NonNullObject(cx, args.get(0))); + if (!obj) + return false; + + // Step 2. + if (!args.get(1).isObjectOrNull()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, + "Reflect.setPrototypeOf", "an object or null", + InformalValueTypeName(args.get(1))); + return false; + } + RootedObject proto(cx, args.get(1).toObjectOrNull()); + + // Step 4. + ObjectOpResult result; + if (!SetPrototype(cx, obj, proto, result)) + return false; + args.rval().setBoolean(bool(result)); + return true; +} + +static const JSFunctionSpec methods[] = { + JS_FN("apply", Reflect_apply, 3, 0), + // JS_FN("construct", Reflect_construct, 2, 0), + JS_FN("defineProperty", Reflect_defineProperty, 3, 0), + JS_FN("deleteProperty", Reflect_deleteProperty, 2, 0), + // JS_FN("enumerate", Reflect_enumerate, 1, 0), + JS_FN("get", Reflect_get, 2, 0), + JS_FN("getOwnPropertyDescriptor", Reflect_getOwnPropertyDescriptor, 2, 0), + JS_FN("getPrototypeOf", Reflect_getPrototypeOf, 1, 0), + JS_SELF_HOSTED_FN("has", "Reflect_has", 2, 0), + JS_FN("isExtensible", Reflect_isExtensible, 1, 0), + JS_FN("ownKeys", Reflect_ownKeys, 1, 0), + JS_FN("preventExtensions", Reflect_preventExtensions, 1, 0), + JS_FN("set", Reflect_set, 3, 0), + JS_FN("setPrototypeOf", Reflect_setPrototypeOf, 2, 0), + JS_FS_END +}; + + +/*** Setup **************************************************************************************/ + JSObject* js::InitReflect(JSContext* cx, HandleObject obj) { - static const JSFunctionSpec static_methods[] = { - JS_FS_END - }; - RootedObject proto(cx, obj->as().getOrCreateObjectPrototype(cx)); if (!proto) return nullptr; @@ -22,7 +373,7 @@ js::InitReflect(JSContext* cx, HandleObject obj) RootedObject reflect(cx, NewObjectWithGivenProto(cx, proto, SingletonObject)); if (!reflect) return nullptr; - if (!JS_DefineFunctions(cx, reflect, static_methods)) + if (!JS_DefineFunctions(cx, reflect, methods)) return nullptr; RootedValue value(cx, ObjectValue(*reflect)); diff --git a/js/src/builtin/Reflect.h b/js/src/builtin/Reflect.h index 60bc67538a50..b227a6614ad4 100644 --- a/js/src/builtin/Reflect.h +++ b/js/src/builtin/Reflect.h @@ -16,4 +16,14 @@ InitReflect(JSContext* cx, js::HandleObject obj); } +namespace js { + +extern bool +Reflect_getPrototypeOf(JSContext* cx, unsigned argc, Value* vp); + +extern bool +Reflect_isExtensible(JSContext* cx, unsigned argc, Value* vp); + +} + #endif /* builtin_Reflect_h */ diff --git a/js/src/builtin/Reflect.js b/js/src/builtin/Reflect.js new file mode 100644 index 000000000000..e31f11e9ac8b --- /dev/null +++ b/js/src/builtin/Reflect.js @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// ES6 draft rev 32 (2015 Feb 2) 26.1.9 +function Reflect_has(target, propertyKey) { + // Step 1. + if (!IsObject(target)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, target)); + + // Steps 2-4 are identical to the runtime semantics of the "in" operator. + return propertyKey in target; +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetArray2.js b/js/src/jit-test/tests/proxy/testDirectProxySetArray2.js index 5cc60fee082d..1025aa04f96f 100644 --- a/js/src/jit-test/tests/proxy/testDirectProxySetArray2.js +++ b/js/src/jit-test/tests/proxy/testDirectProxySetArray2.js @@ -1,10 +1,8 @@ -"use strict"; +// Direct proxies pass through the receiver argument to [[Set]] to their targets. +// This also tests that an ordinary object's [[Set]] method can change the length +// of an array passed as the receiver. -if (this.Reflect && Reflect.set) - throw new Error("Congrats on implementing Reflect.set! Uncomment this test for 1 karma point."); - -/* -load("asserts.js"); +load(libdir + "asserts.js"); var a = [0, 1, 2, 3]; var p = new Proxy({}, {}); @@ -12,4 +10,3 @@ Reflect.set(p, "length", 2, a); assertEq("length" in p, false); assertEq(a.length, 2); assertDeepEq(a, [0, 1]); -*/ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 2a08cf33c802..d8f9adca8cde 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -595,6 +595,19 @@ ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp) return ToPrimitive(cx, obj, preferredType, vp); } +/* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */ +inline bool +ToPropertyKey(JSContext* cx, Value argument, MutableHandleId result) +{ + // Steps 1-2. + RootedValue key(cx, argument); + if (!ToPrimitive(cx, JSTYPE_STRING, &key)) + return false; + + // Steps 3-4. + return ValueToId(cx, key, result); +} + /* * Return true if this is a compiler-created internal function accessed by * its own object. Such a function object must not be accessible to script diff --git a/js/src/tests/ecma_6/Reflect/apply.js b/js/src/tests/ecma_6/Reflect/apply.js new file mode 100644 index 000000000000..d3702e6cc317 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/apply.js @@ -0,0 +1,131 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.apply calls functions. +assertEq(Reflect.apply(Math.floor, undefined, [1.75]), 1); + +// Reflect.apply requires a target object that's callable. +class clsX { constructor() {} } // classes are not callable +var nonCallable = [{}, [], clsX]; +for (var value of nonCallable) { + assertThrowsInstanceOf(() => Reflect.apply(nonCallable), TypeError); +} + +// When target is not callable, Reflect.apply does not try to get argumentList.length before throwing. +var hits = 0; +var bogusArgumentList = {get length() { hit++; throw "FAIL";}}; +assertThrowsInstanceOf(() => Reflect.apply({callable: false}, null, bogusArgumentList), + TypeError); +assertEq(hits, 0); + +// Reflect.apply works on a range of different callable objects. +// Builtin functions (we also tested Math.floor above): +assertEq(Reflect.apply(String.fromCharCode, + undefined, + [104, 101, 108, 108, 111]), + "hello"); + +// Builtin methods: +assertEq(Reflect.apply(RegExp.prototype.exec, + /ab/, + ["confabulation"]).index, + 4); + +// Builtin methods of primitive objects: +assertEq(Reflect.apply("".charAt, + "ponies", + [3]), + "i"); + +// Bound functions: +assertEq(Reflect.apply(function () { return this; }.bind(Math), + Function, + []), + Math); +assertEq(Reflect.apply(Array.prototype.concat.bind([1, 2], [3]), + [4, 5], + [[6, 7, 8]]).join(), + "1,2,3,6,7,8"); + +// Generator functions: +function* g(arg) { yield "pass" + arg; } +assertEq(Reflect.apply(g, + undefined, + ["word"]).next().value, + "password"); + +// Proxies: +function f() { return 13; } +assertEq(Reflect.apply(new Proxy(f, {}), + undefined, + []), + 13); + +// Cross-compartment wrappers: +var gw = newGlobal(); +assertEq(Reflect.apply(gw.parseInt, + undefined, + ["45"]), + 45); +assertEq(Reflect.apply(gw.Symbol.for, + undefined, + ["moon"]), + Symbol.for("moon")); + +gw.eval("function q() { return q; }"); +assertEq(Reflect.apply(gw.q, + undefined, + []), + gw.q); + + +// Exceptions are propagated. +var nope = new Error("nope"); +function fail() { + throw nope; +} +assertThrowsValue(() => Reflect.apply(fail, undefined, []), + nope); + +// Exceptions thrown by cross-compartment wrappers are re-wrapped for the +// calling compartment. +var gxw = gw.eval("var x = new Error('x'); x"); +gw.eval("function fail() { throw x; }"); +assertThrowsValue(() => Reflect.apply(gw.fail, undefined, []), + gxw); + +// The thisArgument is passed to the target function as the 'this' value. +var obj = {}; +hits = 0; +assertEq(Reflect.apply(function () { hits++; assertEq(this, obj); }, + obj, + []), + undefined); +assertEq(hits, 1); + +// Primitive values can be thisArgument. +function strictThis() { "use strict"; return this; } +for (var value of [null, undefined, 0, -0, NaN, Symbol("moon")]) { + assertEq(Reflect.apply(strictThis, value, []), + value); +} + +// If the target is a non-strict function and thisArgument is a primitive value +// other than null or undefined, then thisArgument is converted to a wrapper +// object. +var testValues = [true, 1e9, "ok", Symbol("ok")]; +function nonStrictThis(expected) { + assertEq(typeof this, "object"); + assertEq(Reflect.apply(Object.prototype.toString, this, []).toLowerCase(), expected); + return "ok"; +} +for (var value of testValues) { + assertEq(Reflect.apply(nonStrictThis, + value, + ["[object " + typeof value + "]"]), + "ok"); +} + +// For more Reflect.apply tests, see target.js and argumentsList.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/argumentsList.js b/js/src/tests/ecma_6/Reflect/argumentsList.js new file mode 100644 index 000000000000..3d59bee79a2d --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/argumentsList.js @@ -0,0 +1,168 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Tests for the argumentList argument to Reflect.apply and Reflect.construct. + +if (Reflect.construct) { + throw new Error("Congratulations on implementing Reflect.construct! " + + "Please uncomment the Reflect.construct tests below."); +} + +// Reflect.apply and Reflect.construct require an argumentList argument that must be an object. +assertThrowsInstanceOf(() => Reflect.apply(Math.min, undefined), // missing + TypeError); +//assertThrowsInstanceOf(() => Reflect.construct(Object), // missing +// TypeError); +for (var primitive of SOME_PRIMITIVE_VALUES) { + assertThrowsInstanceOf(() => Reflect.apply(Math.min, undefined, primitive), + TypeError); + //assertThrowsInstanceOf(() => Reflect.construct(Object, primitive), + // TypeError); +} + +// Array used by several tests below. +var BOTH = [ + Reflect.apply, + // Adapt Reflect.construct to accept the same arguments as Reflect.apply. + //(target, thisArgument, argumentList) => Reflect.construct(target, argumentList) +]; + +// The argumentList is copied and becomes the list of arguments passed to the function. +function getRest(...x) { return x; } +var args = [1, 2, 3]; +for (var method of BOTH) { + var result = method(getRest, undefined, args); + assertEq(result.join(), args.join()); + assertEq(result !== args, true); +} + +// argumentList.length can be less than func.length. +function testLess(a, b, c, d, e) { + assertEq(a, 1); + assertEq(b, true); + assertEq(c, "three"); + assertEq(d, Symbol.for); + assertEq(e, undefined); + + assertEq(arguments.length, 4); + assertEq(arguments !== args, true); + return "ok"; +} +args = [1, true, "three", Symbol.for]; +assertEq(Reflect.apply(testLess, undefined, args), "ok"); +//assertEq(Reflect.construct(testLess, args) instanceof testLess, true); + +// argumentList.length can be more than func.length. +function testMoar(a) { + assertEq(a, args[0]); + return "good"; +} +assertEq(Reflect.apply(testMoar, undefined, args), "good"); +//assertEq(Reflect.construct(testMoar, args) instanceof testMoar, true); + +// argumentList can be any object with a .length property. +function getArgs(...args) { + return args; +} +for (var method of BOTH) { + assertDeepEq(method(getArgs, undefined, {length: 0}), + []); + assertDeepEq(method(getArgs, undefined, {length: 1, "0": "zero"}), + ["zero"]); + assertDeepEq(method(getArgs, undefined, {length: 2}), + [undefined, undefined]); + assertDeepEq(method(getArgs, undefined, function (a, b, c) {}), + [undefined, undefined, undefined]); +} + +// The Iterable/Iterator interfaces are not used. +var funnyArgs = { + 0: "zero", + 1: "one", + length: 2, + [Symbol.iterator]() { throw "FAIL 1"; }, + next() { throw "FAIL 2"; } +}; +for (var method of BOTH) { + assertDeepEq(method(getArgs, undefined, funnyArgs), + ["zero", "one"]); +} + +// If argumentList has no .length property, no arguments are passed. +function count() { return {numArgsReceived: arguments.length}; } +for (var method of BOTH) { + assertEq(method(count, undefined, {"0": 0, "1": 1}).numArgsReceived, + 0); + function* g() { yield 1; yield 2; } + assertEq(method(count, undefined, g()).numArgsReceived, + 0); +} + +// If argumentsList.length has a getter, it is called. +var log; +args = { + get length() { log += "L"; return 1; }, + get "0"() { log += "0"; return "zero"; }, + get "1"() { log += "1"; return "one"; } +}; +for (var method of BOTH) { + log = ""; + assertDeepEq(method(getArgs, undefined, args), + ["zero"]); + assertEq(log, "L0"); +} + +// The argumentsList.length getter can throw; the exception is propagated. +var exc = {status: "bad"}; +args = { + get length() { throw exc; } +}; +for (var method of BOTH) { + assertThrowsValue(() => method(count, undefined, args), exc); +} + +// If argumentsList.length is unreasonably huge, we get an error. +// (This is an implementation limit.) +for (var method of BOTH) { + for (var value of [1e12, 1e240, Infinity]) { + assertThrowsInstanceOf(() => method(count, undefined, {length: value}), + Error); + } +} + +// argumentsList.length is converted to an integer. +for (var value of [1.7, "1", {valueOf() { return "1"; }}]) { + args = { + length: value, + "0": "ponies" + }; + for (var method of BOTH) { + var result = method(getArgs, undefined, args); + assertEq(result.length, 1); + assertEq(result[0], "ponies"); + } +} + +// If argumentsList.length is negative or NaN, no arguments are passed. +for (var method of BOTH) { + for (var num of [-1, -0.1, -0, -1e99, -Infinity, NaN]) { + assertEq(method(count, undefined, {length: num}).numArgsReceived, + 0); + } +} + +// Many arguments can be passed. +var many = 65537; +var args = {length: many, 0: "zero", [many - 1]: "last"}; +function testMany(...args) { + "use strict"; + for (var i = 0; i < many; i++) { + assertEq(i in args, true); + assertEq(args[i], i === 0 ? "zero" : i === many - 1 ? "last" : undefined); + } + return this; +} +assertEq(Reflect.apply(testMany, "pass", args), "pass"); +//assertEq(Reflect.construct(testMany, args) instanceof testMany, true); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/defineProperty.js b/js/src/tests/ecma_6/Reflect/defineProperty.js new file mode 100644 index 000000000000..c3b1e8600b09 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/defineProperty.js @@ -0,0 +1,164 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.defineProperty defines properties. +var obj = {}; +assertEq(Reflect.defineProperty(obj, "x", {value: 7}), true); +assertEq(obj.x, 7); +var desc = Reflect.getOwnPropertyDescriptor(obj, "x"); +assertDeepEq(desc, {value: 7, + writable: false, + enumerable: false, + configurable: false}); + +// Reflect.defineProperty can define a symbol-keyed property. +var key = Symbol(":o)"); +assertEq(Reflect.defineProperty(obj, key, {value: 8}), true); +assertEq(obj[key], 8); + +// array .length property +obj = [1, 2, 3, 4, 5]; +assertEq(Reflect.defineProperty(obj, "length", {value: 4}), true); +assertDeepEq(obj, [1, 2, 3, 4]); + +// The target can be a proxy. +obj = {}; +var proxy = new Proxy(obj, { + defineProperty(t, id, desc) { + t[id] = 1; + return true; + } +}); +assertEq(Reflect.defineProperty(proxy, "prop", {value: 7}), true); +assertEq(obj.prop, 1); +assertEq(delete obj.prop, true); +assertEq("prop" in obj, false); + +// The attributes object is re-parsed, not passed through to the +// handler.defineProperty method. +obj = {}; +var attributes = { + configurable: 17, + enumerable: undefined, + value: null +}; +proxy = new Proxy(obj, { + defineProperty(t, id, desc) { + assertEq(desc !== attributes, true); + assertEq(desc.configurable, true); + assertEq(desc.enumerable, false); + assertEq(desc.value, null); + assertEq("writable" in desc, false); + return 15; // and the return value here is coerced to boolean + } +}); +assertEq(Reflect.defineProperty(proxy, "prop", attributes), true); + + +// === Failure and error cases +// +// Reflect.defineProperty behaves much like Object.defineProperty, which has +// extremely thorough tests elsewhere, and the implementation is largely +// shared. Duplicating those tests with Reflect.defineProperty would be a +// big waste. +// +// However, certain failures cause Reflect.defineProperty to return false +// without throwing a TypeError (unlike Object.defineProperty). So here we test +// many error cases to check that behavior. + +// missing attributes argument +assertThrowsInstanceOf(() => Reflect.defineProperty(obj, "y"), + TypeError); + +// non-object attributes argument +for (var attributes of SOME_PRIMITIVE_VALUES) { + assertThrowsInstanceOf(() => Reflect.defineProperty(obj, "y", attributes), + TypeError); +} + +// inextensible object +obj = Object.preventExtensions({}); +assertEq(Reflect.defineProperty(obj, "prop", {value: 4}), false); + +// inextensible object with irrelevant inherited property +obj = Object.preventExtensions(Object.create({"prop": 3})); +assertEq(Reflect.defineProperty(obj, "prop", {value: 4}), false); + +// redefine nonconfigurable to configurable +obj = Object.freeze({prop: 1}); +assertEq(Reflect.defineProperty(obj, "prop", {configurable: true}), false); + +// redefine enumerability of nonconfigurable property +obj = Object.freeze(Object.defineProperties({}, { + x: {enumerable: true, configurable: false, value: 0}, + y: {enumerable: false, configurable: false, value: 0}, +})); +assertEq(Reflect.defineProperty(obj, "x", {enumerable: false}), false); +assertEq(Reflect.defineProperty(obj, "y", {enumerable: true}), false); + +// redefine nonconfigurable data to accessor property, or vice versa +obj = Object.seal({x: 1, get y() { return 2; }}); +assertEq(Reflect.defineProperty(obj, "x", {get() { return 2; }}), false); +assertEq(Reflect.defineProperty(obj, "y", {value: 1}), false); + +// redefine nonwritable, nonconfigurable property as writable +obj = Object.freeze({prop: 0}); +assertEq(Reflect.defineProperty(obj, "prop", {writable: true}), false); +assertEq(Reflect.defineProperty(obj, "prop", {writable: false}), true); // no-op + +// change value of nonconfigurable nonwritable property +obj = Object.freeze({prop: 0}); +assertEq(Reflect.defineProperty(obj, "prop", {value: -0}), false); +assertEq(Reflect.defineProperty(obj, "prop", {value: +0}), true); // no-op + +// change getter or setter +function g() {} +function s(x) {} +obj = {}; +Object.defineProperty(obj, "prop", {get: g, set: s, configurable: false}); +assertEq(Reflect.defineProperty(obj, "prop", {get: s}), false); +assertEq(Reflect.defineProperty(obj, "prop", {get: g}), true); // no-op +assertEq(Reflect.defineProperty(obj, "prop", {set: g}), false); +assertEq(Reflect.defineProperty(obj, "prop", {set: s}), true); // no-op + +// Proxy defineProperty handler method that returns false +var falseValues = [false, 0, -0, "", NaN, null, undefined]; +if (typeof objectEmulatingUndefined === "function") + falseValues.push(objectEmulatingUndefined()); +var value; +proxy = new Proxy({}, { + defineProperty(t, id, desc) { + return value; + } +}); +for (value of falseValues) { + assertEq(Reflect.defineProperty(proxy, "prop", {value: 1}), false); +} + +// Proxy defineProperty handler method returns true, in violation of invariants. +// Per spec, this is a TypeError, not a false return. +obj = Object.freeze({x: 1}); +proxy = new Proxy(obj, { + defineProperty(t, id, desc) { + return true; + } +}); +assertThrowsInstanceOf(() => Reflect.defineProperty(proxy, "x", {value: 2}), TypeError); +assertThrowsInstanceOf(() => Reflect.defineProperty(proxy, "y", {value: 0}), TypeError); +assertEq(Reflect.defineProperty(proxy, "x", {value: 1}), true); + +// The second argument is converted ToPropertyKey before any internal methods +// are called on the first argument. +var poison = + (counter => new Proxy({}, new Proxy({}, { get() { throw counter++; } })))(42); +assertThrowsValue(() => { + Reflect.defineProperty(poison, { + toString() { throw 17; }, + valueOf() { throw 8675309; } + }, poison); +}, 17); + + +// For more Reflect.defineProperty tests, see target.js and propertyKeys.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/deleteProperty.js b/js/src/tests/ecma_6/Reflect/deleteProperty.js new file mode 100644 index 000000000000..744e0f571504 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/deleteProperty.js @@ -0,0 +1,80 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.deleteProperty deletes properties. +var obj = {x: 1, y: 2}; +assertEq(Reflect.deleteProperty(obj, "x"), true); +assertDeepEq(obj, {y: 2}); + +var arr = [1, 1, 2, 3, 5]; +assertEq(Reflect.deleteProperty(arr, "3"), true); +assertDeepEq(arr, [1, 1, 2, , 5]); + + +// === Failure and error cases +// Since Reflect.deleteProperty is almost exactly identical to the non-strict +// `delete` operator, there is not much to test that would not be redundant. + +// Returns true if no such property exists. +assertEq(Reflect.deleteProperty({}, "q"), true); + +// Or if it's inherited. +var proto = {x: 1}; +assertEq(Reflect.deleteProperty(Object.create(proto), "x"), true); +assertEq(proto.x, 1); + +// Return false if asked to delete a non-configurable property. +var arr = []; +assertEq(Reflect.deleteProperty(arr, "length"), false); +assertEq(arr.hasOwnProperty("length"), true); +assertEq(Reflect.deleteProperty(this, "undefined"), false); +assertEq(this.undefined, void 0); + +// Return false if a Proxy's deleteProperty handler returns a false-y value. +var value; +var proxy = new Proxy({}, { + deleteProperty(t, k) { + return value; + } +}); +for (value of [true, false, 0, "something", {}]) { + assertEq(Reflect.deleteProperty(proxy, "q"), !!value); +} + +// If a Proxy's handler method throws, the error is propagated. +proxy = new Proxy({}, { + deleteProperty(t, k) { throw "vase"; } +}); +assertThrowsValue(() => Reflect.deleteProperty(proxy, "prop"), "vase"); + +// Throw a TypeError if a Proxy's handler method returns true in violation of +// the object invariants. +proxy = new Proxy(Object.freeze({prop: 1}), { + deleteProperty(t, k) { return true; } +}); +assertThrowsInstanceOf(() => Reflect.deleteProperty(proxy, "prop"), TypeError); + + +// === Deleting elements from `arguments` + +// Non-strict arguments element becomes unmapped +function f(x, y, z) { + assertEq(Reflect.deleteProperty(arguments, "0"), true); + arguments.x = 33; + return x; +} +assertEq(f(17, 19, 23), 17); + +// Frozen non-strict arguments element +function testFrozenArguments() { + Object.freeze(arguments); + assertEq(Reflect.deleteProperty(arguments, "0"), false); + assertEq(arguments[0], "zero"); + assertEq(arguments[1], "one"); +} +testFrozenArguments("zero", "one"); + + +// For more Reflect.deleteProperty tests, see target.js and propertyKeys.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/get.js b/js/src/tests/ecma_6/Reflect/get.js new file mode 100644 index 000000000000..357e9b087a45 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/get.js @@ -0,0 +1,85 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.get gets the value of a property. + +var x = {p: 1}; +assertEq(Reflect.get(x, "p"), 1); +assertEq(Reflect.get(x, "toString"), Object.prototype.toString); +assertEq(Reflect.get(x, "missing"), undefined); + + +// === Various targets + +// Array +assertEq(Reflect.get([], 700), undefined); +assertEq(Reflect.get(["zero", "one"], 1), "one"); + +// TypedArray +assertEq(Reflect.get(new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]), 7), 7); + +// Treatment of NaN +var f = new Float64Array([NaN]); +var u = new Uint32Array(f.buffer); +u[0]++; +u[1]++; +assertEq(f[0], NaN); +assertEq(Reflect.get(f, 0), NaN); + +// Proxy with no get handler +assertEq(Reflect.get(new Proxy(x, {}), "p"), 1); + +// Proxy with a get handler +var obj = new Proxy(x, { + get(t, k, r) { return k + "ful"; } +}); +assertEq(Reflect.get(obj, "mood"), "moodful"); + +// Exceptions thrown by a proxy's get handler are propagated. +assertThrowsInstanceOf(() => Reflect.get(obj, Symbol()), TypeError); + +// Ordinary object, property has a setter and no getter +obj = {set name(x) {}}; +assertEq(Reflect.get(obj, "name"), undefined); + + +// === Receiver + +// Receiver argument is passed to getters as the this-value. +obj = { get x() { return this; }}; +assertEq(Reflect.get(obj, "x", Math), Math); +assertEq(Reflect.get(Object.create(obj), "x", JSON), JSON); + +// If missing, target is passed instead. +assertEq(Reflect.get(obj, "x"), obj); + +// Receiver argument is passed to the proxy get handler. +obj = new Proxy({}, { + get(t, k, r) { + assertEq(k, "itself"); + return r; + } +}); +assertEq(Reflect.get(obj, "itself"), obj); +assertEq(Reflect.get(obj, "itself", Math), Math); +assertEq(Reflect.get(Object.create(obj), "itself", Math), Math); + +// The receiver shouldn't have to be an object---but we do not implement that +// correctly yet (bug 603201). For now, test the wrong behavior just to make +// sure we don't crash. +var result; +try { + result = Reflect.get(obj, "x", 37.2); +} catch (exc) { + result = exc; +} +if (result === 37.2) { + throw new Error("Congratulations on fixing bug 603201! " + + "Please update this test for 1 karma point."); +} +assertEq(result instanceof TypeError, true); + + +// For more Reflect.get tests, see target.js and propertyKeys.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/getOwnPropertyDescriptor.js b/js/src/tests/ecma_6/Reflect/getOwnPropertyDescriptor.js new file mode 100644 index 000000000000..aa7329778de2 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/getOwnPropertyDescriptor.js @@ -0,0 +1,21 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.getOwnPropertyDescriptor inspects object properties. +assertDeepEq( + Reflect.getOwnPropertyDescriptor({x: "hello"}, "x"), + {value: "hello", writable: true, enumerable: true, configurable: true}); +assertEq( + Reflect.getOwnPropertyDescriptor({x: "hello"}, "y"), + undefined); +assertDeepEq( + Reflect.getOwnPropertyDescriptor([], "length"), + {value: 0, writable: true, enumerable: false, configurable: false}); + +// Reflect.getOwnPropertyDescriptor shares its implementation with +// Object.getOwnPropertyDescriptor. The only difference is how non-object +// targets are handled. +// +// For more Reflect.getOwnPropertyDescriptor tests, see target.js and propertyKeys.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/getPrototypeOf.js b/js/src/tests/ecma_6/Reflect/getPrototypeOf.js new file mode 100644 index 000000000000..c9f130802daf --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/getPrototypeOf.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.getPrototypeOf returns an object's prototype. +assertEq(Reflect.getPrototypeOf({}), Object.prototype); +assertEq(Reflect.getPrototypeOf(Object.prototype), null); +assertEq(Reflect.getPrototypeOf(Object.create(null)), null); + +// Sleeper test for when scripted proxies support the getPrototypeOf handler +// method (bug 888969). +var proxy = new Proxy({}, { + getPrototypeOf(t) { return Math; } +}); +var result = Reflect.getPrototypeOf(proxy); +if (result === Math) { + throw new Error("Congratulations on fixing bug 888969! " + + "Please update this test to cover scripted proxies."); +} + +// For more Reflect.getPrototypeOf tests, see target.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/has.js b/js/src/tests/ecma_6/Reflect/has.js new file mode 100644 index 000000000000..6019a61ed3ae --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/has.js @@ -0,0 +1,41 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.has is identical to the `in` operator. +assertEq(Reflect.has({x: 0}, "x"), true); +assertEq(Reflect.has({x: 0}, "y"), false); +assertEq(Reflect.has({x: 0}, "toString"), true); + +// The target can be an array; Reflect.has works on array elements. +var arr = ["zero"]; +arr[10000] = 0; +assertEq(Reflect.has(arr, "10000"), true); +assertEq(Reflect.has(arr, 10000), true); +assertEq(Reflect.has(arr, "-0"), false); +assertEq(Reflect.has(arr, -0), true); + +// And string objects (though not string primitives; see target.js). +var str = new String("hello"); +assertEq(Reflect.has(str, "4"), true); +assertEq(Reflect.has(str, "-0"), false); +assertEq(Reflect.has(str, -0), true); + +// Proxy without .has() handler method +var obj = {get prop() {}}; +for (var i = 0; i < 2; i++) { + obj = new Proxy(obj, {}); + assertEq(Reflect.has(obj, "prop"), true); + assertEq(Reflect.has(obj, "nope"), false); +} + +// Proxy with .has() handler method +obj = new Proxy({}, { + has(t, k) { return k.startsWith("door"); } +}); +assertEq(Reflect.has(obj, "doorbell"), true); +assertEq(Reflect.has(obj, "dormitory"), false); + + +// For more Reflect.has tests, see target.js and propertyKeys.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/isExtensible.js b/js/src/tests/ecma_6/Reflect/isExtensible.js new file mode 100644 index 000000000000..a6800bf4ab84 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/isExtensible.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.isExtensible behaves just like Object.extensible except when the +// target argument is missing or is not an object (and that behavior is tested +// in target.js). + +// Test basic functionality. +var someObjects = [ + {}, + {a: "a"}, + [0, 1], + new Uint8Array(64), + Object(Symbol("table")), + new Proxy({}, {}) +]; +for (var obj of someObjects) { + assertEq(Reflect.isExtensible(obj), true); + assertEq(Reflect.preventExtensions(obj), true); + assertEq(Reflect.isExtensible(obj), false); +} + +// Array with nonwritable length. +var arr = [0, 1, 2, 3]; +Object.defineProperty(arr, "length", {writable: false}); +assertEq(Reflect.isExtensible(arr), true); + +// Proxy case. +for (var ext of [true, false]) { + var obj = {}; + if (!ext) + Object.preventExtensions(obj); + var proxy = new Proxy(obj, { + isExtensible() { return ext; } + }); + assertEq(Reflect.isExtensible(proxy), ext); +} + +// If a Proxy's isExtensible method throws, the exception is propagated. +proxy = new Proxy({}, { + isExtensible() { throw "oops"; } +}); +assertThrowsValue(() => Reflect.isExtensible(proxy), "oops"); + +// If an invariant is broken, [[IsExtensible]] does not return false. It throws +// a TypeError. +proxy = new Proxy({}, { + isExtensible() { return false; } +}); +assertThrowsInstanceOf(() => Reflect.isExtensible(proxy), TypeError); + +// For more Reflect.isExtensible tests, see target.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/ownKeys.js b/js/src/tests/ecma_6/Reflect/ownKeys.js new file mode 100644 index 000000000000..5433562eb5a0 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/ownKeys.js @@ -0,0 +1,66 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.ownKeys(obj) returns an array of an object's own property keys. + +// Test that Reflect.ownKeys gets the expected result when applied to various +// objects. (These tests also check the basics: that the result is an array, +// that its prototype is correct, etc.) +var sym = Symbol.for("comet"); +var sym2 = Symbol.for("meteor"); +var cases = [ + {object: {z: 3, y: 2, x: 1}, + keys: ["z", "y", "x"]}, + {object: [], + keys: ["length"]}, + {object: new Int8Array(4), + keys: ["0", "1", "2", "3"]}, + {object: new Proxy({a: 7}, {}), + keys: ["a"]}, + {object: {[sym]: "ok"}, + keys: [sym]}, + {object: {[sym]: 0, // test 9.1.12 ordering + "str": 0, + "773": 0, + "0": 0, + [sym2]: 0, + "-1": 0, + "8": 0, + "second str": 0}, + keys: ["0", "8", "773", // indexes in numeric order + "str", "-1", "second str", // strings in insertion order + sym, sym2]}, // symbols in insertion order + {object: newGlobal().Math, // cross-compartment wrapper + keys: Reflect.ownKeys(Math)} +]; +for (var {object, keys} of cases) + assertDeepEq(Reflect.ownKeys(object), keys); + +// Reflect.ownKeys() creates a new array each time it is called. +var object = {}, keys = []; +for (var i = 0; i < 3; i++) { + var newKeys = Reflect.ownKeys(object); + assertEq(newKeys !== keys, true); + keys = newKeys; +} + +// Proxy behavior with successful ownKeys() handler +keys = ["str", "0"]; +obj = {}; +proxy = new Proxy(obj, { + ownKeys() { return keys; } +}); +var actual = Reflect.ownKeys(proxy); +assertDeepEq(actual, keys); // we get correct answers +assertEq(actual !== keys, true); // but not the same object + +// If a proxy breaks invariants, a TypeError is thrown. +var obj = Object.preventExtensions({}); +var proxy = new Proxy(obj, { + ownKeys() { return ["something"]; } +}); +assertThrowsInstanceOf(() => Reflect.ownKeys(proxy), TypeError); + +// For more Reflect.ownKeys tests, see target.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/preventExtensions.js b/js/src/tests/ecma_6/Reflect/preventExtensions.js new file mode 100644 index 000000000000..63ca2545b4cd --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/preventExtensions.js @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.preventExtensions is the same as Object.preventExtensions, except +// for the return value and the behavior in error cases. + +var someObjects = [ + {}, + new Int32Array(7), + Object(Symbol("table")), + new Proxy({}, {}) +]; + +for (var obj of someObjects) { + assertEq(Reflect.preventExtensions(obj), true); + // [[PreventExtensions]] on an already-inextensible object is a no-op. + assertEq(Reflect.preventExtensions(obj), true); +} + +// Error cases. +assertThrowsInstanceOf(() => Reflect.isExtensible(), TypeError); +for (var value of [undefined, null, true, 1, NaN, "Phaedo", Symbol("good")]) { + assertThrowsInstanceOf(() => Reflect.isExtensible(value), TypeError); +} + +// A proxy's preventExtensions handler can return false without doing anything. +obj = {}; +var proxy = new Proxy(obj, { + preventExtensions() { return false; } +}); +assertEq(Reflect.preventExtensions(proxy), false); +assertEq(Reflect.isExtensible(obj), true); +assertEq(Reflect.isExtensible(proxy), true); + +// If a proxy's preventExtensions handler throws, the exception is propagated. +obj = {}; +proxy = new Proxy(obj, { + preventExtensions() { throw "fit"; } +}); +assertThrowsValue(() => Reflect.preventExtensions(proxy), "fit"); +assertEq(Reflect.isExtensible(obj), true); +assertEq(Reflect.isExtensible(proxy), true); + +// If a proxy's preventExtensions handler returns true while leaving the target +// extensible, that's a TypeError. +obj = {}; +proxy = new Proxy(obj, { + preventExtensions() { return true; } +}); +assertThrowsInstanceOf(() => Reflect.preventExtensions(proxy), TypeError); +assertEq(Reflect.isExtensible(obj), true); +assertEq(Reflect.isExtensible(proxy), true); + +// For more Reflect.preventExtensions tests, see target.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/propertyKeys.js b/js/src/tests/ecma_6/Reflect/propertyKeys.js new file mode 100644 index 000000000000..26358ea55063 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/propertyKeys.js @@ -0,0 +1,77 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Test corner cases involving Reflect methods' propertyKey arguments. + +// keys - Array of propertyKey values to be tested. +// +// Each element of this array is a record with these properties: +// +// * value: a value that will be passed as a property key +// to the various Reflect methods; +// +// * expected: (optional) the string or symbol that ToPropertyKey(value) +// should return. If this is omitted, ToPropertyKey(value) === value. +// +var keys = [ + {value: null, expected: "null"}, + {value: undefined, expected: "undefined"}, + {value: true, expected: "true"}, + {value: 42, expected: "42"}, + {value: "string"}, + {value: ""}, + {value: "string with \0"}, + {value: new String("ok"), expected: "ok"}, + {value: Symbol("sym")}, + {value: Symbol.iterator}, + {value: Object(Symbol.for("comet")), expected: Symbol.for("comet")}, + { + value: { + toString() { return "key"; }, + valueOf() { return "bad"; } + }, + expected: "key" + }, + { + value: { + toString: undefined, + valueOf() { return "fallback"; } + }, + expected: "fallback" + } +]; + +if ("toPrimitive" in Symbol) { + throw new Error("Congratulations on implementing Symbol.toPrimitive! " + + "Please add an object with an @@toPrimitive method in the list above."); +} + +for (var {value, expected} of keys) { + if (expected === undefined) + expected = value; + + var obj = {}; + assertEq(Reflect.defineProperty(obj, value, {value: 1, configurable: true}), true); + assertDeepEq(Reflect.ownKeys(obj), [expected]); + assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, value), + {value: 1, + writable: false, + enumerable: false, + configurable: true}); + assertEq(Reflect.deleteProperty(obj, value), true); + assertEq(Reflect.has(obj, value), false); + assertEq(Reflect.set(obj, value, 113), true); + assertEq(obj[expected], 113); + assertEq(Reflect.has(obj, value), true); + assertEq(Reflect.get(obj, value), 113); +} + +// ToPropertyKey can throw. +var exc = {}; +var badKey = {toString() { throw exc; }}; +var methodNames = ["defineProperty", "deleteProperty", "has", "get", "getOwnPropertyDescriptor", "set"]; +for (var name of methodNames) { + assertThrowsValue(() => Reflect[name]({}, badKey), exc); +} + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/set.js b/js/src/tests/ecma_6/Reflect/set.js index 3c57d3c76b79..83cd98b69182 100644 --- a/js/src/tests/ecma_6/Reflect/set.js +++ b/js/src/tests/ecma_6/Reflect/set.js @@ -1,16 +1,280 @@ -if (this.Reflect && Reflect.set) - throw new Error("Congrats on implementing Reflect.set! Uncomment this test for 1 karma point."); +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.set does property assignment. +// With three arguments, this is pretty straightforward. +var obj = {}; +assertEq(Reflect.set(obj, "prop", "value"), true); +assertEq(obj.prop, "value"); + + +// === Various targets + +// It can assign array elements. +var arr = ["duck", "duck", "duck"]; +assertEq(Reflect.set(arr, 2, "goose"), true); +assertEq(arr[2], "goose"); + +// It can extend an array. +assertEq(Reflect.set(arr, 3, "Model T"), true); +assertEq(arr.length, 4); + +// It can truncate an array. +assertEq(Reflect.set(arr, "length", 1), true); +assertDeepEq(arr, ["duck"]); + +// It won't assign to non-writable properties of String objects. +var str = new String("hello"); +assertEq(Reflect.set(str, "0", "y"), false); +assertEq(str[0], "h"); +assertEq(Reflect.set(str, "length", 700), false); +assertEq(str.length, 5); + + +// === Receivers +// The optional fourth argument is the receiver, which [[Set]] methods use for +// various things. + +// On ordinary objects, if the property has a setter, the receiver is passed as +// the this-value to the setter. +var expected; +var obj = { + set prop(v) { + "use strict"; + assertEq(v, 32); + assertEq(this, expected); + } +}; +for (expected of [obj, {}, [], 37.3]) { + assertEq(Reflect.set(obj, "prop", 32, expected), true); +} + +// If the property doesn't already exist, it is defined on the receiver. +obj = {}; +var obj2 = {}; +assertEq(Reflect.set(obj, "prop", 47, obj2), true); +assertDeepEq(obj, {}); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj2, "prop"), + {value: 47, writable: true, enumerable: true, configurable: true}); + +// If the property doesn't already exist, and the receiver isn't an object, return false. +for (var v of SOME_PRIMITIVE_VALUES) { + assertEq(Reflect.set({}, "x", 0, v), false); +} + +// Receiver defaults to the target. +obj = {}; +var hits; +var expectedReceiver; +var proxy = new Proxy(obj, { + set(t, k, v, r) { + assertEq(t, obj); + assertEq(k, "key"); + assertEq(v, "value"); + assertEq(r, expectedReceiver); // not obj + hits++; + return true; + } +}); +hits = 0; +expectedReceiver = proxy; +assertEq(Reflect.set(proxy, "key", "value"), true); +assertEq(hits, 1); + +// But not if explicitly present and undefined. +hits = 0; +expectedReceiver = undefined; +assertEq(Reflect.set(proxy, "key", "value", undefined), true); +assertEq(hits, 1); + +// Reflect.set can be used as fallback behavior in a proxy handler .set() +// method. +var log; +obj = { + set prop(v) { + log += "p"; + assertEq(v, "value"); + assertEq(this, proxy); // not obj! + } +}; +proxy = new Proxy(obj, { + set(t, k, v, r) { + assertEq(t, obj); + assertEq(r, proxy); + log += "s"; + return Reflect.set(t, k, v, r); + } +}); +log = ""; +assertEq(Reflect.set(proxy, "prop", "value"), true); +assertEq(log, "sp"); + + +// === Cross-compartment wrapper behavior. + +// When calling a cross-compartment wrapper, receiver is rewrapped for the +// target compartment. +var g = newGlobal(); +if (!("assertEq" in g)) + g.assertEq = assertEq; // necessary in the browser +g.eval(` + var hits; + var obj = { + set x(v) { + "use strict"; + assertEq(this, receiver); + assertEq(v, "xyzzy"); + hits++; + } + }; + var receiver = {}; +`); +g.hits = 0; +assertEq(Reflect.set(g.obj, "x", "xyzzy", g.receiver), true); +assertEq(g.hits, 1); + +// ...even when receiver is from a different compartment than target. +var receiver = {}; +g.receiver = receiver; +g.hits = 0; +assertEq(Reflect.set(g.obj, "x", "xyzzy", receiver), true); +assertEq(g.hits, 1); + +// ...even when receiver is a primtive value, even undefined. +for (receiver of SOME_PRIMITIVE_VALUES) { + g.receiver = receiver; + g.hits = 0; + assertEq(Reflect.set(g.obj, "x", "xyzzy", receiver), true); + assertEq(g.hits, 1); +} + + +// === Less than 3 arguments + +// With two arguments, the value is assumed to be undefined. +obj = {}; +assertEq(Reflect.set(obj, "size"), true); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, "size"), + {value: undefined, writable: true, enumerable: true, configurable: true}); + +// With just one argument, the key is "undefined". +obj = {}; +assertEq(Reflect.set(obj), true); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, "undefined"), + {value: undefined, writable: true, enumerable: true, configurable: true}); + +// For the no argument-case, see target.js. + + +// === Failure cases + +// Non-writable data property +obj = {}; +Reflect.defineProperty(obj, "x", {value: 0, writable: false}); +assertEq(Reflect.set(obj, "x", 1), false); +assertEq(obj.x, 0); + +// The same, but inherited from a prototype +var obj2 = Object.create(obj); +assertEq(Reflect.set(obj2, "x", 1), false); +assertEq(obj2.hasOwnProperty("x"), false); +assertEq(obj2.x, 0); + +// Getter, no setter +obj = {}; +var desc = {get: () => 12, set: undefined, enumerable: false, configurable: true}; +Reflect.defineProperty(obj, "y", desc); +assertEq(Reflect.set(obj, "y", 13), false); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, "y"), desc); + +// The same, but inherited from a prototype +obj2 = Object.create(obj); +assertEq(Reflect.set(obj2, "y", 1), false); +assertEq(obj2.hasOwnProperty("y"), false); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, "y"), desc); + +// Proxy set handler returns a false value +for (var no of [false, ""]) { + var hits = 0; + obj = {}; + var proxy = new Proxy(obj, { + set(t, k, v, r) { + assertEq(t, obj); + assertEq(k, "x"); + assertEq(v, 33); + assertEq(r, proxy); + hits++; + return no; + } + }); + assertEq(Reflect.set(proxy, "x", 33), false); + assertEq(hits, 1); + assertEq("x" in obj, false); +} + +// Proxy handler method throws +obj = {}; +proxy = new Proxy(obj, { + set(t, k, v, r) { throw "i don't like " + v; } +}); +assertThrowsValue(() => Reflect.set(proxy, "food", "cheese"), "i don't like cheese"); + +// If a Proxy set handler breaks the object invariants, it's a TypeError. +for (obj of [{a: 0}, {get a() { return 0; }}]) { + Object.freeze(obj); + proxy = new Proxy(obj, { + set(t, k, v, r) { return true; } + }); + assertThrowsInstanceOf(() => Reflect.set(proxy, "a", "b"), TypeError); +} -/* // Per spec, this should first call p.[[Set]]("0", 42, a) and // then (since p has no own properties) a.[[Set]]("0", 42, a). -// Obviously the latter should not define a property on p. - +// The latter should not define a property on p. var a = [0, 1, 2, 3]; var p = Object.create(a); Reflect.set(p, "0", 42, a); assertEq(p.hasOwnProperty("0"), false); -assertEq(a[0], 42); -*/ +assertDeepEq(Reflect.getOwnPropertyDescriptor(a, "0"), + {value: 42, writable: true, enumerable: true, configurable: true}); -reportCompare(0, 0, 'ok'); +// Test behavior of ordinary objects' [[Set]] method (ES6 9.1.9). +// On an ordinary object, if the property key isn't present, [[Set]] calls +// receiver.[[GetOwnProperty]]() and then receiver.[[DefineProperty]](). +var log; +obj = {}; +var proxyTarget = {}; +var existingDescriptor, expected, defineResult; +var receiver = new Proxy(proxyTarget, { + getOwnPropertyDescriptor(t, k) { + log += "g"; + return existingDescriptor; + }, + defineProperty(t, k, desc) { + log += "d"; + assertEq(t, proxyTarget); + assertEq(k, "prop"); + assertDeepEq(desc, expected); + return defineResult; + } +}); +existingDescriptor = undefined; +expected = {value: 5, writable: true, enumerable: true, configurable: true}; +for (var defineResult of [true, false]) { + log = ""; + assertEq(Reflect.set(obj, "prop", 5, receiver), defineResult); + assertEq(log, "gd"); +} + +existingDescriptor = {value: 7, writable: true, enumerable: false, configurable: true}; +expected = {value: 4}; +for (var defineResult of [true, false]) { + log = ""; + assertEq(Reflect.set(obj, "prop", 4, receiver), defineResult); + assertEq(log, "gd"); +} + + +// For more Reflect.set tests, see target.js and propertyKeys.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/setPrototypeOf.js b/js/src/tests/ecma_6/Reflect/setPrototypeOf.js new file mode 100644 index 000000000000..0ee69c6bb0e3 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/setPrototypeOf.js @@ -0,0 +1,81 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.setPrototypeOf changes an object's [[Prototype]]. +var obj = {}; +assertEq(Object.getPrototypeOf(obj), Object.prototype); +var proto = {}; +assertEq(Reflect.setPrototypeOf(obj, proto), true); +assertEq(Object.getPrototypeOf(obj), proto); + +// It can change an object's [[Prototype]] to null. +obj = {}; +assertEq(Reflect.setPrototypeOf(obj, null), true); +assertEq(Object.getPrototypeOf(obj), null); + +// The proto argument is required too. +obj = {}; +assertThrowsInstanceOf(() => Reflect.setPrototypeOf(obj), TypeError); + +// The proto argument must be either null or an object. +for (proto of [undefined, false, 0, 1.6, "that", Symbol.iterator]) { + assertThrowsInstanceOf(() => Reflect.setPrototypeOf(obj, proto), TypeError); +} + +// Return false if the target is inextensible. +proto = {}; +obj = Object.preventExtensions(Object.create(proto)); +assertEq(Reflect.setPrototypeOf(obj, {}), false); +assertEq(Reflect.setPrototypeOf(obj, null), false); +assertEq(Reflect.setPrototypeOf(obj, proto), false); // even if not changing anything + +// Return false rather than create a [[Prototype]] cycle. +obj = {}; +assertEq(Reflect.setPrototypeOf(obj, obj), false); + +// Don't create a [[Prototype]] cycle involving 2 objects. +obj = Object.create(proto); +assertEq(Reflect.setPrototypeOf(proto, obj), false); + +// Don't create a longish [[Prototype]] cycle. +for (var i = 0; i < 256; i++) + obj = Object.create(obj); +assertEq(Reflect.setPrototypeOf(proto, obj), false); + +// The spec claims we should allow creating cycles involving proxies. (The +// cycle check quietly exits on encountering the proxy.) +obj = {}; +var proxy = new Proxy(Object.create(obj), {}); +if (Reflect.setPrototypeOf(obj, proxy) !== false) { + throw new Error("Congratulations on implementing ES6 [[SetPrototype]]! " + + "Update this test for 1 karma point!"); + // ...by deleting this if-block and uncommenting the three assertions below. +} +// assertEq(Reflect.setPrototypeOf(obj, proxy), true); +// assertEq(Reflect.getPrototypeOf(obj), proxy); +// assertEq(Reflect.getPrototypeOf(proxy), obj); + +// If a proxy handler returns a false-y value, return false. +var hits = 0; +proto = {name: "proto"}; +obj = {name: "obj"}; +proxy = new Proxy(obj, { + setPrototypeOf(t, p) { + assertEq(t, obj); + assertEq(p, proto); + hits++; + return 0; + } +}); +if (Reflect.setPrototypeOf(proxy, proto) !== true) { + throw new Error("Congratulations on implementing the setPrototypeOf trap for proxies! " + + "Please update this test."); + // ...by deleting this if-block and uncommenting the two assertions below. + // As of this writing, the setPrototypeOf hook is never called; see bug 888969. +} +// assertEq(Reflect.setPrototypeOf(proxy, proto), false); +// assertEq(hits, 1); + +// For more Reflect.setPrototypeOf tests, see target.js. + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/shell.js b/js/src/tests/ecma_6/Reflect/shell.js index e69de29bb2d1..2aa5397ed9c8 100644 --- a/js/src/tests/ecma_6/Reflect/shell.js +++ b/js/src/tests/ecma_6/Reflect/shell.js @@ -0,0 +1,9 @@ +// List of a few values that are not objects. +var SOME_PRIMITIVE_VALUES = [ + undefined, null, + false, + -Infinity, -1.6e99, -1, -0, 0, Math.pow(2, -1074), 1, 4294967295, + Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER + 1, 1.6e99, Infinity, NaN, + "", "Phaedo", + Symbol(), Symbol("iterator"), Symbol.for("iterator"), Symbol.iterator +]; diff --git a/js/src/tests/ecma_6/Reflect/surfaces.js b/js/src/tests/ecma_6/Reflect/surfaces.js index d774fc82e1c0..d9290cab3218 100644 --- a/js/src/tests/ecma_6/Reflect/surfaces.js +++ b/js/src/tests/ecma_6/Reflect/surfaces.js @@ -1,20 +1,60 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ +// Check surface features of the Reflect object. assertEq(typeof Reflect, 'object'); assertEq(Object.getPrototypeOf(Reflect), Object.prototype); assertEq(Reflect.toString(), '[object Object]'); +assertThrowsInstanceOf(() => new Reflect, TypeError); var desc = Object.getOwnPropertyDescriptor(this, "Reflect"); assertEq(desc.enumerable, false); assertEq(desc.configurable, true); assertEq(desc.writable, true); -// Assert that the SpiderMonkey "resolve hook" mechanism does not resurrect the +for (var name in Reflect) + throw new Error("Reflect should not have any enumerable properties"); + +// The name and length of all the standard Reflect methods. +var methods = { + apply: 3, + //construct: 2, + defineProperty: 3, + deleteProperty: 2, + //enumerate: 1, + get: 2, + getOwnPropertyDescriptor: 2, + getPrototypeOf: 1, + has: 2, + isExtensible: 1, + ownKeys: 1, + preventExtensions: 1, + set: 3, + setPrototypeOf: 2 +}; + +// Check that all Reflect properties are listed above. +for (var name of Reflect.ownKeys(Reflect)) { + // If this assertion fails, congratulations on implementing a new Reflect feature! + // Add it to the list of methods above. + if (name !== "parse") + assertEq(name in methods, true, `unexpected property found: Reflect.${name}`); +} + +// Check the .length and property attributes of each Reflect method. +for (var name of Object.keys(methods)) { + var desc = Object.getOwnPropertyDescriptor(Reflect, name); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + assertEq(desc.writable, true); + var f = desc.value; + assertEq(typeof f, "function"); + assertEq(f.length, methods[name]); +} + +// Check that the SpiderMonkey "resolve hook" mechanism does not resurrect the // Reflect property once it is deleted. delete this.Reflect; assertEq("Reflect" in this, false); -reportCompare(0, 0, 'ok'); +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/target.js b/js/src/tests/ecma_6/Reflect/target.js new file mode 100644 index 000000000000..bd8fa99bf0d9 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/target.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Check correct handling of the `target` argument shared by every Reflect method. + +// For each standard Reflect method, an array of arguments +// that would be OK after a suitable target argument. +var methodInfo = { + apply: [undefined, []], + //construct: [[]], + defineProperty: ["x", {}], + deleteProperty: ["x"], + //enumerate: [], + get: ["x", {}], + getOwnPropertyDescriptor: ["x"], + getPrototypeOf: [], + has: ["x"], + isExtensible: [], + ownKeys: [], + preventExtensions: [], + set: ["x", 0], + setPrototypeOf: [{}] +}; + +// Check that all Reflect properties are listed above. +for (var name of Reflect.ownKeys(Reflect)) { + // If this assertion fails, congratulations on implementing a new Reflect feature! + // Add it to methodInfo above. + if (name !== "parse") + assertEq(name in methodInfo, true); +} + +for (var name of Object.keys(methodInfo)) { + var args = methodInfo[name]; + + // The target argument is required. + assertThrowsInstanceOf(Reflect[name], TypeError); + + // Throw if the target argument is not an object. + for (var value of SOME_PRIMITIVE_VALUES) { + assertThrowsInstanceOf(() => Reflect[name](value, ...args), TypeError); + } +} + +reportCompare(0, 0); diff --git a/js/src/vm/DebuggerMemory.cpp b/js/src/vm/DebuggerMemory.cpp index 4bbeb30d21c1..08c1ec82f094 100644 --- a/js/src/vm/DebuggerMemory.cpp +++ b/js/src/vm/DebuggerMemory.cpp @@ -14,6 +14,7 @@ #include #include "jsalloc.h" +#include "jscntxt.h" #include "jscompartment.h" #include "builtin/MapObject.h" diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index 106a1b4b6641..dcf9cdf8f232 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -14,6 +14,7 @@ #include #include "jsapi.h" +#include "jscntxt.h" #include "jscompartment.h" #include "jsfriendapi.h" #include "jshashutil.h" @@ -398,7 +399,8 @@ SavedFrame::checkThis(JSContext* cx, CallArgs& args, const char* fnName, const Value& thisValue = args.thisv(); if (!thisValue.isObject()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, InformalValueTypeName(thisValue)); + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, + InformalValueTypeName(thisValue)); return false; } diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 4c444d274f4f..a07232f38cbe 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -21,6 +21,7 @@ #include "builtin/Intl.h" #include "builtin/Object.h" +#include "builtin/Reflect.h" #include "builtin/SelfHostingDefines.h" #include "builtin/SIMD.h" #include "builtin/TypedObject.h" @@ -1270,11 +1271,13 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("std_Object_create", obj_create, 2,0), JS_FN("std_Object_propertyIsEnumerable", obj_propertyIsEnumerable, 1,0), JS_FN("std_Object_defineProperty", obj_defineProperty, 3,0), - JS_FN("std_Object_getPrototypeOf", obj_getPrototypeOf, 1,0), JS_FN("std_Object_getOwnPropertyNames", obj_getOwnPropertyNames, 1,0), JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0), JS_FN("std_Object_hasOwnProperty", obj_hasOwnProperty, 1,0), + JS_FN("std_Reflect_getPrototypeOf", Reflect_getPrototypeOf, 1,0), + JS_FN("std_Reflect_isExtensible", Reflect_isExtensible, 1,0), + JS_FN("std_Set_has", SetObject::has, 1,0), JS_FN("std_Set_iterator", SetObject::values, 0,0), diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 6b8b93a75e6d..ffda53d372e6 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -1066,6 +1066,7 @@ class InvokeArgs : public JS::CallArgs explicit InvokeArgs(JSContext* cx, bool construct = false) : v_(cx) {} bool init(unsigned argc, bool construct = false) { + MOZ_ASSERT(2 + argc + construct > argc); // no overflow if (!v_.resize(2 + argc + construct)) return false; ImplicitCast(*this) = CallArgsFromVp(argc, v_.begin()); diff --git a/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js b/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js index 7e1a83b93eda..bf7b65927531 100644 --- a/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js +++ b/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js @@ -73,18 +73,14 @@ function test_tamperproof(realObj, accessObj, {method, constant, attribute}) { } // Reflect.set doesn't work either. - if (this.Reflect && this.Reflect.set) - throw new Error("Congratulations on implementing Reflect.set! Here are some tests to uncomment."); - /* - if (method) { - do_check_false(Reflect.set({}, method, "bad", accessObj)); - do_check_eq(realObj[method], originalMethod); - } - if (attribute) { - do_check_false(Reflect.set({}, attribute, "bad", accessObj)); - do_check_eq(originalAttributeDesc.get, Object.getOwnPropertyDescriptor(realObj, attribute).get); - } - */ + if (method) { + do_check_false(Reflect.set({}, method, "bad", accessObj)); + do_check_eq(realObj[method], originalMethod); + } + if (attribute) { + do_check_false(Reflect.set({}, attribute, "bad", accessObj)); + do_check_eq(originalAttributeDesc.get, Object.getOwnPropertyDescriptor(realObj, attribute).get); + } // Object.defineProperty can't do anything either. let names = ["expando"];