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.