blob: 9450ec64ece13a384c9256d39b5306e10eaac950 [file] [log] [blame]
const dontEnumDesc = { value: 1, writable: true, enumerable: false, configurable: true };
const testCases = [
{
name: "Object",
createObject: () => Object.create(null, { foo: dontEnumDesc, bar: dontEnumDesc }),
dontEnumKeys: ["foo", "bar"],
},
{
name: "Error",
createObject: () => new Error(),
dontEnumKeys: ["line", "column", "sourceURL", "stack"],
},
{
name: "Array (empty)",
createObject: () => [],
dontEnumKeys: ["length"],
},
{
name: "Array (sparse)",
createObject: () => Object.defineProperties([0, 1, 2], { 0: dontEnumDesc, 2: dontEnumDesc }),
dontEnumKeys: ["0", "2", "length"],
},
{
name: "Function (strict)",
createObject: () => function() { "use strict"; },
dontEnumKeys: ["length", "name", "prototype"],
},
{
name: "Function (non-strict)",
createObject: () => function() {},
dontEnumKeys: ["length", "name", "prototype"],
},
{
name: "RegExp",
createObject: () => /(?:)/g,
dontEnumKeys: ["lastIndex"],
},
{
name: "String",
createObject: () => new String("foo"),
dontEnumKeys: ["length"],
},
{
name: "Arguments (strict)",
createObject: function(foo) { "use strict"; return arguments; },
dontEnumKeys: ["length", "callee"],
},
{
name: "Arguments (non-strict)",
createObject: function(foo) { return arguments; },
dontEnumKeys: ["length", "callee"],
},
{
name: "Reflect",
createObject: () => $vm.createGlobalObject().Reflect,
dontEnumKeys: ["apply", "get", "has", "set"],
},
{
name: "Date.prototype",
createObject: () => $vm.createGlobalObject().Date.prototype,
dontEnumKeys: ["toISOString", "getTime", "setYear"],
},
];
// basic tests
for (const t of testCases) {
assert(!contains(forIn(t.createObject()), t.dontEnumKeys), t.name);
assert(!contains(Object.keys(t.createObject()), t.dontEnumKeys), t.name);
assert(contains(Object.getOwnPropertyNames(t.createObject()), t.dontEnumKeys), t.name);
assert(contains(Reflect.ownKeys(t.createObject()), t.dontEnumKeys), t.name);
}
// shadowing: DontEnum => Enum
for (const t of testCases) {
assert(
!contains(
forIn(makePrototypeChain(t.createObject(), makeObject(t.dontEnumKeys))),
t.dontEnumKeys,
),
t.name,
);
}
// shadowing: {} => DontEnum => {} => Enum
for (const t of testCases) {
assert(
!contains(
forIn(makePrototypeChain({}, t.createObject(), {}, makeObject(t.dontEnumKeys))),
t.dontEnumKeys,
),
t.name,
);
}
// shadowing: DontEnum => {} => Enum => {} => Enum
for (const t of testCases) {
assert(
!contains(
forIn(makePrototypeChain(t.createObject(), {}, makeObject(t.dontEnumKeys), {}, makeObject(t.dontEnumKeys))),
t.dontEnumKeys,
),
t.name,
);
}
// shadowing: {} => DontEnum (enumerable: true => false) => {} => Enum
for (const t of testCases) {
const dontEnumObject = t.createObject();
const target = makePrototypeChain({}, dontEnumObject, {}, makeObject(t.dontEnumKeys));
assert(!contains(forIn(target), t.dontEnumKeys), t.name);
const enumKeys = t.dontEnumKeys.filter(key => Reflect.defineProperty(dontEnumObject, key, { enumerable: true }));
assert(contains(forIn(target), enumKeys), t.name);
}
// shadowing: {} => DontEnum (delete non-enumerable keys) => {} => Enum
for (const t of testCases) {
const dontEnumObject = t.createObject();
const target = makePrototypeChain({}, dontEnumObject, {}, makeObject(t.dontEnumKeys));
assert(!contains(forIn(target), t.dontEnumKeys), t.name);
const enumKeys = t.dontEnumKeys.filter(key => Reflect.deleteProperty(dontEnumObject, key));
assert(contains(forIn(target), enumKeys), t.name);
}
// shadowing: {} => DontEnum (materialized) => {} => Enum
for (const t of testCases) {
const dontEnumObject = t.createObject();
const target = makePrototypeChain({}, dontEnumObject, {}, makeObject(t.dontEnumKeys));
assert(!contains(forIn(target), t.dontEnumKeys), t.name);
const dontEnumKeys = t.dontEnumKeys.filter(key => {
const desc = Object.getOwnPropertyDescriptor(dontEnumObject, key);
if (!Reflect.deleteProperty(dontEnumObject, key)) return false;
Object.defineProperty(dontEnumObject, key, desc);
return true;
});
if (dontEnumKeys.length)
assert(!contains(forIn(target), dontEnumKeys), t.name);
}
// helpers
function assert(value, name) {
if (!value)
throw new Error(`Bad value: ${value}. Test case: ${name}.`);
}
function contains(array, subarray) {
return subarray.every(item => array.includes(item));
}
function forIn(object) {
const keys = [];
for (const key in object)
keys.push(key);
return keys;
}
function makePrototypeChain(...objects) {
objects.reduce((object, prototype) => {
Object.setPrototypeOf(object, prototype);
return prototype;
});
return objects[0];
}
function makeObject(keys) {
const object = {};
for (const key of keys)
object[key] = key;
return object;
}