зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1049662 - Update ES6 scripted proxies ownKeys trap to ES6 final. r=efaust
This commit is contained in:
Родитель
a5b961e2c0
Коммит
24ba855dcf
|
@ -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 "
|
||||
|
|
Загрузка…
Ссылка в новой задаче