Revision: 17683
Author:   [email protected]
Date:     Wed Nov 13 10:34:06 2013 UTC
Log:      Provide private symbols through internal APIs

Adds a notion of private symbols, mainly intended for internal use, especially, self-hosting of built-in types that would otherwise require new C++ classes.

On the JS side (i.e., in built-ins), private properties can be created and accessed through a set of macros:

  NEW_PRIVATE(print_name)
  HAS_PRIVATE(obj, sym)
  GET_PRIVATE(obj, sym)
  SET_PRIVATE(obj, sym, val)
  DELETE_PRIVATE(obj, sym)

In the V8 API, they are accessible via a new class Private, and respective HasPrivate/Get/Private/SetPrivate/DeletePrivate methods on calss Object.

These APIs are designed and restricted such that their implementation can later be replaced by whatever ES7+ will officially provide.

[email protected]
BUG=

Review URL: https://codereview.chromium.org/48923002
http://code.google.com/p/v8/source/detail?r=17683

Added:
 /branches/bleeding_edge/test/mjsunit/harmony/private.js
Modified:
 /branches/bleeding_edge/include/v8.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/array-iterator.js
 /branches/bleeding_edge/src/factory.cc
 /branches/bleeding_edge/src/factory.h
 /branches/bleeding_edge/src/heap.cc
 /branches/bleeding_edge/src/heap.h
 /branches/bleeding_edge/src/macros.py
 /branches/bleeding_edge/src/messages.js
 /branches/bleeding_edge/src/objects-debug.cc
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects-printer.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/parser.cc
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/test/cctest/test-api.cc
 /branches/bleeding_edge/test/mjsunit/harmony/symbols.js

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/harmony/private.js Wed Nov 13 10:34:06 2013 UTC
@@ -0,0 +1,324 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --harmony-symbols --harmony-collections
+// Flags: --expose-gc --allow-natives-syntax
+
+var symbols = []
+
+// Test different forms of constructor calls, all equivalent.
+function TestNew() {
+  for (var i = 0; i < 2; ++i) {
+    for (var j = 0; j < 5; ++j) {
+      symbols.push(%CreatePrivateSymbol("66"))
+      symbols.push(Object(%CreatePrivateSymbol("66")).valueOf())
+    }
+    gc()  // Promote existing symbols and then allocate some more.
+  }
+}
+TestNew()
+
+
+function TestType() {
+  for (var i in symbols) {
+    assertEquals("symbol", typeof symbols[i])
+    assertTrue(typeof symbols[i] === "symbol")
+    assertTrue(%SymbolIsPrivate(symbols[i]))
+    assertEquals(null, %_ClassOf(symbols[i]))
+    assertEquals("Symbol", %_ClassOf(new Symbol(symbols[i])))
+    assertEquals("Symbol", %_ClassOf(Object(symbols[i])))
+  }
+}
+TestType()
+
+
+function TestPrototype() {
+  for (var i in symbols) {
+    assertSame(Symbol.prototype, symbols[i].__proto__)
+  }
+}
+TestPrototype()
+
+
+function TestConstructor() {
+  for (var i in symbols) {
+    assertSame(Symbol, symbols[i].__proto__.constructor)
+  }
+}
+TestConstructor()
+
+
+function TestName() {
+  for (var i in symbols) {
+    var name = symbols[i].name
+    assertTrue(name === "66")
+  }
+}
+TestName()
+
+
+function TestToString() {
+  for (var i in symbols) {
+    assertThrows(function() { String(symbols[i]) }, TypeError)
+    assertThrows(function() { symbols[i] + "" }, TypeError)
+    assertThrows(function() { symbols[i].toString() }, TypeError)
+ assertThrows(function() { (new Symbol(symbols[i])).toString() }, TypeError)
+    assertThrows(function() { Object(symbols[i]).toString() }, TypeError)
+ assertEquals("[object Symbol]", Object.prototype.toString.call(symbols[i]))
+  }
+}
+TestToString()
+
+
+function TestToBoolean() {
+  for (var i in symbols) {
+    assertTrue(Boolean(symbols[i]).valueOf())
+    assertFalse(!symbols[i])
+    assertTrue(!!symbols[i])
+    assertTrue(symbols[i] && true)
+    assertFalse(!symbols[i] && false)
+    assertTrue(!symbols[i] || true)
+    assertEquals(1, symbols[i] ? 1 : 2)
+    assertEquals(2, !symbols[i] ? 1 : 2)
+    if (!symbols[i]) assertUnreachable();
+    if (symbols[i]) {} else assertUnreachable();
+  }
+}
+TestToBoolean()
+
+
+function TestToNumber() {
+  for (var i in symbols) {
+    assertSame(NaN, Number(symbols[i]).valueOf())
+    assertSame(NaN, symbols[i] + 0)
+  }
+}
+TestToNumber()
+
+
+function TestEquality() {
+  // Every symbol should equal itself, and non-strictly equal its wrapper.
+  for (var i in symbols) {
+    assertSame(symbols[i], symbols[i])
+    assertEquals(symbols[i], symbols[i])
+    assertTrue(Object.is(symbols[i], symbols[i]))
+    assertTrue(symbols[i] === symbols[i])
+    assertTrue(symbols[i] == symbols[i])
+    assertFalse(symbols[i] === new Symbol(symbols[i]))
+    assertFalse(new Symbol(symbols[i]) === symbols[i])
+    assertTrue(symbols[i] == new Symbol(symbols[i]))
+    assertTrue(new Symbol(symbols[i]) == symbols[i])
+  }
+
+  // All symbols should be distinct.
+  for (var i = 0; i < symbols.length; ++i) {
+    for (var j = i + 1; j < symbols.length; ++j) {
+      assertFalse(Object.is(symbols[i], symbols[j]))
+      assertFalse(symbols[i] === symbols[j])
+      assertFalse(symbols[i] == symbols[j])
+    }
+  }
+
+ // Symbols should not be equal to any other value (and the test terminates). + var values = [347, 1.275, NaN, "string", null, undefined, {}, function() {}]
+  for (var i in symbols) {
+    for (var j in values) {
+      assertFalse(symbols[i] === values[j])
+      assertFalse(values[j] === symbols[i])
+      assertFalse(symbols[i] == values[j])
+      assertFalse(values[j] == symbols[i])
+    }
+  }
+}
+TestEquality()
+
+
+function TestGet() {
+  for (var i in symbols) {
+    assertThrows(function() { symbols[i].toString() }, TypeError)
+    assertEquals(symbols[i], symbols[i].valueOf())
+    assertEquals(undefined, symbols[i].a)
+    assertEquals(undefined, symbols[i]["a" + "b"])
+    assertEquals(undefined, symbols[i]["" + "1"])
+    assertEquals(undefined, symbols[i][62])
+  }
+}
+TestGet()
+
+
+function TestSet() {
+  for (var i in symbols) {
+    symbols[i].toString = 0
+    assertThrows(function() { symbols[i].toString() }, TypeError)
+    symbols[i].valueOf = 0
+    assertEquals(symbols[i], symbols[i].valueOf())
+    symbols[i].a = 0
+    assertEquals(undefined, symbols[i].a)
+    symbols[i]["a" + "b"] = 0
+    assertEquals(undefined, symbols[i]["a" + "b"])
+    symbols[i][62] = 0
+    assertEquals(undefined, symbols[i][62])
+  }
+}
+TestSet()
+
+
+function TestCollections() {
+  var set = new Set
+  var map = new Map
+  var weakmap = new WeakMap
+  for (var i in symbols) {
+    set.add(symbols[i])
+    map.set(symbols[i], i)
+    weakmap.set(symbols[i], i)
+  }
+  assertEquals(symbols.length, set.size)
+  assertEquals(symbols.length, map.size)
+  for (var i in symbols) {
+    assertTrue(set.has(symbols[i]))
+    assertTrue(map.has(symbols[i]))
+    assertTrue(weakmap.has(symbols[i]))
+    assertEquals(i, map.get(symbols[i]))
+    assertEquals(i, weakmap.get(symbols[i]))
+  }
+  for (var i in symbols) {
+    assertTrue(set.delete(symbols[i]))
+    assertTrue(map.delete(symbols[i]))
+    assertTrue(weakmap.delete(symbols[i]))
+  }
+  assertEquals(0, set.size)
+  assertEquals(0, map.size)
+}
+TestCollections()
+
+
+
+function TestKeySet(obj) {
+  assertTrue(%HasFastProperties(obj))
+  // Set the even symbols via assignment.
+  for (var i = 0; i < symbols.length; i += 2) {
+    obj[symbols[i]] = i
+ // Object should remain in fast mode until too many properties were added.
+    assertTrue(%HasFastProperties(obj) || i >= 30)
+  }
+}
+
+
+function TestKeyDefine(obj) {
+  // Set the odd symbols via defineProperty (as non-enumerable).
+  for (var i = 1; i < symbols.length; i += 2) {
+    Object.defineProperty(obj, symbols[i], {value: i, configurable: true})
+  }
+}
+
+
+function TestKeyGet(obj) {
+  var obj2 = Object.create(obj)
+  for (var i in symbols) {
+    assertEquals(i|0, obj[symbols[i]])
+    assertEquals(i|0, obj2[symbols[i]])
+  }
+}
+
+
+function TestKeyHas() {
+  for (var i in symbols) {
+    assertTrue(symbols[i] in obj)
+    assertTrue(Object.hasOwnProperty.call(obj, symbols[i]))
+  }
+}
+
+
+function TestKeyEnum(obj) {
+  for (var name in obj) {
+    assertEquals("string", typeof name)
+  }
+}
+
+
+function TestKeyNames(obj) {
+  assertEquals(0, Object.keys(obj).length)
+
+  var names = Object.getOwnPropertyNames(obj)
+  for (var i in names) {
+    assertEquals("string", typeof names[i])
+  }
+}
+
+
+function TestKeyDescriptor(obj) {
+  for (var i in symbols) {
+    var desc = Object.getOwnPropertyDescriptor(obj, symbols[i]);
+    assertEquals(i|0, desc.value)
+    assertTrue(desc.configurable)
+    assertEquals(i % 2 == 0, desc.writable)
+    assertEquals(i % 2 == 0, desc.enumerable)
+    assertEquals(i % 2 == 0,
+        Object.prototype.propertyIsEnumerable.call(obj, symbols[i]))
+  }
+}
+
+
+function TestKeyDelete(obj) {
+  for (var i in symbols) {
+    delete obj[symbols[i]]
+  }
+  for (var i in symbols) {
+ assertEquals(undefined, Object.getOwnPropertyDescriptor(obj, symbols[i]))
+  }
+}
+
+
+var objs = [{}, [], Object.create(null), Object(1), new Map, function(){}]
+
+for (var i in objs) {
+  var obj = objs[i]
+  TestKeySet(obj)
+  TestKeyDefine(obj)
+  TestKeyGet(obj)
+  TestKeyHas(obj)
+  TestKeyEnum(obj)
+  TestKeyNames(obj)
+  TestKeyDescriptor(obj)
+  TestKeyDelete(obj)
+}
+
+
+function TestCachedKeyAfterScavenge() {
+  gc();
+  // Keyed property lookup are cached.  Hereby we assume that the keys are
+ // tenured, so that we only have to clear the cache between mark compacts,
+  // but not between scavenges.  This must also apply for symbol keys.
+  var key = Symbol("key");
+  var a = {};
+  a[key] = "abc";
+
+  for (var i = 0; i < 1000000; i++) {
+    a[key] += "a";  // Allocations cause a scavenge.
+  }
+}
+TestCachedKeyAfterScavenge();
=======================================
--- /branches/bleeding_edge/include/v8.h        Tue Nov 12 12:09:38 2013 UTC
+++ /branches/bleeding_edge/include/v8.h        Wed Nov 13 10:34:06 2013 UTC
@@ -114,6 +114,7 @@
 class StringObject;
 class Symbol;
 class SymbolObject;
+class Private;
 class Uint32;
 class Utils;
 class Value;
@@ -1926,11 +1927,9 @@
   // Returns the print name string of the symbol, or undefined if none.
   Local<Value> Name() const;

-  // Create a symbol without a print name.
-  static Local<Symbol> New(Isolate* isolate);
-
-  // Create a symbol with a print name.
- static Local<Symbol> New(Isolate *isolate, const char* data, int length = -1);
+  // Create a symbol. If data is not NULL, it will be used as a print name.
+  static Local<Symbol> New(
+      Isolate *isolate, const char* data = NULL, int length = -1);

   V8_INLINE static Symbol* Cast(v8::Value* obj);
  private:
@@ -1939,6 +1938,25 @@
 };


+/**
+ * A private symbol
+ *
+ * This is an experimental feature. Use at your own risk.
+ */
+class V8_EXPORT Private : public Data {
+ public:
+ // Returns the print name string of the private symbol, or undefined if none.
+  Local<Value> Name() const;
+
+ // Create a private symbol. If data is not NULL, it will be the print name.
+  static Local<Private> New(
+      Isolate *isolate, const char* data = NULL, int length = -1);
+
+ private:
+  Private();
+};
+
+
 /**
  * A JavaScript number value (ECMA-262, 4.3.20)
  */
@@ -2108,6 +2126,17 @@
                            PropertyAttribute attribute = None,
                            AccessControl settings = DEFAULT);

+  /**
+   * Functionality for private properties.
+   * This is an experimental feature, use at your own risk.
+ * Note: Private properties are inherited. Do not rely on this, since it may
+   * change.
+   */
+  bool HasPrivate(Handle<Private> key);
+  bool SetPrivate(Handle<Private> key, Handle<Value> value);
+  bool DeletePrivate(Handle<Private> key);
+  Local<Value> GetPrivate(Handle<Private> key);
+
   /**
    * Returns an array containing the names of the enumerable properties
    * of this object, including properties from prototype objects.  The
=======================================
--- /branches/bleeding_edge/src/api.cc  Tue Nov 12 11:44:58 2013 UTC
+++ /branches/bleeding_edge/src/api.cc  Wed Nov 13 10:34:06 2013 UTC
@@ -3189,6 +3189,12 @@
   EXCEPTION_BAILOUT_CHECK(isolate, false);
   return true;
 }
+
+
+bool v8::Object::SetPrivate(v8::Handle<Private> key, v8::Handle<Value> value) { + v8::Handle<Value>* key_as_value = reinterpret_cast<v8::Handle<Value>*>(&key);
+  return Set(*key_as_value, value, DontEnum);
+}


 bool v8::Object::ForceDelete(v8::Handle<Value> key) {
@@ -3240,6 +3246,12 @@
   EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
   return Utils::ToLocal(result);
 }
+
+
+Local<Value> v8::Object::GetPrivate(v8::Handle<Private> key) {
+ v8::Handle<Value>* key_as_value = reinterpret_cast<v8::Handle<Value>*>(&key);
+  return Get(*key_as_value);
+}


PropertyAttribute v8::Object::GetPropertyAttributes(v8::Handle<Value> key) {
@@ -3439,6 +3451,12 @@
   EXCEPTION_BAILOUT_CHECK(isolate, false);
   return obj->IsTrue();
 }
+
+
+bool v8::Object::DeletePrivate(v8::Handle<Private> key) {
+ v8::Handle<Value>* key_as_value = reinterpret_cast<v8::Handle<Value>*>(&key);
+  return Delete(*key_as_value);
+}


 bool v8::Object::Has(v8::Handle<Value> key) {
@@ -3453,6 +3471,12 @@
   EXCEPTION_BAILOUT_CHECK(isolate, false);
   return obj->IsTrue();
 }
+
+
+bool v8::Object::HasPrivate(v8::Handle<Private> key) {
+ v8::Handle<Value>* key_as_value = reinterpret_cast<v8::Handle<Value>*>(&key);
+  return Has(*key_as_value);
+}


 bool v8::Object::Delete(uint32_t index) {
@@ -4874,6 +4898,11 @@
   i::Handle<i::Object> name(sym->name(), sym->GetIsolate());
   return Utils::ToLocal(name);
 }
+
+
+Local<Value> Private::Name() const {
+  return reinterpret_cast<const Symbol*>(this)->Name();
+}


 double Number::Value() const {
@@ -6138,27 +6167,39 @@
 }


-Local<Symbol> v8::Symbol::New(Isolate* isolate) {
+Local<Symbol> v8::Symbol::New(Isolate* isolate, const char* data, int length) {
   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
   EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()");
   LOG_API(i_isolate, "Symbol::New()");
   ENTER_V8(i_isolate);
   i::Handle<i::Symbol> result = i_isolate->factory()->NewSymbol();
+  if (data != NULL) {
+    if (length == -1) length = i::StrLength(data);
+    i::Handle<i::String> name = i_isolate->factory()->NewStringFromUtf8(
+        i::Vector<const char>(data, length));
+    result->set_name(*name);
+  }
   return Utils::ToLocal(result);
 }


-Local<Symbol> v8::Symbol::New(Isolate* isolate, const char* data, int length) {
+Local<Private> v8::Private::New(
+    Isolate* isolate, const char* data, int length) {
   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
-  EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()");
-  LOG_API(i_isolate, "Symbol::New(char)");
+  EnsureInitializedForIsolate(i_isolate, "v8::Private::New()");
+  LOG_API(i_isolate, "Private::New()");
   ENTER_V8(i_isolate);
-  if (length == -1) length = i::StrLength(data);
-  i::Handle<i::String> name = i_isolate->factory()->NewStringFromUtf8(
-      i::Vector<const char>(data, length));
-  i::Handle<i::Symbol> result = i_isolate->factory()->NewSymbol();
-  result->set_name(*name);
-  return Utils::ToLocal(result);
+  i::Handle<i::Symbol> symbol = i_isolate->factory()->NewPrivateSymbol();
+  if (data != NULL) {
+    if (length == -1) length = i::StrLength(data);
+    i::Handle<i::String> name = i_isolate->factory()->NewStringFromUtf8(
+        i::Vector<const char>(data, length));
+    symbol->set_name(*name);
+  }
+  Local<Symbol> result = Utils::ToLocal(symbol);
+  v8::Handle<Private>* result_as_private =
+      reinterpret_cast<v8::Handle<Private>*>(&result);
+  return *result_as_private;
 }


=======================================
--- /branches/bleeding_edge/src/array-iterator.js Thu Oct 17 10:02:45 2013 UTC +++ /branches/bleeding_edge/src/array-iterator.js Wed Nov 13 10:34:06 2013 UTC
@@ -36,9 +36,9 @@
 var ARRAY_ITERATOR_KIND_ENTRIES = 3;
 // The spec draft also has "sparse" but it is never used.

-var iteratorObjectSymbol = %CreateSymbol(UNDEFINED);
-var arrayIteratorNextIndexSymbol = %CreateSymbol(UNDEFINED);
-var arrayIterationKindSymbol = %CreateSymbol(UNDEFINED);
+var iteratorObjectSymbol = NEW_PRIVATE("iterator_object");
+var arrayIteratorNextIndexSymbol = NEW_PRIVATE("iterator_next");
+var arrayIterationKindSymbol = NEW_PRIVATE("iterator_kind");

 function ArrayIterator() {}

@@ -46,9 +46,9 @@
 function CreateArrayIterator(array, kind) {
   var object = ToObject(array);
   var iterator = new ArrayIterator;
-  iterator[iteratorObjectSymbol] = object;
-  iterator[arrayIteratorNextIndexSymbol] = 0;
-  iterator[arrayIterationKindSymbol] = kind;
+  SET_PRIVATE(iterator, iteratorObjectSymbol, object);
+  SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, 0);
+  SET_PRIVATE(iterator, arrayIterationKindSymbol, kind);
   return iterator;
 }

@@ -60,24 +60,24 @@
 // 15.4.5.2.2 ArrayIterator.prototype.next( )
 function ArrayIteratorNext() {
   var iterator = ToObject(this);
-  var array = iterator[iteratorObjectSymbol];
+  var array = GET_PRIVATE(iterator, iteratorObjectSymbol);
   if (!array) {
     throw MakeTypeError('incompatible_method_receiver',
                         ['Array Iterator.prototype.next']);
   }

-  var index = iterator[arrayIteratorNextIndexSymbol];
-  var itemKind = iterator[arrayIterationKindSymbol];
+  var index = GET_PRIVATE(iterator, arrayIteratorNextIndexSymbol);
+  var itemKind = GET_PRIVATE(iterator, arrayIterationKindSymbol);
   var length = TO_UINT32(array.length);

   // "sparse" is never used.

   if (index >= length) {
-    iterator[arrayIteratorNextIndexSymbol] = 1 / 0; // Infinity
+    SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, INFINITY);
     return CreateIteratorResultObject(UNDEFINED, true);
   }

-  iterator[arrayIteratorNextIndexSymbol] = index + 1;
+  SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, index + 1);

   if (itemKind == ARRAY_ITERATOR_KIND_VALUES)
     return CreateIteratorResultObject(array[index], false);
=======================================
--- /branches/bleeding_edge/src/factory.cc      Mon Nov 11 15:28:47 2013 UTC
+++ /branches/bleeding_edge/src/factory.cc      Wed Nov 13 10:34:06 2013 UTC
@@ -363,6 +363,14 @@
       isolate()->heap()->AllocateSymbol(),
       Symbol);
 }
+
+
+Handle<Symbol> Factory::NewPrivateSymbol() {
+  CALL_HEAP_FUNCTION(
+      isolate(),
+      isolate()->heap()->AllocatePrivateSymbol(),
+      Symbol);
+}


 Handle<Context> Factory::NewNativeContext() {
=======================================
--- /branches/bleeding_edge/src/factory.h       Tue Nov  5 17:45:42 2013 UTC
+++ /branches/bleeding_edge/src/factory.h       Wed Nov 13 10:34:06 2013 UTC
@@ -186,6 +186,7 @@

   // Create a symbol.
   Handle<Symbol> NewSymbol();
+  Handle<Symbol> NewPrivateSymbol();

   // Create a global (but otherwise uninitialized) context.
   Handle<Context> NewNativeContext();
=======================================
--- /branches/bleeding_edge/src/heap.cc Mon Nov 11 18:00:52 2013 UTC
+++ /branches/bleeding_edge/src/heap.cc Wed Nov 13 10:34:06 2013 UTC
@@ -3311,11 +3311,13 @@
   { MaybeObject* maybe_obj = AllocateSymbol();
     if (!maybe_obj->ToObject(&obj)) return false;
   }
+  Symbol::cast(obj)->set_is_private(true);
   set_frozen_symbol(Symbol::cast(obj));

   { MaybeObject* maybe_obj = AllocateSymbol();
     if (!maybe_obj->ToObject(&obj)) return false;
   }
+  Symbol::cast(obj)->set_is_private(true);
   set_elements_transition_symbol(Symbol::cast(obj));

{ MaybeObject* maybe_obj = SeededNumberDictionary::Allocate(this, 0, TENURED);
@@ -3327,6 +3329,7 @@
   { MaybeObject* maybe_obj = AllocateSymbol();
     if (!maybe_obj->ToObject(&obj)) return false;
   }
+  Symbol::cast(obj)->set_is_private(true);
   set_observed_symbol(Symbol::cast(obj));

   // Handling of script id generation is in Factory::NewScript.
@@ -5516,10 +5519,20 @@
   Symbol::cast(result)->set_hash_field(
       Name::kIsNotArrayIndexMask | (hash << Name::kHashShift));
   Symbol::cast(result)->set_name(undefined_value());
+  Symbol::cast(result)->set_flags(Smi::FromInt(0));

-  ASSERT(result->IsSymbol());
+  ASSERT(!Symbol::cast(result)->is_private());
   return result;
 }
+
+
+MaybeObject* Heap::AllocatePrivateSymbol() {
+  MaybeObject* maybe = AllocateSymbol();
+  Symbol* symbol;
+  if (!maybe->To(&symbol)) return maybe;
+  symbol->set_is_private(true);
+  return symbol;
+}


 MaybeObject* Heap::AllocateNativeContext() {
=======================================
--- /branches/bleeding_edge/src/heap.h  Fri Nov  8 17:09:14 2013 UTC
+++ /branches/bleeding_edge/src/heap.h  Wed Nov 13 10:34:06 2013 UTC
@@ -881,6 +881,7 @@
   // failed.
   // Please note this does not perform a garbage collection.
   MUST_USE_RESULT MaybeObject* AllocateSymbol();
+  MUST_USE_RESULT MaybeObject* AllocatePrivateSymbol();

   // Allocate a tenured AllocationSite. It's payload is null
   MUST_USE_RESULT MaybeObject* AllocateAllocationSite();
=======================================
--- /branches/bleeding_edge/src/macros.py       Thu Oct 17 10:02:45 2013 UTC
+++ /branches/bleeding_edge/src/macros.py       Wed Nov 13 10:34:06 2013 UTC
@@ -157,6 +157,13 @@
macro TO_OBJECT_INLINE(arg) = (IS_SPEC_OBJECT(%IS_VAR(arg)) ? arg : ToObject(arg)); macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null");

+# Private names.
+macro NEW_PRIVATE(name) = (%CreatePrivateSymbol(name));
+macro HAS_PRIVATE(obj, sym) = (sym in obj);
+macro GET_PRIVATE(obj, sym) = (obj[sym]);
+macro SET_PRIVATE(obj, sym, val) = (obj[sym] = val);
+macro DELETE_PRIVATE(obj, sym) = (delete obj[sym]);
+
 # Constants.  The compiler constant folds them.
 const NAN = $NaN;
 const INFINITY = (1/0);
=======================================
--- /branches/bleeding_edge/src/messages.js     Tue Nov  5 13:04:51 2013 UTC
+++ /branches/bleeding_edge/src/messages.js     Wed Nov 13 10:34:06 2013 UTC
@@ -783,64 +783,67 @@
// ----------------------------------------------------------------------------
 // Error implementation

-var CallSiteReceiverKey = %CreateSymbol("receiver");
-var CallSiteFunctionKey = %CreateSymbol("function");
-var CallSitePositionKey = %CreateSymbol("position");
-var CallSiteStrictModeKey = %CreateSymbol("strict mode");
+//TODO(rossberg)
+var CallSiteReceiverKey = NEW_PRIVATE("receiver");
+var CallSiteFunctionKey = NEW_PRIVATE("function");
+var CallSitePositionKey = NEW_PRIVATE("position");
+var CallSiteStrictModeKey = NEW_PRIVATE("strict mode");

 function CallSite(receiver, fun, pos, strict_mode) {
-  this[CallSiteReceiverKey] = receiver;
-  this[CallSiteFunctionKey] = fun;
-  this[CallSitePositionKey] = pos;
-  this[CallSiteStrictModeKey] = strict_mode;
+  SET_PRIVATE(this, CallSiteReceiverKey, receiver);
+  SET_PRIVATE(this, CallSiteFunctionKey, fun);
+  SET_PRIVATE(this, CallSitePositionKey, pos);
+  SET_PRIVATE(this, CallSiteStrictModeKey, strict_mode);
 }

 function CallSiteGetThis() {
- return this[CallSiteStrictModeKey] ? UNDEFINED : this[CallSiteReceiverKey];
+  return GET_PRIVATE(this, CallSiteStrictModeKey)
+      ? UNDEFINED : GET_PRIVATE(this, CallSiteReceiverKey);
 }

 function CallSiteGetTypeName() {
-  return GetTypeName(this[CallSiteReceiverKey], false);
+  return GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), false);
 }

 function CallSiteIsToplevel() {
-  if (this[CallSiteReceiverKey] == null) {
+  if (GET_PRIVATE(this, CallSiteReceiverKey) == null) {
     return true;
   }
-  return IS_GLOBAL(this[CallSiteReceiverKey]);
+  return IS_GLOBAL(GET_PRIVATE(this, CallSiteReceiverKey));
 }

 function CallSiteIsEval() {
-  var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
   return script && script.compilation_type == COMPILATION_TYPE_EVAL;
 }

 function CallSiteGetEvalOrigin() {
-  var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
   return FormatEvalOrigin(script);
 }

 function CallSiteGetScriptNameOrSourceURL() {
-  var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
   return script ? script.nameOrSourceURL() : null;
 }

 function CallSiteGetFunction() {
- return this[CallSiteStrictModeKey] ? UNDEFINED : this[CallSiteFunctionKey];
+  return GET_PRIVATE(this, CallSiteStrictModeKey)
+      ? UNDEFINED : GET_PRIVATE(this, CallSiteFunctionKey);
 }

 function CallSiteGetFunctionName() {
   // See if the function knows its own name
-  var name = this[CallSiteFunctionKey].name;
+  var name = GET_PRIVATE(this, CallSiteFunctionKey).name;
   if (name) {
     return name;
   }
-  name = %FunctionGetInferredName(this[CallSiteFunctionKey]);
+  name = %FunctionGetInferredName(GET_PRIVATE(this, CallSiteFunctionKey));
   if (name) {
     return name;
   }
   // Maybe this is an evaluation?
-  var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
   if (script && script.compilation_type == COMPILATION_TYPE_EVAL) {
     return "eval";
   }
@@ -850,8 +853,8 @@
 function CallSiteGetMethodName() {
   // See if we can find a unique property on the receiver that holds
   // this function.
-  var receiver = this[CallSiteReceiverKey];
-  var fun = this[CallSiteFunctionKey];
+  var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
+  var fun = GET_PRIVATE(this, CallSiteFunctionKey);
   var ownName = fun.name;
   if (ownName && receiver &&
       (%_CallFunction(receiver, ownName, ObjectLookupGetter) === fun ||
@@ -880,49 +883,51 @@
 }

 function CallSiteGetFileName() {
-  var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
   return script ? script.name : null;
 }

 function CallSiteGetLineNumber() {
-  if (this[CallSitePositionKey] == -1) {
+  if (GET_PRIVATE(this, CallSitePositionKey) == -1) {
     return null;
   }
-  var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
   var location = null;
   if (script) {
- location = script.locationFromPosition(this[CallSitePositionKey], true);
+    location = script.locationFromPosition(
+        GET_PRIVATE(this, CallSitePositionKey), true);
   }
   return location ? location.line + 1 : null;
 }

 function CallSiteGetColumnNumber() {
-  if (this[CallSitePositionKey] == -1) {
+  if (GET_PRIVATE(this, CallSitePositionKey) == -1) {
     return null;
   }
-  var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
   var location = null;
   if (script) {
- location = script.locationFromPosition(this[CallSitePositionKey], true);
+    location = script.locationFromPosition(
+      GET_PRIVATE(this, CallSitePositionKey), true);
   }
   return location ? location.column + 1: null;
 }

 function CallSiteIsNative() {
-  var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
   return script ? (script.type == TYPE_NATIVE) : false;
 }

 function CallSiteGetPosition() {
-  return this[CallSitePositionKey];
+  return GET_PRIVATE(this, CallSitePositionKey);
 }

 function CallSiteIsConstructor() {
-  var receiver = this[CallSiteReceiverKey];
+  var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
   var constructor = (receiver != null && IS_OBJECT(receiver))
                         ? %GetDataProperty(receiver, "constructor") : null;
   if (!constructor) return false;
-  return this[CallSiteFunctionKey] === constructor;
+  return GET_PRIVATE(this, CallSiteFunctionKey) === constructor;
 }

 function CallSiteToString() {
@@ -965,7 +970,7 @@
   var isConstructor = this.isConstructor();
   var isMethodCall = !(this.isToplevel() || isConstructor);
   if (isMethodCall) {
-    var typeName = GetTypeName(this[CallSiteReceiverKey], true);
+ var typeName = GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), true);
     var methodName = this.getMethodName();
     if (functionName) {
       if (typeName &&
=======================================
--- /branches/bleeding_edge/src/objects-debug.cc Fri Nov 8 19:33:05 2013 UTC +++ /branches/bleeding_edge/src/objects-debug.cc Wed Nov 13 10:34:06 2013 UTC
@@ -243,6 +243,7 @@
   CHECK(HasHashCode());
   CHECK_GT(Hash(), 0);
   CHECK(name()->IsUndefined() || name()->IsString());
+  CHECK(flags()->IsSmi());
 }


=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Tue Nov 12 14:43:18 2013 UTC
+++ /branches/bleeding_edge/src/objects-inl.h   Wed Nov 13 10:34:06 2013 UTC
@@ -2692,6 +2692,8 @@


 ACCESSORS(Symbol, name, Object, kNameOffset)
+ACCESSORS(Symbol, flags, Smi, kFlagsOffset)
+BOOL_ACCESSORS(Symbol, flags, is_private, kPrivateBit)


 bool String::Equals(String* other) {
=======================================
--- /branches/bleeding_edge/src/objects-printer.cc Thu Nov 7 16:35:27 2013 UTC +++ /branches/bleeding_edge/src/objects-printer.cc Wed Nov 13 10:34:06 2013 UTC
@@ -523,6 +523,7 @@
   PrintF(out, " - hash: %d\n", Hash());
   PrintF(out, " - name: ");
   name()->ShortPrint();
+  PrintF(out, " - private: %d\n", is_private());
   PrintF(out, "\n");
 }

=======================================
--- /branches/bleeding_edge/src/objects.h       Tue Nov 12 14:43:18 2013 UTC
+++ /branches/bleeding_edge/src/objects.h       Wed Nov 13 10:34:06 2013 UTC
@@ -352,6 +352,7 @@
V(SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE) \ \ V(SYMBOL_TYPE) \ + \ V(MAP_TYPE) \ V(CODE_TYPE) \ V(ODDBALL_TYPE) \
@@ -684,7 +685,7 @@
       | kNotInternalizedTag,

   // Non-string names
-  SYMBOL_TYPE = kNotStringTag,  // LAST_NAME_TYPE, FIRST_NONSTRING_TYPE
+  SYMBOL_TYPE = kNotStringTag,  // FIRST_NONSTRING_TYPE, LAST_NAME_TYPE

   // Objects allocated in their own spaces (never in new space).
   MAP_TYPE,
@@ -8380,6 +8381,11 @@
   // [name]: the print name of a symbol, or undefined if none.
   DECL_ACCESSORS(name, Object)

+  DECL_ACCESSORS(flags, Smi)
+
+  // [is_private]: whether this is a private symbol.
+  DECL_BOOLEAN_ACCESSORS(is_private)
+
   // Casting.
   static inline Symbol* cast(Object* obj);

@@ -8389,12 +8395,14 @@

   // Layout description.
   static const int kNameOffset = Name::kSize;
-  static const int kSize = kNameOffset + kPointerSize;
+  static const int kFlagsOffset = kNameOffset + kPointerSize;
+  static const int kSize = kFlagsOffset + kPointerSize;

- typedef FixedBodyDescriptor<kNameOffset, kNameOffset + kPointerSize, kSize>
-          BodyDescriptor;
+ typedef FixedBodyDescriptor<kNameOffset, kFlagsOffset, kSize> BodyDescriptor;

  private:
+  static const int kPrivateBit = 0;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(Symbol);
 };

=======================================
--- /branches/bleeding_edge/src/parser.cc       Mon Nov 11 08:07:37 2013 UTC
+++ /branches/bleeding_edge/src/parser.cc       Wed Nov 13 10:34:06 2013 UTC
@@ -4114,8 +4114,7 @@
     while (!done) {
       bool is_strict_reserved = false;
       Handle<String> param_name =
-          ParseIdentifierOrStrictReservedWord(&is_strict_reserved,
-                                              CHECK_OK);
+ ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);

       // Store locations for possible future error reports.
       if (!name_loc.IsValid() && IsEvalOrArguments(param_name)) {
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Wed Nov 13 10:07:04 2013 UTC
+++ /branches/bleeding_edge/src/runtime.cc      Wed Nov 13 10:34:06 2013 UTC
@@ -586,6 +586,19 @@
   if (name->IsString()) symbol->set_name(*name);
   return symbol;
 }
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreatePrivateSymbol) {
+  HandleScope scope(isolate);
+  ASSERT(args.length() == 1);
+  Handle<Object> name(args[0], isolate);
+  RUNTIME_ASSERT(name->IsString() || name->IsUndefined());
+  Symbol* symbol;
+  MaybeObject* maybe = isolate->heap()->AllocatePrivateSymbol();
+  if (!maybe->To(&symbol)) return maybe;
+  if (name->IsString()) symbol->set_name(*name);
+  return symbol;
+}


 RUNTIME_FUNCTION(MaybeObject*, Runtime_SymbolName) {
@@ -594,6 +607,14 @@
   CONVERT_ARG_CHECKED(Symbol, symbol, 0);
   return symbol->name();
 }
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SymbolIsPrivate) {
+  SealHandleScope shs(isolate);
+  ASSERT(args.length() == 1);
+  CONVERT_ARG_CHECKED(Symbol, symbol, 0);
+  return isolate->heap()->ToBoolean(symbol->is_private());
+}


 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) {
@@ -4788,6 +4809,19 @@

   return object->GetElement(isolate, index);
 }
+
+
+static Handle<Name> ToName(Isolate* isolate, Handle<Object> key) {
+  if (key->IsName()) {
+    return Handle<Name>::cast(key);
+  } else {
+    bool has_pending_exception = false;
+    Handle<Object> converted =
+        Execution::ToString(isolate, key, &has_pending_exception);
+    if (has_pending_exception) return Handle<Name>();
+    return Handle<Name>::cast(converted);
+  }
+}


 MaybeObject* Runtime::HasObjectProperty(Isolate* isolate,
@@ -4802,16 +4836,8 @@
   }

   // Convert the key to a name - possibly by calling back into JavaScript.
-  Handle<Name> name;
-  if (key->IsName()) {
-    name = Handle<Name>::cast(key);
-  } else {
-    bool has_pending_exception = false;
-    Handle<Object> converted =
-        Execution::ToString(isolate, key, &has_pending_exception);
-    if (has_pending_exception) return Failure::Exception();
-    name = Handle<Name>::cast(converted);
-  }
+  Handle<Name> name = ToName(isolate, key);
+  RETURN_IF_EMPTY_HANDLE(isolate, name);

   return isolate->heap()->ToBoolean(JSReceiver::HasProperty(object, name));
 }
@@ -4844,16 +4870,8 @@
   }

   // Convert the key to a name - possibly by calling back into JavaScript.
-  Handle<Name> name;
-  if (key->IsName()) {
-    name = Handle<Name>::cast(key);
-  } else {
-    bool has_pending_exception = false;
-    Handle<Object> converted =
-        Execution::ToString(isolate, key, &has_pending_exception);
-    if (has_pending_exception) return Failure::Exception();
-    name = Handle<Name>::cast(converted);
-  }
+  Handle<Name> name = ToName(isolate, key);
+  RETURN_IF_EMPTY_HANDLE(isolate, name);

   // Check if the name is trivially convertible to an index and get
   // the element if so.
=======================================
--- /branches/bleeding_edge/src/runtime.h       Tue Nov 12 14:43:18 2013 UTC
+++ /branches/bleeding_edge/src/runtime.h       Wed Nov 13 10:34:06 2013 UTC
@@ -317,7 +317,9 @@
   \
   /* Harmony symbols */ \
   F(CreateSymbol, 1, 1) \
+  F(CreatePrivateSymbol, 1, 1) \
   F(SymbolName, 1, 1) \
+  F(SymbolIsPrivate, 1, 1) \
   \
   /* Harmony proxies */ \
   F(CreateJSProxy, 2, 1) \
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Tue Nov 12 11:44:58 2013 UTC +++ /branches/bleeding_edge/test/cctest/test-api.cc Wed Nov 13 10:34:06 2013 UTC
@@ -2744,6 +2744,70 @@
   CHECK(!obj->Has(sym2));
   CHECK_EQ(2002, obj->Get(sym1)->Int32Value());
   CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+
+  // Symbol properties are inherited.
+  v8::Local<v8::Object> child = v8::Object::New();
+  child->SetPrototype(obj);
+  CHECK(child->Has(sym1));
+  CHECK_EQ(2002, child->Get(sym1)->Int32Value());
+  CHECK_EQ(0, child->GetOwnPropertyNames()->Length());
+}
+
+
+THREADED_TEST(PrivateProperties) {
+  LocalContext env;
+  v8::Isolate* isolate = env->GetIsolate();
+  v8::HandleScope scope(isolate);
+
+  v8::Local<v8::Object> obj = v8::Object::New();
+  v8::Local<v8::Private> priv1 = v8::Private::New(isolate);
+  v8::Local<v8::Private> priv2 = v8::Private::New(isolate, "my-private");
+
+  CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+  CHECK(priv2->Name()->Equals(v8::String::New("my-private")));
+
+  // Make sure delete of a non-existent private symbol property works.
+  CHECK(obj->DeletePrivate(priv1));
+  CHECK(!obj->HasPrivate(priv1));
+
+  CHECK(obj->SetPrivate(priv1, v8::Integer::New(1503)));
+  CHECK(obj->HasPrivate(priv1));
+  CHECK_EQ(1503, obj->GetPrivate(priv1)->Int32Value());
+  CHECK(obj->SetPrivate(priv1, v8::Integer::New(2002)));
+  CHECK(obj->HasPrivate(priv1));
+  CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value());
+
+  CHECK_EQ(0, obj->GetOwnPropertyNames()->Length());
+  int num_props = obj->GetPropertyNames()->Length();
+  CHECK(obj->Set(v8::String::New("bla"), v8::Integer::New(20)));
+  CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+  CHECK_EQ(num_props + 1, obj->GetPropertyNames()->Length());
+
+  CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+  // Add another property and delete it afterwards to force the object in
+  // slow case.
+  CHECK(obj->SetPrivate(priv2, v8::Integer::New(2008)));
+  CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value());
+  CHECK_EQ(2008, obj->GetPrivate(priv2)->Int32Value());
+  CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value());
+  CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+
+  CHECK(obj->HasPrivate(priv1));
+  CHECK(obj->HasPrivate(priv2));
+  CHECK(obj->DeletePrivate(priv2));
+  CHECK(obj->HasPrivate(priv1));
+  CHECK(!obj->HasPrivate(priv2));
+  CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value());
+  CHECK_EQ(1, obj->GetOwnPropertyNames()->Length());
+
+  // Private properties are inherited (for the time being).
+  v8::Local<v8::Object> child = v8::Object::New();
+  child->SetPrototype(obj);
+  CHECK(child->HasPrivate(priv1));
+  CHECK_EQ(2002, child->GetPrivate(priv1)->Int32Value());
+  CHECK_EQ(0, child->GetOwnPropertyNames()->Length());
 }


=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/symbols.js Thu Apr 11 12:27:55 2013 UTC +++ /branches/bleeding_edge/test/mjsunit/harmony/symbols.js Wed Nov 13 10:34:06 2013 UTC
@@ -59,6 +59,7 @@
   for (var i in symbols) {
     assertEquals("symbol", typeof symbols[i])
     assertTrue(typeof symbols[i] === "symbol")
+    assertFalse(%SymbolIsPrivate(symbols[i]))
     assertEquals(null, %_ClassOf(symbols[i]))
     assertEquals("Symbol", %_ClassOf(new Symbol(symbols[i])))
     assertEquals("Symbol", %_ClassOf(Object(symbols[i])))

--
--
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/groups/opt_out.

Reply via email to