Commit 764e1a0f authored by rossberg@chromium.org's avatar rossberg@chromium.org

ES6 symbols: Introduce Symbol class, along with abstract Name class

The new instance type 'Symbol' represents ES6 symbols (a.k.a. private/unique names). Currently, symbols are simple data objects that only carry a hash code, random-generated upon allocation.

The new type 'Name' now serves as the common super class for strings and symbols, and is supposed to represent property names. We will eventually migrate APIs from String to Name for the standard key type.

Strings and symbols share the same hash field representation, via the Name class. This way, we should be able to use the same code paths for symbols and internalized strings in most cases. Also, Symbol's instance type code is allocated adjacent to internalized string codes in the enum, allowing a simple range check for the common case.

Baseline CL: https://codereview.chromium.org/12210083/

R=mstarzinger@chromium.org
BUG=

Review URL: https://codereview.chromium.org/12223071

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13783 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 922efe43
......@@ -4221,7 +4221,7 @@ class Internals {
static const int kNullValueRootIndex = 7;
static const int kTrueValueRootIndex = 8;
static const int kFalseValueRootIndex = 9;
static const int kEmptyStringRootIndex = 118;
static const int kEmptyStringRootIndex = 119;
static const int kNodeClassIdOffset = 1 * kApiPointerSize;
static const int kNodeFlagsOffset = 1 * kApiPointerSize + 3;
......@@ -4231,10 +4231,10 @@ class Internals {
static const int kNodeIsIndependentShift = 4;
static const int kNodeIsPartiallyDependentShift = 5;
static const int kJSObjectType = 0xad;
static const int kJSObjectType = 0xae;
static const int kFirstNonstringType = 0x80;
static const int kOddballType = 0x82;
static const int kForeignType = 0x85;
static const int kOddballType = 0x83;
static const int kForeignType = 0x86;
static const int kUndefinedOddballKind = 5;
static const int kNullOddballKind = 3;
......
......@@ -283,6 +283,14 @@ Handle<String> Factory::NewExternalStringFromTwoByte(
}
Handle<Symbol> Factory::NewSymbol() {
CALL_HEAP_FUNCTION(
isolate(),
isolate()->heap()->AllocateSymbol(),
Symbol);
}
Handle<Context> Factory::NewNativeContext() {
CALL_HEAP_FUNCTION(
isolate(),
......
......@@ -166,6 +166,9 @@ class Factory {
Handle<String> NewExternalStringFromTwoByte(
const ExternalTwoByteString::Resource* resource);
// Create a symbol.
Handle<Symbol> NewSymbol();
// Create a global (but otherwise uninitialized) context.
Handle<Context> NewNativeContext();
......
......@@ -2380,6 +2380,11 @@ bool Heap::CreateInitialMaps() {
}
set_heap_number_map(Map::cast(obj));
{ MaybeObject* maybe_obj = AllocateMap(SYMBOL_TYPE, Symbol::kSize);
if (!maybe_obj->ToObject(&obj)) return false;
}
set_symbol_map(Map::cast(obj));
{ MaybeObject* maybe_obj = AllocateMap(FOREIGN_TYPE, Foreign::kSize);
if (!maybe_obj->ToObject(&obj)) return false;
}
......@@ -5165,6 +5170,34 @@ MaybeObject* Heap::AllocateHashTable(int length, PretenureFlag pretenure) {
}
MaybeObject* Heap::AllocateSymbol(PretenureFlag pretenure) {
// Statically ensure that it is safe to allocate symbols in paged spaces.
STATIC_ASSERT(Symbol::kSize <= Page::kNonCodeObjectAreaSize);
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
Object* result;
MaybeObject* maybe = AllocateRaw(Symbol::kSize, space, OLD_DATA_SPACE);
if (!maybe->ToObject(&result)) return maybe;
HeapObject::cast(result)->set_map_no_write_barrier(symbol_map());
// Generate a random hash value.
int hash;
int attempts = 0;
do {
hash = V8::RandomPrivate(isolate()) & Name::kHashBitMask;
attempts++;
} while (hash == 0 && attempts < 30);
if (hash == 0) hash = 1; // never return 0
Symbol::cast(result)->set_hash_field(
Name::kIsNotArrayIndexMask | (hash << Name::kHashShift));
ASSERT(result->IsSymbol());
return result;
}
MaybeObject* Heap::AllocateNativeContext() {
Object* result;
{ MaybeObject* maybe_result =
......
......@@ -87,6 +87,7 @@ namespace internal {
V(FixedArray, regexp_multiple_cache, RegExpMultipleCache) \
V(Object, termination_exception, TerminationException) \
V(Smi, hash_seed, HashSeed) \
V(Map, symbol_map, SymbolMap) \
V(Map, string_map, StringMap) \
V(Map, ascii_string_map, AsciiStringMap) \
V(Map, cons_string_map, ConsStringMap) \
......@@ -844,6 +845,13 @@ class Heap {
void* external_pointer,
PretenureFlag pretenure);
// Allocate a symbol.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
// Please note this does not perform a garbage collection.
MUST_USE_RESULT MaybeObject* AllocateSymbol(
PretenureFlag pretenure = NOT_TENURED);
// Allocate a tenured JS global property cell.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
......
......@@ -80,6 +80,9 @@ void HeapObject::HeapObjectVerify() {
}
switch (instance_type) {
case SYMBOL_TYPE:
Symbol::cast(this)->SymbolVerify();
break;
case MAP_TYPE:
Map::cast(this)->MapVerify();
break;
......@@ -213,6 +216,13 @@ void HeapObject::VerifyHeapPointer(Object* p) {
}
void Symbol::SymbolVerify() {
CHECK(IsSymbol());
CHECK(HasHashCode());
CHECK_GT(Hash(), 0);
}
void HeapNumber::HeapNumberVerify() {
CHECK(IsHeapNumber());
}
......
......@@ -183,6 +183,13 @@ bool Object::NonFailureIsHeapObject() {
TYPE_CHECKER(HeapNumber, HEAP_NUMBER_TYPE)
TYPE_CHECKER(Symbol, SYMBOL_TYPE)
bool Object::IsName() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() <= LAST_NAME_TYPE;
}
bool Object::IsString() {
......@@ -2416,6 +2423,7 @@ CAST_ACCESSOR(ConsString)
CAST_ACCESSOR(ExternalString)
CAST_ACCESSOR(ExternalAsciiString)
CAST_ACCESSOR(ExternalTwoByteString)
CAST_ACCESSOR(Symbol)
CAST_ACCESSOR(JSReceiver)
CAST_ACCESSOR(JSObject)
CAST_ACCESSOR(Smi)
......@@ -2473,12 +2481,12 @@ SMI_ACCESSORS(FreeSpace, size, kSizeOffset)
SMI_ACCESSORS(String, length, kLengthOffset)
uint32_t String::hash_field() {
uint32_t Name::hash_field() {
return READ_UINT32_FIELD(this, kHashFieldOffset);
}
void String::set_hash_field(uint32_t value) {
void Name::set_hash_field(uint32_t value) {
WRITE_UINT32_FIELD(this, kHashFieldOffset, value);
#if V8_HOST_ARCH_64_BIT
WRITE_UINT32_FIELD(this, kHashFieldOffset + kIntSize, 0);
......@@ -5267,22 +5275,22 @@ SeededNumberDictionary* JSObject::element_dictionary() {
}
bool String::IsHashFieldComputed(uint32_t field) {
bool Name::IsHashFieldComputed(uint32_t field) {
return (field & kHashNotComputedMask) == 0;
}
bool String::HasHashCode() {
bool Name::HasHashCode() {
return IsHashFieldComputed(hash_field());
}
uint32_t String::Hash() {
uint32_t Name::Hash() {
// Fast case: has hash code already been computed?
uint32_t field = hash_field();
if (IsHashFieldComputed(field)) return field >> kHashShift;
// Slow case: compute hash code and set it.
return ComputeAndSetHash();
// Slow case: compute hash code and set it. Has to be a string.
return String::cast(this)->ComputeAndSetHash();
}
......
......@@ -76,6 +76,9 @@ void HeapObject::HeapObjectPrint(FILE* out) {
}
switch (instance_type) {
case SYMBOL_TYPE:
Symbol::cast(this)->SymbolPrint(out);
break;
case MAP_TYPE:
Map::cast(this)->MapPrint(out);
break;
......@@ -477,6 +480,7 @@ static const char* TypeToString(InstanceType type) {
case INVALID_TYPE: return "INVALID";
case MAP_TYPE: return "MAP";
case HEAP_NUMBER_TYPE: return "HEAP_NUMBER";
case SYMBOL_TYPE: return "SYMBOL";
case STRING_TYPE: return "TWO_BYTE_STRING";
case ASCII_STRING_TYPE: return "ASCII_STRING";
case CONS_STRING_TYPE:
......@@ -545,6 +549,12 @@ static const char* TypeToString(InstanceType type) {
}
void Symbol::SymbolPrint(FILE* out) {
HeapObject::PrintHeader(out, "Symbol");
PrintF(out, " - hash: %d\n", Hash());
}
void Map::MapPrint(FILE* out) {
HeapObject::PrintHeader(out, "Map");
PrintF(out, " - type: %s\n", TypeToString(instance_type()));
......
......@@ -128,6 +128,11 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
kVisitDataObjectGeneric,
Foreign::kSize);
case SYMBOL_TYPE:
return GetVisitorIdForSize(kVisitDataObject,
kVisitDataObjectGeneric,
Symbol::kSize);
case FILLER_TYPE:
return kVisitDataObjectGeneric;
......
......@@ -1325,6 +1325,9 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
accumulator->Add("<Odd Oddball>");
break;
}
case SYMBOL_TYPE:
accumulator->Add("<Symbol: %d>", Symbol::cast(this)->Hash());
break;
case HEAP_NUMBER_TYPE:
accumulator->Add("<Number: ");
HeapNumber::cast(this)->HeapNumberPrint(accumulator);
......@@ -1433,6 +1436,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
case JS_GLOBAL_PROPERTY_CELL_TYPE:
JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v);
break;
case SYMBOL_TYPE:
case HEAP_NUMBER_TYPE:
case FILLER_TYPE:
case BYTE_ARRAY_TYPE:
......
This diff is collapsed.
......@@ -94,6 +94,7 @@ SOURCES = {
'test-sockets.cc',
'test-spaces.cc',
'test-strings.cc',
'test-symbols.cc',
'test-strtod.cc',
'test-thread-termination.cc',
'test-threads.cc',
......
......@@ -92,6 +92,7 @@
'test-sockets.cc',
'test-spaces.cc',
'test-strings.cc',
'test-symbols.cc',
'test-strtod.cc',
'test-thread-termination.cc',
'test-threads.cc',
......
// Copyright 2013 the V8 project authors. All rights reserved.
// Check that we can traverse very deep stacks of ConsStrings using
// StringCharacterStram. Check that Get(int) works on very deep stacks
// of ConsStrings. These operations may not be very fast, but they
// should be possible without getting errors due to too deep recursion.
#include "v8.h"
#include "cctest.h"
#include "objects.h"
using namespace v8::internal;
static v8::Persistent<v8::Context> env;
static void InitializeVM() {
if (env.IsEmpty()) {
v8::HandleScope scope;
const char* extensions[] = { "v8/print" };
v8::ExtensionConfiguration config(1, extensions);
env = v8::Context::New(&config);
}
v8::HandleScope scope;
env->Enter();
}
TEST(Create) {
InitializeVM();
Isolate* isolate = Isolate::Current();
HandleScope scope(isolate);
const int kNumSymbols = 30;
Handle<Symbol> symbols[kNumSymbols];
for (int i = 0; i < kNumSymbols; ++i) {
symbols[i] = isolate->factory()->NewSymbol();
CHECK(symbols[i]->IsName());
CHECK(symbols[i]->IsSymbol());
CHECK(symbols[i]->HasHashCode());
CHECK_GT(symbols[i]->Hash(), 0);
symbols[i]->ShortPrint();
PrintF("\n");
#if OBJECT_PRINT
symbols[i]->Print();
#endif
#if VERIFY_HEAP
symbols[i]->Verify();
#endif
}
HEAP->PerformScavenge();
HEAP->CollectAllGarbage(Heap::kNoGCFlags);
// All symbols should be distinct.
for (int i = 0; i < kNumSymbols; ++i) {
CHECK(symbols[i]->SameValue(*symbols[i]));
for (int j = i + 1; j < kNumSymbols; ++j) {
CHECK(!symbols[i]->SameValue(*symbols[j]));
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment