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(); |