class StringObject;
class Symbol;
class SymbolObject;
+class Private;
class Uint32;
class Utils;
class Value;
// 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:
/**
+ * 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)
*/
class V8_EXPORT Number : public Primitive {
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
* array returned by this method contains the same values as would
}
+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) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::ForceDelete()", return false);
}
+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) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::GetPropertyAttribute()",
}
+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) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::Has()", return false);
}
+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) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::DeleteProperty()",
}
+Local<Value> Private::Name() const {
+ return reinterpret_cast<const Symbol*>(this)->Name();
+}
+
+
double Number::Value() const {
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->Number();
}
-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;
}
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() {}
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;
}
// 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);
}
+Handle<Symbol> Factory::NewPrivateSymbol() {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocatePrivateSymbol(),
+ Symbol);
+}
+
+
Handle<Context> Factory::NewNativeContext() {
CALL_HEAP_FUNCTION(
isolate(),
// Create a symbol.
Handle<Symbol> NewSymbol();
+ Handle<Symbol> NewPrivateSymbol();
// Create a global (but otherwise uninitialized) context.
Handle<Context> NewNativeContext();
{ 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);
{ 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.
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() {
Object* result;
{ MaybeObject* maybe_result =
// 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();
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);
// ----------------------------------------------------------------------------
// 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";
}
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 ||
}
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() {
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 &&
CHECK(HasHashCode());
CHECK_GT(Hash(), 0);
CHECK(name()->IsUndefined() || name()->IsString());
+ CHECK(flags()->IsSmi());
}
ACCESSORS(Symbol, name, Object, kNameOffset)
+ACCESSORS(Symbol, flags, Smi, kFlagsOffset)
+BOOL_ACCESSORS(Symbol, flags, is_private, kPrivateBit)
bool String::Equals(String* other) {
PrintF(out, " - hash: %d\n", Hash());
PrintF(out, " - name: ");
name()->ShortPrint();
+ PrintF(out, " - private: %d\n", is_private());
PrintF(out, "\n");
}
V(SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE) \
\
V(SYMBOL_TYPE) \
+ \
V(MAP_TYPE) \
V(CODE_TYPE) \
V(ODDBALL_TYPE) \
| 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,
// [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);
// 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);
};
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)) {
}
+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) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 1);
}
+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) {
SealHandleScope shs(isolate);
ASSERT(args.length() == 2);
}
+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,
Handle<JSReceiver> object,
Handle<Object> key) {
}
// 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));
}
}
// 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.
\
/* Harmony symbols */ \
F(CreateSymbol, 1, 1) \
+ F(CreatePrivateSymbol, 1, 1) \
F(SymbolName, 1, 1) \
+ F(SymbolIsPrivate, 1, 1) \
\
/* Harmony proxies */ \
F(CreateJSProxy, 2, 1) \
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());
}
--- /dev/null
+// 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();
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])))