From 764e1a0fcf7dd10616e86b09cbbb87d8e503393f Mon Sep 17 00:00:00 2001 From: "rossberg@chromium.org" Date: Fri, 1 Mar 2013 10:34:31 +0000 Subject: [PATCH] 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 --- include/v8.h | 8 +- src/factory.cc | 8 ++ src/factory.h | 3 + src/heap.cc | 33 +++++++ src/heap.h | 8 ++ src/objects-debug.cc | 10 +++ src/objects-inl.h | 22 +++-- src/objects-printer.cc | 10 +++ src/objects-visiting.cc | 5 ++ src/objects.cc | 4 + src/objects.h | 208 +++++++++++++++++++++++++++----------------- test/cctest/SConscript | 1 + test/cctest/cctest.gyp | 1 + test/cctest/test-symbols.cc | 63 ++++++++++++++ 14 files changed, 295 insertions(+), 89 deletions(-) create mode 100644 test/cctest/test-symbols.cc diff --git a/include/v8.h b/include/v8.h index 7fdcb05..1426f57 100644 --- a/include/v8.h +++ b/include/v8.h @@ -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; diff --git a/src/factory.cc b/src/factory.cc index bd3dc1f..bb0e85f 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -283,6 +283,14 @@ Handle Factory::NewExternalStringFromTwoByte( } +Handle Factory::NewSymbol() { + CALL_HEAP_FUNCTION( + isolate(), + isolate()->heap()->AllocateSymbol(), + Symbol); +} + + Handle Factory::NewNativeContext() { CALL_HEAP_FUNCTION( isolate(), diff --git a/src/factory.h b/src/factory.h index eecf6be..3651d36 100644 --- a/src/factory.h +++ b/src/factory.h @@ -166,6 +166,9 @@ class Factory { Handle NewExternalStringFromTwoByte( const ExternalTwoByteString::Resource* resource); + // Create a symbol. + Handle NewSymbol(); + // Create a global (but otherwise uninitialized) context. Handle NewNativeContext(); diff --git a/src/heap.cc b/src/heap.cc index 660cdc7..e1260ae 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -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 = diff --git a/src/heap.h b/src/heap.h index 7e5a4e8..05f8b63 100644 --- a/src/heap.h +++ b/src/heap.h @@ -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. diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 0762f79..e583016 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -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()); } diff --git a/src/objects-inl.h b/src/objects-inl.h index cc46932..c566dd9 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -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(); } diff --git a/src/objects-printer.cc b/src/objects-printer.cc index ad6ab91..9c3a35d 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -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())); diff --git a/src/objects-visiting.cc b/src/objects-visiting.cc index 7082e59..088f5eb 100644 --- a/src/objects-visiting.cc +++ b/src/objects-visiting.cc @@ -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; diff --git a/src/objects.cc b/src/objects.cc index 618734f..7d3ae7a 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1325,6 +1325,9 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { accumulator->Add(""); break; } + case SYMBOL_TYPE: + accumulator->Add("", Symbol::cast(this)->Hash()); + break; case HEAP_NUMBER_TYPE: accumulator->Add("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: diff --git a/src/objects.h b/src/objects.h index b7a32a9..7e53c68 100644 --- a/src/objects.h +++ b/src/objects.h @@ -95,15 +95,25 @@ // - ExternalIntArray // - ExternalUnsignedIntArray // - ExternalFloatArray -// - String -// - SeqString -// - SeqOneByteString -// - SeqTwoByteString -// - SlicedString -// - ConsString -// - ExternalString -// - ExternalAsciiString -// - ExternalTwoByteString +// - Name +// - String +// - SeqString +// - SeqOneByteString +// - SeqTwoByteString +// - SlicedString +// - ConsString +// - ExternalString +// - ExternalAsciiString +// - ExternalTwoByteString +// - InternalizedString +// - SeqInternalizedString +// - SeqOneByteInternalizedString +// - SeqTwoByteInternalizedString +// - ConsInternalizedString +// - ExternalInternalizedString +// - ExternalAsciiInternalizedString +// - ExternalTwoByteInternalizedString +// - Symbol // - HeapNumber // - Code // - Map @@ -269,6 +279,7 @@ const int kStubMinorKeyBits = kBitsPerInt - kSmiTagSize - kStubMajorKeyBits; V(SHORT_EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE) \ V(SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ASCII_DATA_TYPE) \ \ + V(SYMBOL_TYPE) \ V(MAP_TYPE) \ V(CODE_TYPE) \ V(ODDBALL_TYPE) \ @@ -580,8 +591,11 @@ enum InstanceType { SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ASCII_DATA_TYPE = SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE | kInternalizedTag, + // Non-string names + SYMBOL_TYPE = kNotStringTag, // LAST_NAME_TYPE, FIRST_NONSTRING_TYPE + // Objects allocated in their own spaces (never in new space). - MAP_TYPE = kNotStringTag, // FIRST_NONSTRING_TYPE + MAP_TYPE, CODE_TYPE, ODDBALL_TYPE, JS_GLOBAL_PROPERTY_CELL_TYPE, @@ -663,7 +677,8 @@ enum InstanceType { FIRST_TYPE = 0x0, LAST_TYPE = JS_FUNCTION_TYPE, INVALID_TYPE = FIRST_TYPE - 1, - FIRST_NONSTRING_TYPE = MAP_TYPE, + LAST_NAME_TYPE = SYMBOL_TYPE, + FIRST_NONSTRING_TYPE = SYMBOL_TYPE, // Boundaries for testing for an external array. FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE, LAST_EXTERNAL_ARRAY_TYPE = EXTERNAL_PIXEL_ARRAY_TYPE, @@ -839,6 +854,7 @@ class MaybeObject BASE_EMBEDDED { #define HEAP_OBJECT_TYPE_LIST(V) \ V(HeapNumber) \ + V(Name) \ V(String) \ V(SeqString) \ V(ExternalString) \ @@ -849,6 +865,7 @@ class MaybeObject BASE_EMBEDDED { V(SeqTwoByteString) \ V(SeqOneByteString) \ V(InternalizedString) \ + V(Symbol) \ \ V(ExternalArray) \ V(ExternalByteArray) \ @@ -7199,6 +7216,102 @@ class StringShape BASE_EMBEDDED { }; +// The Name abstract class captures anything that can be used as a property +// name, i.e., strings and symbols. All names store a hash value. +class Name: public HeapObject { + public: + // Get and set the hash field of the name. + inline uint32_t hash_field(); + inline void set_hash_field(uint32_t value); + + // Tells whether the hash code has been computed. + inline bool HasHashCode(); + + // Returns a hash value used for the property table + inline uint32_t Hash(); + + // Casting. + static inline Name* cast(Object* obj); + + // Layout description. + static const int kHashFieldOffset = HeapObject::kHeaderSize; + static const int kSize = kHashFieldOffset + kPointerSize; + + // Mask constant for checking if a name has a computed hash code + // and if it is a string that is an array index. The least significant bit + // indicates whether a hash code has been computed. If the hash code has + // been computed the 2nd bit tells whether the string can be used as an + // array index. + static const int kHashNotComputedMask = 1; + static const int kIsNotArrayIndexMask = 1 << 1; + static const int kNofHashBitFields = 2; + + // Shift constant retrieving hash code from hash field. + static const int kHashShift = kNofHashBitFields; + + // Only these bits are relevant in the hash, since the top two are shifted + // out. + static const uint32_t kHashBitMask = 0xffffffffu >> kHashShift; + + // Array index strings this short can keep their index in the hash field. + static const int kMaxCachedArrayIndexLength = 7; + + // For strings which are array indexes the hash value has the string length + // mixed into the hash, mainly to avoid a hash value of zero which would be + // the case for the string '0'. 24 bits are used for the array index value. + static const int kArrayIndexValueBits = 24; + static const int kArrayIndexLengthBits = + kBitsPerInt - kArrayIndexValueBits - kNofHashBitFields; + + STATIC_CHECK((kArrayIndexLengthBits > 0)); + + static const int kArrayIndexHashLengthShift = + kArrayIndexValueBits + kNofHashBitFields; + + static const int kArrayIndexHashMask = (1 << kArrayIndexHashLengthShift) - 1; + + static const int kArrayIndexValueMask = + ((1 << kArrayIndexValueBits) - 1) << kHashShift; + + // Check that kMaxCachedArrayIndexLength + 1 is a power of two so we + // could use a mask to test if the length of string is less than or equal to + // kMaxCachedArrayIndexLength. + STATIC_CHECK(IS_POWER_OF_TWO(kMaxCachedArrayIndexLength + 1)); + + static const int kContainsCachedArrayIndexMask = + (~kMaxCachedArrayIndexLength << kArrayIndexHashLengthShift) | + kIsNotArrayIndexMask; + + // Value of empty hash field indicating that the hash is not computed. + static const int kEmptyHashField = + kIsNotArrayIndexMask | kHashNotComputedMask; + + protected: + static inline bool IsHashFieldComputed(uint32_t field); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Name); +}; + + +// ES6 symbols. +class Symbol: public Name { + public: + // Casting. + static inline Symbol* cast(Object* obj); + + // Dispatched behavior. + DECLARE_PRINTER(Symbol) + DECLARE_VERIFIER(Symbol) + + // Layout description. + static const int kSize = Name::kSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Symbol); +}; + + // The String abstract class captures JavaScript string values: // // Ecma-262: @@ -7207,7 +7320,7 @@ class StringShape BASE_EMBEDDED { // ordered sequence of zero or more 16-bit unsigned integer values. // // All string values have a length field. -class String: public HeapObject { +class String: public Name { public: enum Encoding { ONE_BYTE_ENCODING, TWO_BYTE_ENCODING }; @@ -7260,10 +7373,6 @@ class String: public HeapObject { inline int length(); inline void set_length(int value); - // Get and set the hash field of the string. - inline uint32_t hash_field(); - inline void set_hash_field(uint32_t value); - // Returns whether this string has only ASCII chars, i.e. all of them can // be ASCII encoded. This might be the case even if the string is // two-byte. Such strings may appear when the embedder prefers @@ -7364,12 +7473,6 @@ class String: public HeapObject { SmartArrayPointer ToWideCString( RobustnessFlag robustness_flag = FAST_STRING_TRAVERSAL); - // Tells whether the hash code has been computed. - inline bool HasHashCode(); - - // Returns a hash value used for the property table - inline uint32_t Hash(); - bool ComputeArrayIndex(uint32_t* index); // Externalization. @@ -7402,70 +7505,19 @@ class String: public HeapObject { inline bool IsFlat(); // Layout description. - static const int kLengthOffset = HeapObject::kHeaderSize; - static const int kHashFieldOffset = kLengthOffset + kPointerSize; - static const int kSize = kHashFieldOffset + kPointerSize; + static const int kLengthOffset = Name::kSize; + static const int kSize = kLengthOffset + kPointerSize; // Maximum number of characters to consider when trying to convert a string // value into an array index. static const int kMaxArrayIndexSize = 10; + STATIC_CHECK(kMaxArrayIndexSize < (1 << kArrayIndexLengthBits)); // Max char codes. static const int32_t kMaxOneByteCharCode = unibrow::Latin1::kMaxChar; static const uint32_t kMaxOneByteCharCodeU = unibrow::Latin1::kMaxChar; static const int kMaxUtf16CodeUnit = 0xffff; - // Mask constant for checking if a string has a computed hash code - // and if it is an array index. The least significant bit indicates - // whether a hash code has been computed. If the hash code has been - // computed the 2nd bit tells whether the string can be used as an - // array index. - static const int kHashNotComputedMask = 1; - static const int kIsNotArrayIndexMask = 1 << 1; - static const int kNofHashBitFields = 2; - - // Shift constant retrieving hash code from hash field. - static const int kHashShift = kNofHashBitFields; - - // Only these bits are relevant in the hash, since the top two are shifted - // out. - static const uint32_t kHashBitMask = 0xffffffffu >> kHashShift; - - // Array index strings this short can keep their index in the hash - // field. - static const int kMaxCachedArrayIndexLength = 7; - - // For strings which are array indexes the hash value has the string length - // mixed into the hash, mainly to avoid a hash value of zero which would be - // the case for the string '0'. 24 bits are used for the array index value. - static const int kArrayIndexValueBits = 24; - static const int kArrayIndexLengthBits = - kBitsPerInt - kArrayIndexValueBits - kNofHashBitFields; - - STATIC_CHECK((kArrayIndexLengthBits > 0)); - STATIC_CHECK(kMaxArrayIndexSize < (1 << kArrayIndexLengthBits)); - - static const int kArrayIndexHashLengthShift = - kArrayIndexValueBits + kNofHashBitFields; - - static const int kArrayIndexHashMask = (1 << kArrayIndexHashLengthShift) - 1; - - static const int kArrayIndexValueMask = - ((1 << kArrayIndexValueBits) - 1) << kHashShift; - - // Check that kMaxCachedArrayIndexLength + 1 is a power of two so we - // could use a mask to test if the length of string is less than or equal to - // kMaxCachedArrayIndexLength. - STATIC_CHECK(IS_POWER_OF_TWO(kMaxCachedArrayIndexLength + 1)); - - static const int kContainsCachedArrayIndexMask = - (~kMaxCachedArrayIndexLength << kArrayIndexHashLengthShift) | - kIsNotArrayIndexMask; - - // Value of empty hash field indicating that the hash is not computed. - static const int kEmptyHashField = - kIsNotArrayIndexMask | kHashNotComputedMask; - // Value of hash field containing computed hash equal to zero. static const int kEmptyStringHash = kIsNotArrayIndexMask; @@ -7547,13 +7599,13 @@ class String: public HeapObject { unsigned length); private: + friend class Name; + // Try to flatten the top level ConsString that is hiding behind this // string. This is a no-op unless the string is a ConsString. Flatten // mutates the ConsString and might return a failure. MUST_USE_RESULT MaybeObject* SlowTryFlatten(PretenureFlag pretenure); - static inline bool IsHashFieldComputed(uint32_t field); - // Slow case of String::Equals. This implementation works on any strings // but it is most efficient on strings that are almost flat. bool SlowEquals(String* other); diff --git a/test/cctest/SConscript b/test/cctest/SConscript index bcd1e98..8ed3f52 100644 --- a/test/cctest/SConscript +++ b/test/cctest/SConscript @@ -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', diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index a8754fc..eb0d907 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -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', diff --git a/test/cctest/test-symbols.cc b/test/cctest/test-symbols.cc new file mode 100644 index 0000000..adc100d --- /dev/null +++ b/test/cctest/test-symbols.cc @@ -0,0 +1,63 @@ +// 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 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 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])); + } + } +} -- 2.7.4