Bug 1049662 - Update ES6 scripted proxies ownKeys trap to ES6 final. r=efaust

This commit is contained in:
Tom Schuster 2015-07-20 11:19:51 +02:00
Родитель a5b961e2c0
Коммит 24ba855dcf
8 изменённых файлов: 178 добавлений и 179 удалений

Просмотреть файл

@ -1,6 +1,5 @@
load(libdir + "asserts.js");
// Throw a TypeError if the trap reports the same property twice
var handler = { ownKeys : () => [ 'foo', 'foo' ] };
for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy])
assertThrowsInstanceOf(() => Object.getOwnPropertyNames(p), TypeError);
assertDeepEq(Object.getOwnPropertyNames(p), ['foo', 'foo']);

Просмотреть файл

@ -1,6 +1,5 @@
load(libdir + "asserts.js");
// Throw a TypeError if the trap reports the same property twice
var handler = { ownKeys: () => [ 'foo', 'foo' ] };
for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy])
assertThrowsInstanceOf(() => Object.keys(p), TypeError);
assertDeepEq(Object.keys(p), []); // Properties are not enumerable.

Просмотреть файл

@ -370,6 +370,7 @@ MSG_DEF(JSMSG_CANT_SET_NW_NC, 0, JSEXN_TYPEERR, "proxy can't successful
MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter")
MSG_DEF(JSMSG_CANT_SKIP_NC, 0, JSEXN_TYPEERR, "proxy can't skip a non-configurable property")
MSG_DEF(JSMSG_INVALID_TRAP_RESULT, 2, JSEXN_TYPEERR, "trap {1} for {0} returned an invalid result")
MSG_DEF(JSMSG_ONWKEYS_STR_SYM, 0, JSEXN_TYPEERR, "proxy [[OwnPropertyKeys]] must return an array with only string and symbol elements")
MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 0, JSEXN_TYPEERR, "proxy must report the same value for a non-writable, non-configurable property")
MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 0, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property without a getter")
MSG_DEF(JSMSG_OBJECT_ACCESS_DENIED, 0, JSEXN_ERR, "Permission denied to access object")

Просмотреть файл

@ -101,7 +101,12 @@ static inline bool
Enumerate(JSContext* cx, HandleObject pobj, jsid id,
bool enumerable, unsigned flags, Maybe<IdSet>& ht, AutoIdVector* props)
{
if (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() || pobj->getOps()->enumerate) {
// Allow duplicate properties from Proxy's [[OwnPropertyKeys]].
bool proxyOwnProperty = pobj->is<ProxyObject>() && (flags & JSITER_OWNONLY);
if (!proxyOwnProperty && (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() ||
pobj->getOps()->enumerate))
{
if (!ht) {
ht.emplace(cx);
// Most of the time there are only a handful of entries.

Просмотреть файл

@ -127,20 +127,6 @@ ValidatePropertyDescriptor(JSContext* cx, bool extensible, Handle<PropertyDescri
return true;
}
// Aux.6 IsSealed(O, P)
static bool
IsSealed(JSContext* cx, HandleObject obj, HandleId id, bool* bp)
{
// step 1
Rooted<PropertyDescriptor> desc(cx);
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
return false;
// steps 2-3
*bp = desc.object() && !desc.configurable();
return true;
}
// Get the [[ProxyHandler]] of a scripted direct proxy.
static JSObject*
GetDirectProxyHandlerObject(JSObject* proxy)
@ -160,107 +146,6 @@ ReportInvalidTrapResult(JSContext* cx, JSObject* proxy, JSAtom* atom)
nullptr, bytes.ptr());
}
// This function is shared between ownPropertyKeys, enumerate, and
// getOwnEnumerablePropertyKeys.
static bool
ArrayToIdVector(JSContext* cx, HandleObject proxy, HandleObject target, HandleValue v,
AutoIdVector& props, unsigned flags, JSAtom* trapName_)
{
MOZ_ASSERT(v.isObject());
RootedObject array(cx, &v.toObject());
RootedAtom trapName(cx, trapName_);
// steps g-h
uint32_t n;
if (!GetLengthProperty(cx, array, &n))
return false;
// steps i-k
for (uint32_t i = 0; i < n; ++i) {
// step i
RootedValue v(cx);
if (!GetElement(cx, array, array, i, &v))
return false;
// step ii
RootedId id(cx);
if (!ValueToId<CanGC>(cx, v, &id))
return false;
// step iii
for (uint32_t j = 0; j < i; ++j) {
if (props[j].get() == id) {
ReportInvalidTrapResult(cx, proxy, trapName);
return false;
}
}
// step iv
bool isFixed;
if (!HasOwnProperty(cx, target, id, &isFixed))
return false;
// step v
bool extensible;
if (!IsExtensible(cx, target, &extensible))
return false;
if (!extensible && !isFixed) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
return false;
}
// step vi
if (!props.append(id))
return false;
}
// step l
AutoIdVector ownProps(cx);
if (!GetPropertyKeys(cx, target, flags, &ownProps))
return false;
// step m
for (size_t i = 0; i < ownProps.length(); ++i) {
RootedId id(cx, ownProps[i]);
bool found = false;
for (size_t j = 0; j < props.length(); ++j) {
if (props[j].get() == id) {
found = true;
break;
}
}
if (found)
continue;
// step i
bool sealed;
if (!IsSealed(cx, target, id, &sealed))
return false;
if (sealed) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC);
return false;
}
// step ii
bool isFixed;
if (!HasOwnProperty(cx, target, id, &isFixed))
return false;
// step iii
bool extensible;
if (!IsExtensible(cx, target, &extensible))
return false;
if (!extensible && isFixed) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
return false;
}
}
// step n
return true;
}
// ES6 implements both getPrototype and setPrototype traps. We don't have them yet (see bug
// 888969). For now, use these, to account for proxy revocation.
bool
@ -611,51 +496,181 @@ ScriptedDirectProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, Ha
return result.succeed();
}
// ES6 (5 April 2014) 9.5.12 Proxy.[[OwnPropertyKeys]]()
// ES6 7.3.17 But elementTypes is is fixed to symbol/string.
static bool
CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v, AutoIdVector& props)
{
// 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;
// Steps 6-8.
RootedValue next(cx);
RootedId id(cx);
for (uint32_t index = 0; index < len; index++) {
if (!GetElement(cx, obj, obj, index, &next))
return false;
if (!next.isString() && !next.isSymbol()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ONWKEYS_STR_SYM);
return false;
}
// Unobservable for strings/symbols.
if (!ValueToId<CanGC>(cx, next, &id))
return false;
if (!props.append(id))
return false;
}
// Step 9.
return true;
}
// ES6 9.5.12 Proxy.[[OwnPropertyKeys]]()
bool
ScriptedDirectProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
AutoIdVector& props) const
{
// step 1
// Step 1.
RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
// step 2
// Step 2.
if (!handler) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
return false;
}
// Step 3. Superfluous assertion.
// step 3
// Step 4.
RootedObject target(cx, proxy->as<ProxyObject>().target());
// step 4-5
// Steps 5-6.
RootedValue trap(cx);
if (!GetProperty(cx, handler, handler, cx->names().ownKeys, &trap))
return false;
// step 6
// Step 7.
if (trap.isUndefined())
return GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props);
// step 7-8
// Step 8.
Value argv[] = {
ObjectValue(*target)
};
RootedValue trapResult(cx);
if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
RootedValue trapResultArray(cx);
if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResultArray))
return false;
// step 9
if (trapResult.isPrimitive()) {
ReportInvalidTrapResult(cx, proxy, cx->names().ownKeys);
// Steps 9-10.
AutoIdVector trapResult(cx);
if (!CreateFilteredListFromArrayLike(cx, trapResultArray, trapResult))
return false;
// Steps 11-12.
bool extensibleTarget;
if (!IsExtensible(cx, target, &extensibleTarget))
return false;
// Steps 13-14.
AutoIdVector targetKeys(cx);
if (!GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &targetKeys))
return false;
// Step 15. Superfluous assertion.
// Steps 16-17.
AutoIdVector targetConfigurableKeys(cx);
AutoIdVector targetNonconfigurableKeys(cx);
// Step 18.
Rooted<PropertyDescriptor> desc(cx);
for (size_t i = 0; i < targetKeys.length(); ++i) {
// Steps a-b.
if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc))
return false;
// Steps c-d.
if (desc.object() && !desc.configurable()) {
if (!targetNonconfigurableKeys.append(targetKeys[i]))
return false;
} else {
if (!targetConfigurableKeys.append(targetKeys[i]))
return false;
}
}
// Here we add a bunch of extra sanity checks. It is unclear if they will also appear in
// the spec. See step 10-11
return ArrayToIdVector(cx, proxy, target, trapResult, props,
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
cx->names().ownKeys);
// Step 19.
if (extensibleTarget && targetNonconfigurableKeys.empty())
return props.appendAll(trapResult);
// Step 20.
AutoIdVector uncheckedResultKeys(cx);
if (!uncheckedResultKeys.appendAll(trapResult))
return false;
// Step 21.
for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) {
RootedId key(cx, targetNonconfigurableKeys[i]);
MOZ_ASSERT(key != JSID_VOID);
bool found = false;
for (size_t j = 0; j < uncheckedResultKeys.length(); ++j) {
if (key == uncheckedResultKeys[j]) {
uncheckedResultKeys[j].set(JSID_VOID);
found = true;
break;
}
}
if (!found) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC);
return false;
}
}
// Step 22.
if (extensibleTarget)
return props.appendAll(trapResult);
// Step 23.
for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) {
RootedId key(cx, targetConfigurableKeys[i]);
MOZ_ASSERT(key != JSID_VOID);
bool found = false;
for (size_t j = 0; j < uncheckedResultKeys.length(); ++j) {
if (key == uncheckedResultKeys[j]) {
uncheckedResultKeys[j].set(JSID_VOID);
found = true;
break;
}
}
if (!found) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
return false;
}
}
// Step 24.
for (size_t i = 0; i < uncheckedResultKeys.length(); ++i) {
if (uncheckedResultKeys[i].get() != JSID_VOID) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
return false;
}
}
// Step 25.
return props.appendAll(trapResult);
}
// ES6 draft rev 32 (2 Feb 2014) 9.5.10 Proxy.[[Delete]](P)

Просмотреть файл

@ -0,0 +1,20 @@
function makeProxy(type) {
return new Proxy({}, { ownKeys() { return [type]; } });
}
for (var type of [123, 12.5, true, false, undefined, null, {}, []]) {
var proxy = makeProxy(type);
assertThrowsInstanceOf(() => Object.ownKeys(proxy), TypeError);
assertThrowsInstanceOf(() => Object.getOwnPropertyNames(proxy), TypeError);
}
type = Symbol();
proxy = makeProxy(type);
assertEq(Object.getOwnPropertySymbols(proxy)[0], type);
type = "abc";
proxy = makeProxy(type);
assertEq(Object.getOwnPropertyNames(proxy)[0], type);
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -1,40 +0,0 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
//-----------------------------------------------------------------------------
var BUGNUMBER = 580200;
var summary =
'Assertion failure enumerating own properties of proxy returning ' +
'duplicated own property name';
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var x = Proxy.create({ keys: function() { return ["0","0"]; } }, [1,2]);
var ax = Object.keys(x);
assertEq(ax.length, 1, "array: " + ax);
assertEq(ax[0], "0");
var p = Proxy.create({ keys: function() { return ["1","1"]; } }, null);
var ap = Object.keys(p);
assertEq(ap.length, 1, "array: " + ap);
assertEq(ap[0], "1");
var x = Proxy.create({ getOwnPropertyNames: function() { return ["0","0"]; } }, [1,2]);
var ax = Object.getOwnPropertyNames(x);
assertEq(ax.length, 1, "array: " + ax);
assertEq(ax[0], "0");
var p = Proxy.create({ getOwnPropertyNames: function() { return ["1","1"]; } }, null);
var ap = Object.getOwnPropertyNames(p);
assertEq(ap.length, 1, "array: " + ap);
assertEq(ap[0], "1");
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");

Просмотреть файл

@ -29,11 +29,11 @@ namespace js {
*
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
*/
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 297;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 298;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
static_assert(JSErr_Limit == 406,
static_assert(JSErr_Limit == 407,
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
"removed MSG_DEFs from js.msg, you should increment "
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "