Bug 645416, part 23 - Implement ValueToId for symbols. This makes symbols work as property keys. r=efaust.

--HG--
extra : rebase_source : eec18157d90daf54afc300b81d351011e5b669d5
This commit is contained in:
Jason Orendorff 2014-06-23 10:57:02 -05:00
parent f427cf7043
commit 07ec21e434
11 changed files with 384 additions and 20 deletions

View File

@ -2,14 +2,33 @@
var g = newGlobal();
var dbg = Debugger(g);
var names;
var withNames, globalNames;
g.h = function () {
names = dbg.getNewestFrame().environment.names();
var env = dbg.getNewestFrame().environment;
withNames = env.names();
while (env.parent !== null)
env = env.parent;
globalNames = env.names();
};
g.eval("var obj = {a: 1};\n" +
"with ({a: 1, '0xcafe': 2, ' ': 3, '': 4, '0': 5}) h();");
assertEq(names.indexOf("a") !== -1, true);
assertEq(names.indexOf("0xcafe"), -1);
assertEq(names.indexOf(" "), -1);
assertEq(names.indexOf(""), -1);
assertEq(names.indexOf("0"), -1);
g.eval("" +
function fill(obj) {
obj.sanityCheck = 1;
obj["0xcafe"] = 2;
obj[" "] = 3;
obj[""] = 4;
obj[0] = 5;
obj[Symbol.for("moon")] = 6;
return obj;
})
g.eval("fill(this);\n" +
"with (fill({})) h();");
for (var names of [withNames, globalNames]) {
assertEq(names.indexOf("sanityCheck") !== -1, true);
assertEq(names.indexOf("0xcafe"), -1);
assertEq(names.indexOf(" "), -1);
assertEq(names.indexOf(""), -1);
assertEq(names.indexOf("0"), -1);
assertEq(names.indexOf(Symbol.for("moon")), -1);
}

View File

@ -1,13 +1,15 @@
// Forward to the target if the trap is not defined
var target = {};
Object.defineProperty(Proxy(target, {}), 'foo', {
value: 'bar',
writable: true,
enumerable: false,
configurable: true
});
var desc = Object.getOwnPropertyDescriptor(target, 'foo');
assertEq(desc.value, 'bar');
assertEq(desc.writable, true);
assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
for (var key of ['foo', Symbol("quux")]) {
Object.defineProperty(Proxy(target, {}), key, {
value: 'bar',
writable: true,
enumerable: false,
configurable: true
});
var desc = Object.getOwnPropertyDescriptor(target, key);
assertEq(desc.value, 'bar');
assertEq(desc.writable, true);
assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
}

View File

@ -49,6 +49,11 @@ ValueToIdPure(const Value &v, jsid *id)
return true;
}
if (v.isSymbol()) {
*id = SYMBOL_TO_JSID(v.toSymbol());
return true;
}
if (!v.isString() || !v.toString()->isAtom())
return false;
@ -67,6 +72,11 @@ ValueToId(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v,
return true;
}
if (v.isSymbol()) {
idp.set(SYMBOL_TO_JSID(v.toSymbol()));
return true;
}
JSAtom *atom = ToAtom<allowGC>(cx, v);
if (!atom)
return false;

View File

@ -1132,6 +1132,9 @@ public:
bool
js_SuppressDeletedProperty(JSContext *cx, HandleObject obj, jsid id)
{
if (JSID_IS_SYMBOL(id))
return true;
Rooted<JSFlatString*> str(cx, IdToString(cx, id));
if (!str)
return false;

View File

@ -0,0 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/ */
var obj = {};
var sym = Symbol();
var gets = 0;
var sets = [];
Object.defineProperty(obj, sym, {
get: function () { return ++gets; },
set: function (v) { sets.push(v); }
});
// getter
for (var i = 1; i < 9; i++)
assertEq(obj[sym], i);
// setter
var expected = [];
for (var i = 0; i < 9; i++) {
assertEq(obj[sym] = i, i);
expected.push(i);
}
assertDeepEq(sets, expected);
// increment operator
gets = 0;
sets = [];
assertEq(obj[sym]++, 1);
assertDeepEq(sets, [2]);
// assignment
gets = 0;
sets = [];
assertEq(obj[sym] *= 12, 12);
assertDeepEq(sets, [12]);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View File

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/ */
var symbols = [
Symbol(),
Symbol("one"),
Symbol.for("two"),
Symbol.iterator
];
for (var sym of symbols) {
var obj = {};
// access a nonexistent property
assertEq(sym in obj, false);
assertEq(obj.hasOwnProperty(sym), false);
assertEq(obj[sym], undefined);
assertEq(typeof obj[sym], "undefined");
assertEq(Object.getOwnPropertyDescriptor(obj, sym), undefined);
// assign, then try accessing again
obj[sym] = "ok";
assertEq(sym in obj, true);
assertEq(obj.hasOwnProperty(sym), true);
assertEq(obj[sym], "ok");
assertDeepEq(Object.getOwnPropertyDescriptor(obj, sym), {
configurable: true,
enumerable: true,
value: "ok",
writable: true
});
// assign again, observe value is overwritten
obj[sym] = 12;
assertEq(obj[sym], 12);
// increment
assertEq(obj[sym]++, 12);
assertEq(obj[sym], 13);
}
if (typeof reportCompare === "function")
reportCompare(0, 0);

View File

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/ */
var sym = Symbol.for("hello");
function F() {}
var f = new F();
// inherited data property
F.prototype[sym] = "world";
assertEq(sym in f, true);
assertEq(f.hasOwnProperty(sym), false);
assertEq(f[sym], "world");
// shadowing assignment
f[sym] = "kitty";
assertEq(f[sym], "kitty");
assertEq(F.prototype[sym], "world");
// deletion, revealing previously shadowed property
assertEq(delete f[sym], true);
assertEq(f.hasOwnProperty(sym), false);
assertEq(f[sym], "world");
// inherited accessor property
var value = undefined;
Object.defineProperty(F.prototype, sym, {
configurable: true,
get: function () { return 23; },
set: function (v) { value = v; }
});
assertEq(sym in f, true);
assertEq(f.hasOwnProperty(sym), false);
assertEq(f[sym], 23);
f[sym] = "gravity";
assertEq(value, "gravity");
// inherited accessor property with no setter
Object.defineProperty(F.prototype, sym, {
set: undefined
});
assertThrowsInstanceOf(function () { "use strict"; f[sym] = 0; }, TypeError);
// deeply inherited accessor property
var g = Object.create(f);
for (var i = 0; i < 100; i++)
g = Object.create(g);
assertEq(g[sym], 23);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View File

@ -0,0 +1,31 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/ */
var sym = Symbol.for("moon");
function checkNotWritable(obj) {
// In sloppy mode, assigning to a nonwritable property silently fails.
obj[sym] = "portals";
assertEq(obj[sym], "cheese");
// In strict mode code, it throws.
assertThrowsInstanceOf(function () { "use strict"; obj[sym] = "robots"; }, TypeError);
assertEq(obj[sym], "cheese");
}
var x = {};
Object.defineProperty(x, sym, {
configurable: true,
enumerable: true,
value: "cheese",
writable: false
});
checkNotWritable(x);
// Assignment can't shadow inherited nonwritable properties either.
var y = Object.create(x);
checkNotWritable(y);
checkNotWritable(Object.create(y));
if (typeof reportCompare === "function")
reportCompare(0, 0);

View File

@ -0,0 +1,138 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/ */
// Basic tests for standard Object APIs interacting with symbols.
// Object.defineProperty
function F() {}
var f = new F;
Object.defineProperty(f, Symbol.for("name"), {
configurable: true,
value: "eff"
});
assertEq("name" in f, false);
assertEq("Symbol(name)" in f, false);
assertEq(Symbol.for("name") in f, true);
assertEq(f[Symbol.for("name")], "eff");
// Object.defineProperties
function D() {}
var descs = new D;
var s1 = Symbol("s1");
var hits = 0;
descs[s1] = {
configurable: true,
enumerable: true,
get: () => hits++,
set: undefined
};
var s2 = Symbol("s2");
descs[s2] = {
configurable: true,
enumerable: false,
value: {},
writable: true
};
var s3 = Symbol("s3");
D.prototype[s3] = {value: "FAIL"};
assertEq(Object.defineProperties(f, descs), f);
assertEq(s1 in f, true);
assertEq(f[s1], 0);
assertEq(hits, 1);
assertEq(s2 in f, true);
assertEq(f[s2], descs[s2].value);
assertEq(s3 in f, false);
// Object.create
var n = Object.create({}, descs);
assertEq(s1 in n, true);
assertEq(n[s1], 1);
assertEq(hits, 2);
assertEq(s2 in n, true);
assertEq(n[s2], descs[s2].value);
assertEq(s3 in n, false);
// Object.getOwnPropertyDescriptor
var desc = Object.getOwnPropertyDescriptor(n, s1);
assertDeepEq(desc, descs[s1]);
assertEq(desc.get, descs[s1].get);
desc = Object.getOwnPropertyDescriptor(n, s2);
assertDeepEq(desc, descs[s2]);
assertEq(desc.value, descs[s2].value);
// Object.prototype.hasOwnProperty
assertEq(descs.hasOwnProperty(s1), true);
assertEq(descs.hasOwnProperty(s2), true);
assertEq(descs.hasOwnProperty(s3), false);
assertEq([].hasOwnProperty(Symbol.iterator), false);
if (!("@@iterator" in []))
throw new Error("Congratulations on implementing Symbol.iterator! Please update this test.");
assertEq(Array.prototype.hasOwnProperty(Symbol.iterator), false); // should be true
// Object.prototype.propertyIsEnumerable
assertEq(n.propertyIsEnumerable(s1), true);
assertEq(n.propertyIsEnumerable(s2), false);
assertEq(n.propertyIsEnumerable(s3), false); // no such property
assertEq(D.prototype.propertyIsEnumerable(s3), true);
assertEq(descs.propertyIsEnumerable(s3), false); // inherited properties are not considered
// Object.preventExtensions
var obj = {};
obj[s1] = 1;
assertEq(Object.preventExtensions(obj), obj);
assertThrowsInstanceOf(function () { "use strict"; obj[s2] = 2; }, TypeError);
obj[s2] = 2; // still no effect
assertEq(s2 in obj, false);
// Object.isSealed, Object.isFrozen
assertEq(Object.isSealed(obj), false);
assertEq(Object.isFrozen(obj), false);
assertEq(delete obj[s1], true);
assertEq(Object.isSealed(obj), true);
assertEq(Object.isFrozen(obj), true);
obj = {};
obj[s1] = 1;
Object.preventExtensions(obj);
Object.defineProperty(obj, s1, {configurable: false}); // still writable
assertEq(Object.isSealed(obj), true);
assertEq(Object.isFrozen(obj), false);
obj[s1] = 2;
assertEq(obj[s1], 2);
Object.defineProperty(obj, s1, {writable: false});
assertEq(Object.isFrozen(obj), true);
// Object.seal, Object.freeze
var obj = {};
obj[s1] = 1;
Object.seal(obj);
desc = Object.getOwnPropertyDescriptor(obj, s1);
assertEq(desc.configurable, false);
assertEq(desc.writable, true);
Object.freeze(obj);
assertEq(Object.getOwnPropertyDescriptor(obj, s1).writable, false);
// Object.setPrototypeOf purges caches for symbol-keyed properties.
var proto = {};
proto[s1] = 1;
Object.defineProperty(proto, s2, {
get: () => 2,
set: v => undefined
});
var obj = Object.create(proto);
var last1, last2;
var N = 9;
for (var i = 0; i < N; i++) {
last1 = obj[s1];
last2 = obj[s2];
obj[s2] = "marker";
if (i === N - 2)
Object.setPrototypeOf(obj, {});
}
assertEq(last1, undefined);
assertEq(last2, undefined);
assertEq(obj.hasOwnProperty(s2), true);
assertEq(obj[s2], "marker");
if (typeof reportCompare === "function")
reportCompare(0, 0);

View File

@ -0,0 +1,19 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var sym = Symbol("method");
var hits = 0;
var obj = {
__noSuchMethod__: function (key, args) {
assertEq(key, sym);
assertEq(args.length, 2);
assertEq(args[0], "hello");
assertEq(args[1], "world");
hits++;
}
};
obj[sym]("hello", "world");
assertEq(hits, 1);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View File

@ -356,6 +356,16 @@ GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObje
break;
}
if (rref.isSymbol()) {
RootedObject obj(cx, objArg);
RootedId id(cx, SYMBOL_TO_JSID(rref.toSymbol()));
if (!JSObject::getGeneric(cx, obj, obj, id, res))
return false;
objArg = obj;
break;
}
JSAtom *name = ToAtom<NoGC>(cx, rref);
if (name) {
if (name->isIndex(&index)) {