Revision: 22942
Author: [email protected]
Date: Wed Aug 6 15:50:40 2014 UTC
Log: This implements unscopables
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
[email protected], [email protected]
Review URL: https://codereview.chromium.org/384963002
Patch from Erik Arvidsson <[email protected]>.
http://code.google.com/p/v8/source/detail?r=22942
Added:
/branches/bleeding_edge/src/unscopables.js
/branches/bleeding_edge/test/cctest/test-unscopables-hidden-prototype.cc
/branches/bleeding_edge/test/mjsunit/harmony/proxies-with-unscopables.js
/branches/bleeding_edge/test/mjsunit/harmony/unscopables.js
Modified:
/branches/bleeding_edge/BUILD.gn
/branches/bleeding_edge/src/bootstrapper.cc
/branches/bleeding_edge/src/contexts.cc
/branches/bleeding_edge/src/contexts.h
/branches/bleeding_edge/src/flag-definitions.h
/branches/bleeding_edge/src/symbol.js
/branches/bleeding_edge/test/cctest/cctest.gyp
/branches/bleeding_edge/tools/generate-runtime-tests.py
/branches/bleeding_edge/tools/gyp/v8.gyp
=======================================
--- /dev/null
+++ /branches/bleeding_edge/src/unscopables.js Wed Aug 6 15:50:40 2014 UTC
@@ -0,0 +1,39 @@
+// 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.
+
+'use strict';
+
+// This file relies on the fact that the following declaration has been
made.
+// var $Array = global.Array;
+// var $Symbol = global.Symbol;
+
+function ExtendSymbol() {
+ %CheckIsBootstrapping();
+ InstallConstants($Symbol, $Array(
+ "unscopables", symbolUnscopables
+ ));
+}
+
+ExtendSymbol();
+
+
+var arrayUnscopables = {
+ __proto__: null,
+ copyWithin: true,
+ entries: true,
+ fill: true,
+ find: true,
+ findIndex: true,
+ keys: true,
+ values: true,
+};
+
+
+function ExtendArrayPrototype() {
+ %CheckIsBootstrapping();
+ %AddNamedProperty($Array.prototype, symbolUnscopables, arrayUnscopables,
+ DONT_ENUM | READ_ONLY);
+}
+
+ExtendArrayPrototype();
=======================================
--- /dev/null
+++
/branches/bleeding_edge/test/cctest/test-unscopables-hidden-prototype.cc
Wed Aug 6 15:50:40 2014 UTC
@@ -0,0 +1,105 @@
+// 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.
+
+#include <stdlib.h>
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+
+namespace {
+
+
+static void Cleanup() {
+ CompileRun(
+ "delete object.x;"
+ "delete hidden_prototype.x;"
+ "delete object[Symbol.unscopables];"
+ "delete hidden_prototype[Symbol.unscopables];");
+}
+
+
+TEST(Unscopables) {
+ i::FLAG_harmony_unscopables = true;
+
+ LocalContext context;
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::HandleScope handle_scope(isolate);
+
+ v8::Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(isolate);
+ v8::Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate);
+
+ t1->SetHiddenPrototype(true);
+
+ v8::Local<v8::Object> object = t0->GetFunction()->NewInstance();
+ v8::Local<v8::Object> hidden_prototype =
t1->GetFunction()->NewInstance();
+
+ object->SetPrototype(hidden_prototype);
+
+ context->Global()->Set(v8_str("object"), object);
+ context->Global()->Set(v8_str("hidden_prototype"), hidden_prototype);
+
+ CHECK_EQ(1, CompileRun(
+ "var result;"
+ "var x = 0;"
+ "object.x = 1;"
+ "with (object) {"
+ " result = x;"
+ "}"
+ "result")->Int32Value());
+
+ Cleanup();
+ CHECK_EQ(2, CompileRun(
+ "var result;"
+ "var x = 0;"
+ "hidden_prototype.x = 2;"
+ "with (object) {"
+ " result = x;"
+ "}"
+ "result")->Int32Value());
+
+ Cleanup();
+ CHECK_EQ(0, CompileRun(
+ "var result;"
+ "var x = 0;"
+ "object.x = 3;"
+ "object[Symbol.unscopables] = {x: true};"
+ "with (object) {"
+ " result = x;"
+ "}"
+ "result")->Int32Value());
+
+ Cleanup();
+ CHECK_EQ(0, CompileRun(
+ "var result;"
+ "var x = 0;"
+ "hidden_prototype.x = 4;"
+ "hidden_prototype[Symbol.unscopables] = {x: true};"
+ "with (object) {"
+ " result = x;"
+ "}"
+ "result")->Int32Value());
+
+ Cleanup();
+ CHECK_EQ(0, CompileRun(
+ "var result;"
+ "var x = 0;"
+ "object.x = 5;"
+ "hidden_prototype[Symbol.unscopables] = {x: true};"
+ "with (object) {"
+ " result = x;"
+ "}"
+ "result;")->Int32Value());
+
+ Cleanup();
+ CHECK_EQ(0, CompileRun(
+ "var result;"
+ "var x = 0;"
+ "hidden_prototype.x = 6;"
+ "object[Symbol.unscopables] = {x: true};"
+ "with (object) {"
+ " result = x;"
+ "}"
+ "result")->Int32Value());
+}
+}
=======================================
--- /dev/null
+++
/branches/bleeding_edge/test/mjsunit/harmony/proxies-with-unscopables.js
Wed Aug 6 15:50:40 2014 UTC
@@ -0,0 +1,153 @@
+// 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-proxies
+
+
+// TODO(arv): Once proxies can intercept symbols, add more tests.
+
+
+function TestBasics() {
+ var log = [];
+
+ var proxy = Proxy.create({
+ getPropertyDescriptor: function(key) {
+ log.push(key);
+ if (key === 'x') {
+ return {
+ value: 1,
+ configurable: true
+ };
+ }
+ return undefined;
+ }
+ });
+
+ var x = 'local';
+
+ with (proxy) {
+ assertEquals(1, x);
+ }
+
+ // One 'x' for HasBinding and one for GetBindingValue
+ assertEquals(['assertEquals', 'x', 'x'], log);
+}
+TestBasics();
+
+
+function TestInconsistent() {
+ var log = [];
+ var calls = 0;
+
+ var proxy = Proxy.create({
+ getPropertyDescriptor: function(key) {
+ log.push(key);
+ if (key === 'x' && calls < 1) {
+ calls++;
+ return {
+ value: 1,
+ configurable: true
+ };
+ }
+ return undefined;
+ }
+ });
+
+ var x = 'local';
+
+ with (proxy) {
+ assertEquals(void 0, x);
+ }
+
+ // One 'x' for HasBinding and one for GetBindingValue
+ assertEquals(['assertEquals', 'x', 'x'], log);
+}
+TestInconsistent();
+
+
+function TestUseProxyAsUnscopables() {
+ var x = 1;
+ var object = {
+ x: 2
+ };
+ var calls = 0;
+ var proxy = Proxy.create({
+ has: function(key) {
+ calls++;
+ assertEquals('x', key);
+ return calls === 2;
+ },
+ getPropertyDescriptor: function(key) {
+ assertUnreachable();
+ }
+ });
+
+ object[Symbol.unscopables] = proxy;
+
+ with (object) {
+ assertEquals(2, x);
+ assertEquals(1, x);
+ }
+
+ // HasBinding, HasBinding
+ assertEquals(2, calls);
+}
+TestUseProxyAsUnscopables();
+
+
+function TestThrowInHasUnscopables() {
+ var x = 1;
+ var object = {
+ x: 2
+ };
+
+ function CustomError() {}
+
+ var calls = 0;
+ var proxy = Proxy.create({
+ has: function(key) {
+ if (calls++ === 0) {
+ throw new CustomError();
+ }
+ assertUnreachable();
+ },
+ getPropertyDescriptor: function(key) {
+ assertUnreachable();
+ }
+ });
+
+ object[Symbol.unscopables] = proxy;
+
+ assertThrows(function() {
+ with (object) {
+ x;
+ }
+ }, CustomError);
+}
+TestThrowInHasUnscopables();
+
+
+var global = this;
+function TestGlobalShouldIgnoreUnscopables() {
+ global.x = 1;
+ var proxy = Proxy.create({
+ getPropertyDescriptor: function() {
+ assertUnreachable();
+ }
+ });
+ global[Symbol.unscopables] = proxy;
+
+ assertEquals(1, global.x);
+ assertEquals(1, x);
+
+ global.x = 2;
+ assertEquals(2, global.x);
+ assertEquals(2, x);
+
+ x = 3;
+ assertEquals(3, global.x);
+ assertEquals(3, x);
+}
+TestGlobalShouldIgnoreUnscopables();
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/harmony/unscopables.js Wed Aug 6
15:50:40 2014 UTC
@@ -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();
=======================================
--- /branches/bleeding_edge/BUILD.gn Wed Aug 6 14:57:04 2014 UTC
+++ /branches/bleeding_edge/BUILD.gn Wed Aug 6 15:50:40 2014 UTC
@@ -253,6 +253,7 @@
"src/array-iterator.js",
"src/harmony-string.js",
"src/harmony-array.js",
+ "src/unscopables.js",
]
outputs = [
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Wed Aug 6 13:45:59 2014 UTC
+++ /branches/bleeding_edge/src/bootstrapper.cc Wed Aug 6 15:50:40 2014 UTC
@@ -1625,6 +1625,10 @@
INSTALL_NATIVE(JSFunction, "DerivedSetTrap", derived_set_trap);
INSTALL_NATIVE(JSFunction, "ProxyEnumerate", proxy_enumerate);
}
+
+ if (FLAG_harmony_unscopables) {
+ INSTALL_NATIVE(Symbol, "symbolUnscopables", unscopables_symbol);
+ }
}
#undef INSTALL_NATIVE
@@ -2063,6 +2067,7 @@
INSTALL_EXPERIMENTAL_NATIVE(i, iteration, "string-iterator.js")
INSTALL_EXPERIMENTAL_NATIVE(i, strings, "harmony-string.js")
INSTALL_EXPERIMENTAL_NATIVE(i, arrays, "harmony-array.js")
+ INSTALL_EXPERIMENTAL_NATIVE(i, unscopables, "unscopables.js")
}
InstallExperimentalNativeFunctions();
=======================================
--- /branches/bleeding_edge/src/contexts.cc Mon Aug 4 11:34:54 2014 UTC
+++ /branches/bleeding_edge/src/contexts.cc Wed Aug 6 15:50:40 2014 UTC
@@ -69,6 +69,38 @@
void Context::set_global_proxy(JSObject* object) {
native_context()->set_global_proxy_object(object);
}
+
+
+/**
+ * Lookups a property in an object environment, taking the unscopables into
+ * account. This is used For HasBinding spec algorithms for
ObjectEnvironment.
+ */
+static Maybe<PropertyAttributes> UnscopableLookup(LookupIterator* it) {
+ Isolate* isolate = it->isolate();
+
+ Maybe<PropertyAttributes> attrs = JSReceiver::GetPropertyAttributes(it);
+ DCHECK(attrs.has_value || isolate->has_pending_exception());
+ if (!attrs.has_value || attrs.value == ABSENT) return attrs;
+
+ Handle<Symbol> unscopables_symbol(
+ isolate->native_context()->unscopables_symbol(), isolate);
+ Handle<Object> receiver = it->GetReceiver();
+ Handle<Object> unscopables;
+ MaybeHandle<Object> maybe_unscopables =
+ Object::GetProperty(receiver, unscopables_symbol);
+ if (!maybe_unscopables.ToHandle(&unscopables)) {
+ return Maybe<PropertyAttributes>();
+ }
+ if (!unscopables->IsSpecObject()) return attrs;
+ Maybe<bool> blacklist = JSReceiver::HasProperty(
+ Handle<JSReceiver>::cast(unscopables), it->name());
+ if (!blacklist.has_value) {
+ DCHECK(isolate->has_pending_exception());
+ return Maybe<PropertyAttributes>();
+ }
+ if (blacklist.value) return maybe(ABSENT);
+ return attrs;
+}
Handle<Object> Context::Lookup(Handle<String> name,
@@ -110,9 +142,13 @@
if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
object->IsJSContextExtensionObject()) {
maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
+ } else if (FLAG_harmony_unscopables && context->IsWithContext()) {
+ LookupIterator it(object, name);
+ maybe = UnscopableLookup(&it);
} else {
maybe = JSReceiver::GetPropertyAttributes(object, name);
}
+
if (!maybe.has_value) return Handle<Object>();
DCHECK(!isolate->has_pending_exception());
*attributes = maybe.value;
=======================================
--- /branches/bleeding_edge/src/contexts.h Tue Aug 5 08:18:22 2014 UTC
+++ /branches/bleeding_edge/src/contexts.h Wed Aug 6 15:50:40 2014 UTC
@@ -200,7 +200,8 @@
V(ITERATOR_RESULT_MAP_INDEX, Map,
iterator_result_map) \
V(MAP_ITERATOR_MAP_INDEX, Map,
map_iterator_map) \
V(SET_ITERATOR_MAP_INDEX, Map,
set_iterator_map) \
- V(ITERATOR_SYMBOL_INDEX, Symbol, iterator_symbol)
+ V(ITERATOR_SYMBOL_INDEX, Symbol,
iterator_symbol) \
+ V(UNSCOPABLES_SYMBOL_INDEX, Symbol, unscopables_symbol)
// JSFunctions are pairs (context, function code), sometimes also called
// closures. A Context object is used to represent function contexts and
@@ -394,6 +395,7 @@
MAP_ITERATOR_MAP_INDEX,
SET_ITERATOR_MAP_INDEX,
ITERATOR_SYMBOL_INDEX,
+ UNSCOPABLES_SYMBOL_INDEX,
// Properties from here are treated as weak references by the full GC.
// Scavenge treats them as strong references.
=======================================
--- /branches/bleeding_edge/src/flag-definitions.h Tue Aug 5 19:37:32 2014
UTC
+++ /branches/bleeding_edge/src/flag-definitions.h Wed Aug 6 15:50:40 2014
UTC
@@ -161,6 +161,7 @@
DEFINE_BOOL(harmony_strings, false, "enable harmony string")
DEFINE_BOOL(harmony_arrays, false, "enable harmony arrays")
DEFINE_BOOL(harmony_arrow_functions, false, "enable harmony arrow
functions")
+DEFINE_BOOL(harmony_unscopables, false, "enable harmony unscopables")
DEFINE_BOOL(harmony, false, "enable all harmony features")
DEFINE_IMPLICATION(harmony, harmony_scoping)
=======================================
--- /branches/bleeding_edge/src/symbol.js Wed Aug 6 09:31:21 2014 UTC
+++ /branches/bleeding_edge/src/symbol.js Wed Aug 6 15:50:40 2014 UTC
@@ -105,7 +105,7 @@
// "isRegExp", symbolIsRegExp,
"iterator", symbolIterator
// "toStringTag", symbolToStringTag,
- // "unscopables", symbolUnscopables
+ // "unscopables", symbolUnscopables // added in unscopables.js
));
InstallFunctions($Symbol, DONT_ENUM, $Array(
"for", SymbolFor,
=======================================
--- /branches/bleeding_edge/test/cctest/cctest.gyp Wed Aug 6 09:35:21 2014
UTC
+++ /branches/bleeding_edge/test/cctest/cctest.gyp Wed Aug 6 15:50:40 2014
UTC
@@ -156,6 +156,7 @@
'test-types.cc',
'test-unbound-queue.cc',
'test-unique.cc',
+ 'test-unscopables-hidden-prototype.cc',
'test-utils.cc',
'test-version.cc',
'test-weakmaps.cc',
=======================================
--- /branches/bleeding_edge/tools/generate-runtime-tests.py Wed Aug 6
11:39:39 2014 UTC
+++ /branches/bleeding_edge/tools/generate-runtime-tests.py Wed Aug 6
15:50:40 2014 UTC
@@ -51,7 +51,7 @@
EXPECTED_FUZZABLE_COUNT = 330
EXPECTED_CCTEST_COUNT = 7
EXPECTED_UNKNOWN_COUNT = 16
-EXPECTED_BUILTINS_COUNT = 809
+EXPECTED_BUILTINS_COUNT = 811
# Don't call these at all.
=======================================
--- /branches/bleeding_edge/tools/gyp/v8.gyp Wed Aug 6 11:39:39 2014 UTC
+++ /branches/bleeding_edge/tools/gyp/v8.gyp Wed Aug 6 15:50:40 2014 UTC
@@ -1417,6 +1417,7 @@
'../../src/string-iterator.js',
'../../src/harmony-string.js',
'../../src/harmony-array.js',
+ '../../src/unscopables.js',
],
'libraries_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries.bin',
'libraries_experimental_bin_file':
'<(SHARED_INTERMEDIATE_DIR)/libraries-experimental.bin',
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.