| Index: src/heap.cc
|
| diff --git a/src/heap.cc b/src/heap.cc
|
| index 2e4ef44ecdcd8fe1dd7b467043e497ee1ca99b6c..30932093be4763c09ebbe8b1498192ab8cbd5cee 100644
|
| --- a/src/heap.cc
|
| +++ b/src/heap.cc
|
| @@ -3774,6 +3774,262 @@ MaybeObject* Heap::AllocateJSMessageObject(String* type,
|
| }
|
|
|
|
|
| +
|
| +// Returns true for a character in a range. Both limits are inclusive.
|
| +static inline bool Between(uint32_t character, uint32_t from, uint32_t to) {
|
| + // This makes uses of the the unsigned wraparound.
|
| + return character - from <= to - from;
|
| +}
|
| +
|
| +
|
| +MUST_USE_RESULT static inline MaybeObject* MakeOrFindTwoCharacterString(
|
| + Heap* heap,
|
| + uint16_t c1,
|
| + uint16_t c2) {
|
| + String* result;
|
| + // Numeric strings have a different hash algorithm not known by
|
| + // LookupTwoCharsStringIfExists, so we skip this step for such strings.
|
| + if ((!Between(c1, '0', '9') || !Between(c2, '0', '9')) &&
|
| + heap->string_table()->LookupTwoCharsStringIfExists(c1, c2, &result)) {
|
| + return result;
|
| + // Now we know the length is 2, we might as well make use of that fact
|
| + // when building the new string.
|
| + } else if (static_cast<unsigned>(c1 | c2) <= String::kMaxOneByteCharCodeU) {
|
| + // We can do this.
|
| + ASSERT(IsPowerOf2(String::kMaxOneByteCharCodeU + 1)); // because of this.
|
| + Object* result;
|
| + { MaybeObject* maybe_result = heap->AllocateRawOneByteString(2);
|
| + if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + }
|
| + uint8_t* dest = SeqOneByteString::cast(result)->GetChars();
|
| + dest[0] = static_cast<uint8_t>(c1);
|
| + dest[1] = static_cast<uint8_t>(c2);
|
| + return result;
|
| + } else {
|
| + Object* result;
|
| + { MaybeObject* maybe_result = heap->AllocateRawTwoByteString(2);
|
| + if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + }
|
| + uc16* dest = SeqTwoByteString::cast(result)->GetChars();
|
| + dest[0] = c1;
|
| + dest[1] = c2;
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +
|
| +MaybeObject* Heap::AllocateConsString(String* first, String* second) {
|
| + int first_length = first->length();
|
| + if (first_length == 0) {
|
| + return second;
|
| + }
|
| +
|
| + int second_length = second->length();
|
| + if (second_length == 0) {
|
| + return first;
|
| + }
|
| +
|
| + int length = first_length + second_length;
|
| +
|
| + // Optimization for 2-byte strings often used as keys in a decompression
|
| + // dictionary. Check whether we already have the string in the string
|
| + // table to prevent creation of many unneccesary strings.
|
| + if (length == 2) {
|
| + uint16_t c1 = first->Get(0);
|
| + uint16_t c2 = second->Get(0);
|
| + return MakeOrFindTwoCharacterString(this, c1, c2);
|
| + }
|
| +
|
| + bool first_is_one_byte = first->IsOneByteRepresentation();
|
| + bool second_is_one_byte = second->IsOneByteRepresentation();
|
| + bool is_one_byte = first_is_one_byte && second_is_one_byte;
|
| + // Make sure that an out of memory exception is thrown if the length
|
| + // of the new cons string is too large.
|
| + if (length > String::kMaxLength || length < 0) {
|
| + isolate()->context()->mark_out_of_memory();
|
| + return Failure::OutOfMemoryException(0x4);
|
| + }
|
| +
|
| + bool is_one_byte_data_in_two_byte_string = false;
|
| + if (!is_one_byte) {
|
| + // At least one of the strings uses two-byte representation so we
|
| + // can't use the fast case code for short ASCII strings below, but
|
| + // we can try to save memory if all chars actually fit in ASCII.
|
| + is_one_byte_data_in_two_byte_string =
|
| + first->HasOnlyOneByteChars() && second->HasOnlyOneByteChars();
|
| + if (is_one_byte_data_in_two_byte_string) {
|
| + isolate_->counters()->string_add_runtime_ext_to_ascii()->Increment();
|
| + }
|
| + }
|
| +
|
| + // If the resulting string is small make a flat string.
|
| + if (length < ConsString::kMinLength) {
|
| + // Note that neither of the two inputs can be a slice because:
|
| + STATIC_ASSERT(ConsString::kMinLength <= SlicedString::kMinLength);
|
| + ASSERT(first->IsFlat());
|
| + ASSERT(second->IsFlat());
|
| + if (is_one_byte) {
|
| + Object* result;
|
| + { MaybeObject* maybe_result = AllocateRawOneByteString(length);
|
| + if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + }
|
| + // Copy the characters into the new object.
|
| + uint8_t* dest = SeqOneByteString::cast(result)->GetChars();
|
| + // Copy first part.
|
| + const uint8_t* src;
|
| + if (first->IsExternalString()) {
|
| + src = ExternalAsciiString::cast(first)->GetChars();
|
| + } else {
|
| + src = SeqOneByteString::cast(first)->GetChars();
|
| + }
|
| + for (int i = 0; i < first_length; i++) *dest++ = src[i];
|
| + // Copy second part.
|
| + if (second->IsExternalString()) {
|
| + src = ExternalAsciiString::cast(second)->GetChars();
|
| + } else {
|
| + src = SeqOneByteString::cast(second)->GetChars();
|
| + }
|
| + for (int i = 0; i < second_length; i++) *dest++ = src[i];
|
| + return result;
|
| + } else {
|
| + if (is_one_byte_data_in_two_byte_string) {
|
| + Object* result;
|
| + { MaybeObject* maybe_result = AllocateRawOneByteString(length);
|
| + if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + }
|
| + // Copy the characters into the new object.
|
| + uint8_t* dest = SeqOneByteString::cast(result)->GetChars();
|
| + String::WriteToFlat(first, dest, 0, first_length);
|
| + String::WriteToFlat(second, dest + first_length, 0, second_length);
|
| + isolate_->counters()->string_add_runtime_ext_to_ascii()->Increment();
|
| + return result;
|
| + }
|
| +
|
| + Object* result;
|
| + { MaybeObject* maybe_result = AllocateRawTwoByteString(length);
|
| + if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + }
|
| + // Copy the characters into the new object.
|
| + uc16* dest = SeqTwoByteString::cast(result)->GetChars();
|
| + String::WriteToFlat(first, dest, 0, first_length);
|
| + String::WriteToFlat(second, dest + first_length, 0, second_length);
|
| + return result;
|
| + }
|
| + }
|
| +
|
| + Map* map = (is_one_byte || is_one_byte_data_in_two_byte_string) ?
|
| + cons_ascii_string_map() : cons_string_map();
|
| +
|
| + Object* result;
|
| + { MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
|
| + if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + }
|
| +
|
| + DisallowHeapAllocation no_gc;
|
| + ConsString* cons_string = ConsString::cast(result);
|
| + WriteBarrierMode mode = cons_string->GetWriteBarrierMode(no_gc);
|
| + cons_string->set_length(length);
|
| + cons_string->set_hash_field(String::kEmptyHashField);
|
| + cons_string->set_first(first, mode);
|
| + cons_string->set_second(second, mode);
|
| + return result;
|
| +}
|
| +
|
| +
|
| +MaybeObject* Heap::AllocateSubString(String* buffer,
|
| + int start,
|
| + int end,
|
| + PretenureFlag pretenure) {
|
| + int length = end - start;
|
| + if (length <= 0) {
|
| + return empty_string();
|
| + } else if (length == 1) {
|
| + return LookupSingleCharacterStringFromCode(buffer->Get(start));
|
| + } else if (length == 2) {
|
| + // Optimization for 2-byte strings often used as keys in a decompression
|
| + // dictionary. Check whether we already have the string in the string
|
| + // table to prevent creation of many unnecessary strings.
|
| + uint16_t c1 = buffer->Get(start);
|
| + uint16_t c2 = buffer->Get(start + 1);
|
| + return MakeOrFindTwoCharacterString(this, c1, c2);
|
| + }
|
| +
|
| + // Make an attempt to flatten the buffer to reduce access time.
|
| + buffer = buffer->TryFlattenGetString();
|
| +
|
| + if (!FLAG_string_slices ||
|
| + !buffer->IsFlat() ||
|
| + length < SlicedString::kMinLength ||
|
| + pretenure == TENURED) {
|
| + Object* result;
|
| + // WriteToFlat takes care of the case when an indirect string has a
|
| + // different encoding from its underlying string. These encodings may
|
| + // differ because of externalization.
|
| + bool is_one_byte = buffer->IsOneByteRepresentation();
|
| + { MaybeObject* maybe_result = is_one_byte
|
| + ? AllocateRawOneByteString(length, pretenure)
|
| + : AllocateRawTwoByteString(length, pretenure);
|
| + if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + }
|
| + String* string_result = String::cast(result);
|
| + // Copy the characters into the new object.
|
| + if (is_one_byte) {
|
| + ASSERT(string_result->IsOneByteRepresentation());
|
| + uint8_t* dest = SeqOneByteString::cast(string_result)->GetChars();
|
| + String::WriteToFlat(buffer, dest, start, end);
|
| + } else {
|
| + ASSERT(string_result->IsTwoByteRepresentation());
|
| + uc16* dest = SeqTwoByteString::cast(string_result)->GetChars();
|
| + String::WriteToFlat(buffer, dest, start, end);
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + ASSERT(buffer->IsFlat());
|
| +#if VERIFY_HEAP
|
| + if (FLAG_verify_heap) {
|
| + buffer->StringVerify();
|
| + }
|
| +#endif
|
| +
|
| + Object* result;
|
| + // When slicing an indirect string we use its encoding for a newly created
|
| + // slice and don't check the encoding of the underlying string. This is safe
|
| + // even if the encodings are different because of externalization. If an
|
| + // indirect ASCII string is pointing to a two-byte string, the two-byte char
|
| + // codes of the underlying string must still fit into ASCII (because
|
| + // externalization must not change char codes).
|
| + { Map* map = buffer->IsOneByteRepresentation()
|
| + ? sliced_ascii_string_map()
|
| + : sliced_string_map();
|
| + MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
|
| + if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + }
|
| +
|
| + DisallowHeapAllocation no_gc;
|
| + SlicedString* sliced_string = SlicedString::cast(result);
|
| + sliced_string->set_length(length);
|
| + sliced_string->set_hash_field(String::kEmptyHashField);
|
| + if (buffer->IsConsString()) {
|
| + ConsString* cons = ConsString::cast(buffer);
|
| + ASSERT(cons->second()->length() == 0);
|
| + sliced_string->set_parent(cons->first());
|
| + sliced_string->set_offset(start);
|
| + } else if (buffer->IsSlicedString()) {
|
| + // Prevent nesting sliced strings.
|
| + SlicedString* parent_slice = SlicedString::cast(buffer);
|
| + sliced_string->set_parent(parent_slice->parent());
|
| + sliced_string->set_offset(start + parent_slice->offset());
|
| + } else {
|
| + sliced_string->set_parent(buffer);
|
| + sliced_string->set_offset(start);
|
| + }
|
| + ASSERT(sliced_string->parent()->IsSeqString() ||
|
| + sliced_string->parent()->IsExternalString());
|
| + return result;
|
| +}
|
| +
|
| +
|
| MaybeObject* Heap::AllocateExternalStringFromAscii(
|
| const ExternalAsciiString::Resource* resource) {
|
| size_t length = resource->length();
|
|
|