25b978cbf4
The unscobables allow us to black list properties from showing up in with statements. https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object-environment-records-hasbinding-n The spec draft is not fully up to date. https://github.com/rwaldron/tc39-notes/blob/master/es6/2014-07/jul-29.md#conclusionresolution BUG=v8:3401 LOG=Y R=rossberg@chromium.org, verwaest@chromium.org Review URL: https://codereview.chromium.org/384963002 Patch from Erik Arvidsson <arv@chromium.org>. git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22942 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
665 lines
14 KiB
JavaScript
665 lines
14 KiB
JavaScript
// Copyright 2014 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Flags: --harmony-unscopables
|
|
// Flags: --harmony-collections
|
|
|
|
var global = this;
|
|
var globalProto = Object.getPrototypeOf(global);
|
|
|
|
// Number of objects being tested. There is an assert ensuring this is correct.
|
|
var objectCount = 21;
|
|
|
|
|
|
function runTest(f) {
|
|
function restore(object, oldProto) {
|
|
delete object[Symbol.unscopables];
|
|
delete object.x;
|
|
delete object.x_;
|
|
delete object.y;
|
|
delete object.z;
|
|
Object.setPrototypeOf(object, oldProto);
|
|
}
|
|
|
|
function getObject(i) {
|
|
var objects = [
|
|
{},
|
|
[],
|
|
function() {},
|
|
function() {
|
|
return arguments;
|
|
}(),
|
|
function() {
|
|
'use strict';
|
|
return arguments;
|
|
}(),
|
|
Object(1),
|
|
Object(true),
|
|
Object('bla'),
|
|
new Date,
|
|
new RegExp,
|
|
new Set,
|
|
new Map,
|
|
new WeakMap,
|
|
new WeakSet,
|
|
new ArrayBuffer(10),
|
|
new Int32Array(5),
|
|
Object,
|
|
Function,
|
|
Date,
|
|
RegExp,
|
|
global
|
|
];
|
|
|
|
assertEquals(objectCount, objects.length);
|
|
return objects[i];
|
|
}
|
|
|
|
// Tests depends on this not being there to start with.
|
|
delete Array.prototype[Symbol.unscopables];
|
|
|
|
if (f.length === 1) {
|
|
for (var i = 0; i < objectCount; i++) {
|
|
var object = getObject(i);
|
|
var oldObjectProto = Object.getPrototypeOf(object);
|
|
f(object);
|
|
restore(object, oldObjectProto);
|
|
}
|
|
} else {
|
|
for (var i = 0; i < objectCount; i++) {
|
|
for (var j = 0; j < objectCount; j++) {
|
|
var object = getObject(i);
|
|
var proto = getObject(j);
|
|
if (object === proto) {
|
|
continue;
|
|
}
|
|
var oldObjectProto = Object.getPrototypeOf(object);
|
|
var oldProtoProto = Object.getPrototypeOf(proto);
|
|
f(object, proto);
|
|
restore(object, oldObjectProto);
|
|
restore(proto, oldProtoProto);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test array first, since other tests are changing
|
|
// Array.prototype[Symbol.unscopables].
|
|
function TestArrayPrototypeUnscopables() {
|
|
var descr = Object.getOwnPropertyDescriptor(Array.prototype,
|
|
Symbol.unscopables);
|
|
assertFalse(descr.enumerable);
|
|
assertFalse(descr.writable);
|
|
assertTrue(descr.configurable);
|
|
assertEquals(null, Object.getPrototypeOf(descr.value));
|
|
|
|
var copyWithin = 'local copyWithin';
|
|
var entries = 'local entries';
|
|
var fill = 'local fill';
|
|
var find = 'local find';
|
|
var findIndex = 'local findIndex';
|
|
var keys = 'local keys';
|
|
var values = 'local values';
|
|
|
|
var array = [];
|
|
array.toString = 42;
|
|
|
|
with (array) {
|
|
assertEquals('local copyWithin', copyWithin);
|
|
assertEquals('local entries', entries);
|
|
assertEquals('local fill', fill);
|
|
assertEquals('local find', find);
|
|
assertEquals('local findIndex', findIndex);
|
|
assertEquals('local keys', keys);
|
|
assertEquals('local values', values);
|
|
assertEquals(42, toString);
|
|
}
|
|
}
|
|
TestArrayPrototypeUnscopables();
|
|
|
|
|
|
|
|
function TestBasics(object) {
|
|
var x = 1;
|
|
var y = 2;
|
|
var z = 3;
|
|
object.x = 4;
|
|
object.y = 5;
|
|
|
|
with (object) {
|
|
assertEquals(4, x);
|
|
assertEquals(5, y);
|
|
assertEquals(3, z);
|
|
}
|
|
|
|
object[Symbol.unscopables] = {x: true};
|
|
with (object) {
|
|
assertEquals(1, x);
|
|
assertEquals(5, y);
|
|
assertEquals(3, z);
|
|
}
|
|
|
|
object[Symbol.unscopables] = {x: 0, y: true};
|
|
with (object) {
|
|
assertEquals(1, x);
|
|
assertEquals(2, y);
|
|
assertEquals(3, z);
|
|
}
|
|
}
|
|
runTest(TestBasics);
|
|
|
|
|
|
function TestUnscopableChain(object) {
|
|
var x = 1;
|
|
object.x = 2;
|
|
|
|
with (object) {
|
|
assertEquals(2, x);
|
|
}
|
|
|
|
object[Symbol.unscopables] = {
|
|
__proto__: {x: true}
|
|
};
|
|
with (object) {
|
|
assertEquals(1, x);
|
|
}
|
|
}
|
|
runTest(TestUnscopableChain);
|
|
|
|
|
|
function TestBasicsSet(object) {
|
|
var x = 1;
|
|
object.x = 2;
|
|
|
|
with (object) {
|
|
assertEquals(2, x);
|
|
}
|
|
|
|
object[Symbol.unscopables] = {x: true};
|
|
with (object) {
|
|
assertEquals(1, x);
|
|
x = 3;
|
|
assertEquals(3, x);
|
|
}
|
|
|
|
assertEquals(3, x);
|
|
assertEquals(2, object.x);
|
|
}
|
|
runTest(TestBasicsSet);
|
|
|
|
|
|
function TestOnProto(object, proto) {
|
|
var x = 1;
|
|
var y = 2;
|
|
var z = 3;
|
|
proto.x = 4;
|
|
|
|
Object.setPrototypeOf(object, proto);
|
|
object.y = 5;
|
|
|
|
with (object) {
|
|
assertEquals(4, x);
|
|
assertEquals(5, y);
|
|
assertEquals(3, z);
|
|
}
|
|
|
|
proto[Symbol.unscopables] = {x: true};
|
|
with (object) {
|
|
assertEquals(1, x);
|
|
assertEquals(5, y);
|
|
assertEquals(3, z);
|
|
}
|
|
|
|
object[Symbol.unscopables] = {y: true};
|
|
with (object) {
|
|
assertEquals(4, x);
|
|
assertEquals(2, y);
|
|
assertEquals(3, z);
|
|
}
|
|
|
|
proto[Symbol.unscopables] = {y: true};
|
|
object[Symbol.unscopables] = {x: true};
|
|
with (object) {
|
|
assertEquals(1, x);
|
|
assertEquals(5, y);
|
|
assertEquals(3, z);
|
|
}
|
|
}
|
|
runTest(TestOnProto);
|
|
|
|
|
|
function TestSetBlockedOnProto(object, proto) {
|
|
var x = 1;
|
|
object.x = 2;
|
|
|
|
with (object) {
|
|
assertEquals(2, x);
|
|
}
|
|
|
|
Object.setPrototypeOf(object, proto);
|
|
proto[Symbol.unscopables] = {x: true};
|
|
with (object) {
|
|
assertEquals(1, x);
|
|
x = 3;
|
|
assertEquals(3, x);
|
|
}
|
|
|
|
assertEquals(3, x);
|
|
assertEquals(2, object.x);
|
|
}
|
|
runTest(TestSetBlockedOnProto);
|
|
|
|
|
|
function TestNonObject(object) {
|
|
var x = 1;
|
|
var y = 2;
|
|
object.x = 3;
|
|
object.y = 4;
|
|
|
|
object[Symbol.unscopables] = 'xy';
|
|
with (object) {
|
|
assertEquals(3, x);
|
|
assertEquals(4, y);
|
|
}
|
|
|
|
object[Symbol.unscopables] = null;
|
|
with (object) {
|
|
assertEquals(3, x);
|
|
assertEquals(4, y);
|
|
}
|
|
}
|
|
runTest(TestNonObject);
|
|
|
|
|
|
function TestChangeDuringWith(object) {
|
|
var x = 1;
|
|
var y = 2;
|
|
object.x = 3;
|
|
object.y = 4;
|
|
|
|
with (object) {
|
|
assertEquals(3, x);
|
|
assertEquals(4, y);
|
|
object[Symbol.unscopables] = {x: true};
|
|
assertEquals(1, x);
|
|
assertEquals(4, y);
|
|
}
|
|
}
|
|
runTest(TestChangeDuringWith);
|
|
|
|
|
|
function TestChangeDuringWithWithPossibleOptimization(object) {
|
|
var x = 1;
|
|
object.x = 2;
|
|
with (object) {
|
|
for (var i = 0; i < 1000; i++) {
|
|
if (i === 500) object[Symbol.unscopables] = {x: true};
|
|
assertEquals(i < 500 ? 2: 1, x);
|
|
}
|
|
}
|
|
}
|
|
TestChangeDuringWithWithPossibleOptimization({});
|
|
|
|
|
|
function TestChangeDuringWithWithPossibleOptimization2(object) {
|
|
var x = 1;
|
|
object.x = 2;
|
|
object[Symbol.unscopables] = {x: true};
|
|
with (object) {
|
|
for (var i = 0; i < 1000; i++) {
|
|
if (i === 500) delete object[Symbol.unscopables];
|
|
assertEquals(i < 500 ? 1 : 2, x);
|
|
}
|
|
}
|
|
}
|
|
TestChangeDuringWithWithPossibleOptimization2({});
|
|
|
|
|
|
function TestChangeDuringWithWithPossibleOptimization3(object) {
|
|
var x = 1;
|
|
object.x = 2;
|
|
object[Symbol.unscopables] = {};
|
|
with (object) {
|
|
for (var i = 0; i < 1000; i++) {
|
|
if (i === 500) object[Symbol.unscopables].x = true;
|
|
assertEquals(i < 500 ? 2 : 1, x);
|
|
}
|
|
}
|
|
}
|
|
TestChangeDuringWithWithPossibleOptimization3({});
|
|
|
|
|
|
function TestChangeDuringWithWithPossibleOptimization4(object) {
|
|
var x = 1;
|
|
object.x = 2;
|
|
object[Symbol.unscopables] = {x: true};
|
|
with (object) {
|
|
for (var i = 0; i < 1000; i++) {
|
|
if (i === 500) delete object[Symbol.unscopables].x;
|
|
assertEquals(i < 500 ? 1 : 2, x);
|
|
}
|
|
}
|
|
}
|
|
TestChangeDuringWithWithPossibleOptimization4({});
|
|
|
|
|
|
function TestAccessorReceiver(object, proto) {
|
|
var x = 'local';
|
|
|
|
Object.defineProperty(proto, 'x', {
|
|
get: function() {
|
|
assertEquals(object, this);
|
|
return this.x_;
|
|
},
|
|
configurable: true
|
|
});
|
|
proto.x_ = 'proto';
|
|
|
|
Object.setPrototypeOf(object, proto);
|
|
proto.x_ = 'object';
|
|
|
|
with (object) {
|
|
assertEquals('object', x);
|
|
}
|
|
}
|
|
runTest(TestAccessorReceiver);
|
|
|
|
|
|
function TestUnscopablesGetter(object) {
|
|
// This test gets really messy when object is the global since the assert
|
|
// functions are properties on the global object and the call count gets
|
|
// completely different.
|
|
if (object === global) return;
|
|
|
|
var x = 'local';
|
|
object.x = 'object';
|
|
|
|
var callCount = 0;
|
|
Object.defineProperty(object, Symbol.unscopables, {
|
|
get: function() {
|
|
callCount++;
|
|
return {};
|
|
},
|
|
configurable: true
|
|
});
|
|
with (object) {
|
|
assertEquals('object', x);
|
|
}
|
|
// Once for HasBinding
|
|
assertEquals(1, callCount);
|
|
|
|
callCount = 0;
|
|
Object.defineProperty(object, Symbol.unscopables, {
|
|
get: function() {
|
|
callCount++;
|
|
return {x: true};
|
|
},
|
|
configurable: true
|
|
});
|
|
with (object) {
|
|
assertEquals('local', x);
|
|
}
|
|
// Once for HasBinding
|
|
assertEquals(1, callCount);
|
|
|
|
callCount = 0;
|
|
Object.defineProperty(object, Symbol.unscopables, {
|
|
get: function() {
|
|
callCount++;
|
|
return callCount == 1 ? {} : {x: true};
|
|
},
|
|
configurable: true
|
|
});
|
|
with (object) {
|
|
x = 1;
|
|
}
|
|
// Once for HasBinding
|
|
assertEquals(1, callCount);
|
|
assertEquals(1, object.x);
|
|
assertEquals('local', x);
|
|
with (object) {
|
|
x = 2;
|
|
}
|
|
// One more HasBinding.
|
|
assertEquals(2, callCount);
|
|
assertEquals(1, object.x);
|
|
assertEquals(2, x);
|
|
}
|
|
runTest(TestUnscopablesGetter);
|
|
|
|
|
|
var global = this;
|
|
function TestUnscopablesGetter2() {
|
|
var x = 'local';
|
|
|
|
var globalProto = Object.getPrototypeOf(global);
|
|
var protos = [{}, [], function() {}, global];
|
|
var objects = [{}, [], function() {}];
|
|
|
|
protos.forEach(function(proto) {
|
|
objects.forEach(function(object) {
|
|
Object.defineProperty(proto, 'x', {
|
|
get: function() {
|
|
assertEquals(object, this);
|
|
return 'proto';
|
|
},
|
|
configurable: true
|
|
});
|
|
|
|
object.__proto__ = proto;
|
|
Object.defineProperty(object, 'x', {
|
|
get: function() {
|
|
assertEquals(object, this);
|
|
return 'object';
|
|
},
|
|
configurable: true
|
|
});
|
|
|
|
with (object) {
|
|
assertEquals('object', x);
|
|
}
|
|
|
|
object[Symbol.unscopables] = {x: true};
|
|
with (object) {
|
|
assertEquals('local', x);
|
|
}
|
|
|
|
delete proto[Symbol.unscopables];
|
|
delete object[Symbol.unscopables];
|
|
});
|
|
});
|
|
|
|
delete global.x;
|
|
Object.setPrototypeOf(global, globalProto);
|
|
}
|
|
TestUnscopablesGetter2();
|
|
|
|
|
|
function TestSetterOnBlacklisted(object, proto) {
|
|
var x = 'local';
|
|
Object.defineProperty(proto, 'x', {
|
|
set: function(x) {
|
|
assertUnreachable();
|
|
},
|
|
get: function() {
|
|
return 'proto';
|
|
},
|
|
configurable: true
|
|
});
|
|
Object.setPrototypeOf(object, proto);
|
|
Object.defineProperty(object, 'x', {
|
|
get: function() {
|
|
return this.x_;
|
|
},
|
|
set: function(x) {
|
|
this.x_ = x;
|
|
},
|
|
configurable: true
|
|
});
|
|
object.x_ = 1;
|
|
|
|
with (object) {
|
|
x = 2;
|
|
assertEquals(2, x);
|
|
}
|
|
|
|
assertEquals(2, object.x);
|
|
|
|
object[Symbol.unscopables] = {x: true};
|
|
|
|
with (object) {
|
|
x = 3;
|
|
assertEquals(3, x);
|
|
}
|
|
|
|
assertEquals(2, object.x);
|
|
}
|
|
runTest(TestSetterOnBlacklisted);
|
|
|
|
|
|
function TestObjectsAsUnscopables(object, unscopables) {
|
|
var x = 1;
|
|
object.x = 2;
|
|
|
|
with (object) {
|
|
assertEquals(2, x);
|
|
object[Symbol.unscopables] = unscopables;
|
|
assertEquals(2, x);
|
|
}
|
|
}
|
|
runTest(TestObjectsAsUnscopables);
|
|
|
|
|
|
function TestAccessorOnUnscopables(object) {
|
|
var x = 1;
|
|
object.x = 2;
|
|
|
|
var unscopables = {
|
|
get x() {
|
|
assertUnreachable();
|
|
}
|
|
};
|
|
|
|
with (object) {
|
|
assertEquals(2, x);
|
|
object[Symbol.unscopables] = unscopables;
|
|
assertEquals(1, x);
|
|
}
|
|
}
|
|
runTest(TestAccessorOnUnscopables);
|
|
|
|
|
|
function TestLengthUnscopables(object, proto) {
|
|
var length = 2;
|
|
with (object) {
|
|
assertEquals(1, length);
|
|
object[Symbol.unscopables] = {length: true};
|
|
assertEquals(2, length);
|
|
delete object[Symbol.unscopables];
|
|
assertEquals(1, length);
|
|
}
|
|
}
|
|
TestLengthUnscopables([1], Array.prototype);
|
|
TestLengthUnscopables(function(x) {}, Function.prototype);
|
|
TestLengthUnscopables(new String('x'), String.prototype);
|
|
|
|
|
|
function TestFunctionNameUnscopables(object) {
|
|
var name = 'local';
|
|
with (object) {
|
|
assertEquals('f', name);
|
|
object[Symbol.unscopables] = {name: true};
|
|
assertEquals('local', name);
|
|
delete object[Symbol.unscopables];
|
|
assertEquals('f', name);
|
|
}
|
|
}
|
|
TestFunctionNameUnscopables(function f() {});
|
|
|
|
|
|
function TestFunctionPrototypeUnscopables() {
|
|
var prototype = 'local';
|
|
var f = function() {};
|
|
var g = function() {};
|
|
Object.setPrototypeOf(f, g);
|
|
var fp = f.prototype;
|
|
var gp = g.prototype;
|
|
with (f) {
|
|
assertEquals(fp, prototype);
|
|
f[Symbol.unscopables] = {prototype: true};
|
|
assertEquals('local', prototype);
|
|
delete f[Symbol.unscopables];
|
|
assertEquals(fp, prototype);
|
|
}
|
|
}
|
|
TestFunctionPrototypeUnscopables(function() {});
|
|
|
|
|
|
function TestFunctionArgumentsUnscopables() {
|
|
var func = function() {
|
|
var arguments = 'local';
|
|
var args = func.arguments;
|
|
with (func) {
|
|
assertEquals(args, arguments);
|
|
func[Symbol.unscopables] = {arguments: true};
|
|
assertEquals('local', arguments);
|
|
delete func[Symbol.unscopables];
|
|
assertEquals(args, arguments);
|
|
}
|
|
}
|
|
func(1);
|
|
}
|
|
TestFunctionArgumentsUnscopables();
|
|
|
|
|
|
function TestArgumentsLengthUnscopables() {
|
|
var func = function() {
|
|
var length = 'local';
|
|
with (arguments) {
|
|
assertEquals(1, length);
|
|
arguments[Symbol.unscopables] = {length: true};
|
|
assertEquals('local', length);
|
|
}
|
|
}
|
|
func(1);
|
|
}
|
|
TestArgumentsLengthUnscopables();
|
|
|
|
|
|
function TestFunctionCallerUnscopables() {
|
|
var func = function() {
|
|
var caller = 'local';
|
|
with (func) {
|
|
assertEquals(TestFunctionCallerUnscopables, caller);
|
|
func[Symbol.unscopables] = {caller: true};
|
|
assertEquals('local', caller);
|
|
delete func[Symbol.unscopables];
|
|
assertEquals(TestFunctionCallerUnscopables, caller);
|
|
}
|
|
}
|
|
func(1);
|
|
}
|
|
TestFunctionCallerUnscopables();
|
|
|
|
|
|
function TestGetUnscopablesGetterThrows() {
|
|
var object = {
|
|
get x() {
|
|
assertUnreachable();
|
|
}
|
|
};
|
|
function CustomError() {}
|
|
Object.defineProperty(object, Symbol.unscopables, {
|
|
get: function() {
|
|
throw new CustomError();
|
|
}
|
|
});
|
|
assertThrows(function() {
|
|
with (object) {
|
|
x;
|
|
}
|
|
}, CustomError);
|
|
}
|
|
TestGetUnscopablesGetterThrows();
|