Index: test/mjsunit/harmony/unscopables.js |
diff --git a/test/mjsunit/harmony/unscopables.js b/test/mjsunit/harmony/unscopables.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..678536dba425a952e9c9acdada1a09ea8f111d53 |
--- /dev/null |
+++ b/test/mjsunit/harmony/unscopables.js |
@@ -0,0 +1,664 @@ |
+// 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(); |