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.

Reply via email to