зеркало из https://github.com/mozilla/gecko-dev.git
Bug 645416, part 27 - Implement Object.getOwnPropertySymbols(). r=Waldo.
--HG-- extra : rebase_source : 88b44837f0b845f88a881d4b472d3c9ef44bcbf0
This commit is contained in:
Родитель
9a4a0f2647
Коммит
3d134987af
|
@ -889,34 +889,46 @@ obj_is(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
|
||||
bool
|
||||
js::IdToStringOrSymbol(JSContext *cx, HandleId id, MutableHandleValue result)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject obj(cx);
|
||||
if (!GetFirstArgumentAsObject(cx, args, "Object.getOwnPropertyNames", &obj))
|
||||
if (JSID_IS_INT(id)) {
|
||||
JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
|
||||
if (!str)
|
||||
return false;
|
||||
result.setString(str);
|
||||
} else if (JSID_IS_ATOM(id)) {
|
||||
result.setString(JSID_TO_STRING(id));
|
||||
} else {
|
||||
result.setSymbol(JSID_TO_SYMBOL(id));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES6 draft rev 25 (2014 May 22) 19.1.2.8.1 */
|
||||
static bool
|
||||
GetOwnPropertyKeys(JSContext *cx, const CallArgs &args, unsigned flags, const char *fnname)
|
||||
{
|
||||
// steps 1-2
|
||||
RootedObject obj(cx, ToObject(cx, args.get(0)));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
// steps 3-10
|
||||
AutoIdVector keys(cx);
|
||||
if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
|
||||
if (!GetPropertyNames(cx, obj, flags, &keys))
|
||||
return false;
|
||||
|
||||
// step 11
|
||||
AutoValueVector vals(cx);
|
||||
if (!vals.resize(keys.length()))
|
||||
return false;
|
||||
|
||||
for (size_t i = 0, len = keys.length(); i < len; i++) {
|
||||
jsid id = keys[i];
|
||||
if (JSID_IS_INT(id)) {
|
||||
JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
|
||||
if (!str)
|
||||
return false;
|
||||
vals[i].setString(str);
|
||||
} else if (JSID_IS_ATOM(id)) {
|
||||
vals[i].setString(JSID_TO_STRING(id));
|
||||
} else {
|
||||
vals[i].setSymbol(JSID_TO_SYMBOL(id));
|
||||
}
|
||||
MOZ_ASSERT_IF(JSID_IS_SYMBOL(keys[i]), flags & JSITER_SYMBOLS);
|
||||
MOZ_ASSERT_IF(!JSID_IS_SYMBOL(keys[i]), !(flags & JSITER_SYMBOLSONLY));
|
||||
if (!IdToStringOrSymbol(cx, keys[i], vals[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
|
||||
|
@ -927,6 +939,24 @@ obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY | JSITER_HIDDEN,
|
||||
"Object.getOwnPropertyNames");
|
||||
}
|
||||
|
||||
/* ES6 draft rev 25 (2014 May 22) 19.1.2.8 */
|
||||
static bool
|
||||
obj_getOwnPropertySymbols(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return GetOwnPropertyKeys(cx, args,
|
||||
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY,
|
||||
"Object.getOwnPropertySymbols");
|
||||
}
|
||||
|
||||
/* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
|
||||
static bool
|
||||
obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
@ -1179,6 +1209,7 @@ const JSFunctionSpec js::object_static_methods[] = {
|
|||
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),
|
||||
|
|
|
@ -21,6 +21,14 @@ extern const JSFunctionSpec object_static_methods[];
|
|||
bool
|
||||
obj_construct(JSContext *cx, unsigned argc, JS::Value *vp);
|
||||
|
||||
/*
|
||||
* Like IdToValue, but convert int jsids to strings. This is used when
|
||||
* exposing a jsid to script for Object.getOwnProperty{Names,Symbols}
|
||||
* or scriptable proxy traps.
|
||||
*/
|
||||
bool
|
||||
IdToStringOrSymbol(JSContext *cx, JS::HandleId id, JS::MutableHandleValue result);
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
// Object.prototype.toSource. Function.prototype.toSource and uneval use this.
|
||||
JSString *
|
||||
|
|
|
@ -825,6 +825,7 @@ IsObjectInContextCompartment(JSObject *obj, const JSContext *cx);
|
|||
#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */
|
||||
#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */
|
||||
#define JSITER_SYMBOLS 0x20 /* also include symbol property keys */
|
||||
#define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
RunningWithTrustedPrincipals(JSContext *cx);
|
||||
|
|
|
@ -119,8 +119,9 @@ Enumerate(JSContext *cx, HandleObject pobj, jsid id,
|
|||
}
|
||||
|
||||
// Symbol-keyed properties and nonenumerable properties are skipped unless
|
||||
// the caller specifically asks for them.
|
||||
if (JSID_IS_SYMBOL(id) && !(flags & JSITER_SYMBOLS))
|
||||
// the caller specifically asks for them. A caller can also filter out
|
||||
// non-symbols by asking for JSITER_SYMBOLSONLY.
|
||||
if (JSID_IS_SYMBOL(id) ? !(flags & JSITER_SYMBOLS) : (flags & JSITER_SYMBOLSONLY))
|
||||
return true;
|
||||
if (!enumerable && !(flags & JSITER_HIDDEN))
|
||||
return true;
|
||||
|
@ -132,50 +133,57 @@ static bool
|
|||
EnumerateNativeProperties(JSContext *cx, HandleObject pobj, unsigned flags, IdSet &ht,
|
||||
AutoIdVector *props)
|
||||
{
|
||||
/* Collect any dense elements from this object. */
|
||||
size_t initlen = pobj->getDenseInitializedLength();
|
||||
const Value *vp = pobj->getDenseElements();
|
||||
for (size_t i = 0; i < initlen; ++i, ++vp) {
|
||||
if (!vp->isMagic(JS_ELEMENTS_HOLE)) {
|
||||
/* Dense arrays never get so large that i would not fit into an integer id. */
|
||||
if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
|
||||
bool enumerateSymbols;
|
||||
if (flags & JSITER_SYMBOLSONLY) {
|
||||
enumerateSymbols = true;
|
||||
} else {
|
||||
/* Collect any dense elements from this object. */
|
||||
size_t initlen = pobj->getDenseInitializedLength();
|
||||
const Value *vp = pobj->getDenseElements();
|
||||
for (size_t i = 0; i < initlen; ++i, ++vp) {
|
||||
if (!vp->isMagic(JS_ELEMENTS_HOLE)) {
|
||||
/* Dense arrays never get so large that i would not fit into an integer id. */
|
||||
if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Collect any typed array elements from this object. */
|
||||
if (pobj->is<TypedArrayObject>()) {
|
||||
size_t len = pobj->as<TypedArrayObject>().length();
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t initialLength = props->length();
|
||||
|
||||
/* Collect all unique property names from this object's shape. */
|
||||
bool symbolsFound = false;
|
||||
Shape::Range<NoGC> r(pobj->lastProperty());
|
||||
for (; !r.empty(); r.popFront()) {
|
||||
Shape &shape = r.front();
|
||||
jsid id = shape.propid();
|
||||
|
||||
if (JSID_IS_SYMBOL(id)) {
|
||||
symbolsFound = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Enumerate(cx, pobj, id, shape.enumerable(), flags, ht, props))
|
||||
return false;
|
||||
}
|
||||
::Reverse(props->begin() + initialLength, props->end());
|
||||
|
||||
enumerateSymbols = symbolsFound && (flags & JSITER_SYMBOLS);
|
||||
}
|
||||
|
||||
/* Collect any typed array elements from this object. */
|
||||
if (pobj->is<TypedArrayObject>()) {
|
||||
size_t len = pobj->as<TypedArrayObject>().length();
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t initialLength = props->length();
|
||||
|
||||
/* Collect all unique property names from this object's shape. */
|
||||
Shape::Range<NoGC> r(pobj->lastProperty());
|
||||
bool symbolsFound = false;
|
||||
for (; !r.empty(); r.popFront()) {
|
||||
Shape &shape = r.front();
|
||||
jsid id = shape.propid();
|
||||
|
||||
if (JSID_IS_SYMBOL(id)) {
|
||||
symbolsFound = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Enumerate(cx, pobj, id, shape.enumerable(), flags, ht, props))
|
||||
return false;
|
||||
}
|
||||
::Reverse(props->begin() + initialLength, props->end());
|
||||
|
||||
if (symbolsFound && (flags & JSITER_SYMBOLS)) {
|
||||
if (enumerateSymbols) {
|
||||
// Do a second pass to collect symbols. ES6 draft rev 25 (2014 May 22)
|
||||
// 9.1.12 requires that all symbols appear after all strings in the
|
||||
// result.
|
||||
initialLength = props->length();
|
||||
size_t initialLength = props->length();
|
||||
for (Shape::Range<NoGC> r(pobj->lastProperty()); !r.empty(); r.popFront()) {
|
||||
Shape &shape = r.front();
|
||||
jsid id = shape.propid();
|
||||
|
@ -388,7 +396,9 @@ js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
|
|||
JS_FRIEND_API(bool)
|
||||
js::GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
|
||||
{
|
||||
return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS), props);
|
||||
return Snapshot(cx, obj,
|
||||
flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY),
|
||||
props);
|
||||
}
|
||||
|
||||
size_t sCustomIteratorCount = 0;
|
||||
|
|
|
@ -650,23 +650,10 @@ Trap(JSContext *cx, HandleObject handler, HandleValue fval, unsigned argc, Value
|
|||
return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
|
||||
}
|
||||
|
||||
static bool
|
||||
IdToExposableValue(JSContext *cx, HandleId id, MutableHandleValue value)
|
||||
{
|
||||
value.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
|
||||
if (value.isSymbol())
|
||||
return true;
|
||||
JSString *name = ToString<CanGC>(cx, value);
|
||||
if (!name)
|
||||
return false;
|
||||
value.setString(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
Trap1(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, MutableHandleValue rval)
|
||||
{
|
||||
if (!IdToExposableValue(cx, id, rval)) // Re-use out-param to avoid Rooted overhead.
|
||||
if (!IdToStringOrSymbol(cx, id, rval))
|
||||
return false;
|
||||
return Trap(cx, handler, fval, 1, rval.address(), rval);
|
||||
}
|
||||
|
@ -676,7 +663,7 @@ Trap2(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, Value
|
|||
MutableHandleValue rval)
|
||||
{
|
||||
RootedValue v(cx, v_);
|
||||
if (!IdToExposableValue(cx, id, rval)) // Re-use out-param to avoid Rooted overhead.
|
||||
if (!IdToStringOrSymbol(cx, id, rval))
|
||||
return false;
|
||||
JS::AutoValueArray<2> argv(cx);
|
||||
argv[0].set(rval);
|
||||
|
@ -950,7 +937,7 @@ ScriptedIndirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObjec
|
|||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue idv(cx);
|
||||
if (!IdToExposableValue(cx, id, &idv))
|
||||
if (!IdToStringOrSymbol(cx, id, &idv))
|
||||
return false;
|
||||
JS::AutoValueArray<2> argv(cx);
|
||||
argv[0].setObjectOrNull(receiver);
|
||||
|
@ -969,7 +956,7 @@ ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObjec
|
|||
{
|
||||
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
|
||||
RootedValue idv(cx);
|
||||
if (!IdToExposableValue(cx, id, &idv))
|
||||
if (!IdToStringOrSymbol(cx, id, &idv))
|
||||
return false;
|
||||
JS::AutoValueArray<3> argv(cx);
|
||||
argv[0].setObjectOrNull(receiver);
|
||||
|
@ -1465,7 +1452,7 @@ ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject
|
|||
|
||||
// step 8-9
|
||||
RootedValue propKey(cx);
|
||||
if (!IdToExposableValue(cx, id, &propKey))
|
||||
if (!IdToStringOrSymbol(cx, id, &propKey))
|
||||
return false;
|
||||
|
||||
Value argv[] = {
|
||||
|
@ -1588,7 +1575,7 @@ ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, Ha
|
|||
|
||||
// step 10, 12
|
||||
RootedValue propKey(cx);
|
||||
if (!IdToExposableValue(cx, id, &propKey))
|
||||
if (!IdToStringOrSymbol(cx, id, &propKey))
|
||||
return false;
|
||||
|
||||
Value argv[] = {
|
||||
|
@ -1711,7 +1698,7 @@ ScriptedDirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId
|
|||
|
||||
// step 8
|
||||
RootedValue value(cx);
|
||||
if (!IdToExposableValue(cx, id, &value))
|
||||
if (!IdToStringOrSymbol(cx, id, &value))
|
||||
return false;
|
||||
Value argv[] = {
|
||||
ObjectValue(*target),
|
||||
|
@ -1809,7 +1796,7 @@ ScriptedDirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id,
|
|||
|
||||
// step 5
|
||||
RootedValue value(cx);
|
||||
if (!IdToExposableValue(cx, id, &value))
|
||||
if (!IdToStringOrSymbol(cx, id, &value))
|
||||
return false;
|
||||
Value argv[] = {
|
||||
ObjectOrNullValue(target),
|
||||
|
@ -1871,7 +1858,7 @@ ScriptedDirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject
|
|||
|
||||
// step 5
|
||||
RootedValue value(cx);
|
||||
if (!IdToExposableValue(cx, id, &value))
|
||||
if (!IdToStringOrSymbol(cx, id, &value))
|
||||
return false;
|
||||
Value argv[] = {
|
||||
ObjectOrNullValue(target),
|
||||
|
@ -1934,7 +1921,7 @@ ScriptedDirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject
|
|||
|
||||
// step 5
|
||||
RootedValue value(cx);
|
||||
if (!IdToExposableValue(cx, id, &value))
|
||||
if (!IdToStringOrSymbol(cx, id, &value))
|
||||
return false;
|
||||
Value argv[] = {
|
||||
ObjectOrNullValue(target),
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/ */
|
||||
|
||||
// getOwnPropertySymbols(proxy) calls the getOwnPropertyNames hook (only).
|
||||
|
||||
var symbols = [Symbol(), Symbol("moon"), Symbol.for("sun"), Symbol.iterator];
|
||||
var hits = 0;
|
||||
|
||||
function HandlerProxy() {
|
||||
return new Proxy({}, {
|
||||
get: function (t, key) {
|
||||
if (key !== "ownKeys")
|
||||
throw new Error("tried to access handler[" + uneval(key) + "]");
|
||||
hits++;
|
||||
return t => symbols;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function OwnKeysProxy() {
|
||||
return new Proxy({}, new HandlerProxy);
|
||||
}
|
||||
|
||||
assertDeepEq(Object.getOwnPropertySymbols(new OwnKeysProxy), symbols);
|
||||
assertEq(hits, 1);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,46 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/ */
|
||||
|
||||
assertDeepEq(Object.getOwnPropertySymbols({}), []);
|
||||
|
||||
// String keys are ignored.
|
||||
assertEq(Object.getOwnPropertySymbols({a: 1, b: 2}).length, 0);
|
||||
assertEq(Object.getOwnPropertySymbols([0, 1, 2, 3]).length, 0);
|
||||
|
||||
// Symbol keys are observed.
|
||||
var iterable = {};
|
||||
Object.defineProperty(iterable, Symbol.iterator, {
|
||||
value: () => [][Symbol.iterator]()
|
||||
});
|
||||
assertDeepEq(Object.getOwnPropertySymbols(iterable), [Symbol.iterator]);
|
||||
assertDeepEq(Object.getOwnPropertySymbols(new Proxy(iterable, {})), [Symbol.iterator]);
|
||||
|
||||
// Test on an object with a thousand own properties.
|
||||
var obj = {};
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
obj[Symbol.for("x" + i)] = 1;
|
||||
}
|
||||
assertEq(Object.getOwnPropertyNames(obj).length, 0);
|
||||
var symbols = Object.getOwnPropertySymbols(obj);
|
||||
assertEq(symbols.length, 1000);
|
||||
assertEq(symbols.indexOf(Symbol.for("x0")) !== -1, true);
|
||||
assertEq(symbols.indexOf(Symbol.for("x241")) !== -1, true);
|
||||
assertEq(symbols.indexOf(Symbol.for("x999")) !== -1, true);
|
||||
assertEq(Object.getOwnPropertySymbols(new Proxy(obj, {})).length, 1000);
|
||||
|
||||
// The prototype chain is not consulted.
|
||||
assertEq(Object.getOwnPropertySymbols(Object.create(obj)).length, 0);
|
||||
assertEq(Object.getOwnPropertySymbols(new Proxy(Object.create(obj), {})).length, 0);
|
||||
|
||||
// Primitives are coerced to objects; but there are never any symbol-keyed
|
||||
// properties on the resulting wrapper objects.
|
||||
assertThrowsInstanceOf(() => Object.getOwnPropertySymbols(), TypeError);
|
||||
assertThrowsInstanceOf(() => Object.getOwnPropertySymbols(undefined), TypeError);
|
||||
assertThrowsInstanceOf(() => Object.getOwnPropertySymbols(null), TypeError);
|
||||
for (var primitive of [true, 1, 3.14, "hello", Symbol()])
|
||||
assertEq(Object.getOwnPropertySymbols(primitive).length, 0);
|
||||
|
||||
assertEq(Object.getOwnPropertySymbols.length, 1);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
Загрузка…
Ссылка в новой задаче