Index: src/runtime.cc |
diff --git a/src/runtime.cc b/src/runtime.cc |
index 53c048e9a786fa9d4cb0e3c11412fd96f5cc56a6..ce47ff16c83b282092594e52de6e24497e65c318 100644 |
--- a/src/runtime.cc |
+++ b/src/runtime.cc |
@@ -6159,6 +6159,135 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) { |
return answer; |
} |
+template <typename Char> |
+static void JoinSparseArrayWithSeparator(FixedArray* elements, |
+ int elements_length, |
+ uint32_t array_length, |
+ String* separator, |
+ Vector<Char> buffer) { |
+ int previous_separator_position = 0; |
+ int separator_length = separator->length(); |
+ int cursor = 0; |
+ for (int i = 0; i < elements_length; i += 2) { |
+ int position = NumberToInt32(elements->get(i)); |
+ String* string = String::cast(elements->get(i + 1)); |
+ int string_length = string->length(); |
+ if (string->length() > 0) { |
+ while (previous_separator_position < position) { |
+ String::WriteToFlat<Char>(separator, &buffer[cursor], |
+ 0, separator_length); |
+ cursor += separator_length; |
+ previous_separator_position++; |
+ } |
+ String::WriteToFlat<Char>(string, &buffer[cursor], |
+ 0, string_length); |
+ cursor += string->length(); |
+ } |
+ } |
+ if (separator_length > 0) { |
+ // Array length must be representable as a signed 32-bit number, |
+ // otherwise the total string length would have been too large. |
+ ASSERT(array_length <= 0x7fffffff); // Is int32_t. |
+ int last_array_index = static_cast<int>(array_length - 1); |
+ while (previous_separator_position < last_array_index) { |
+ String::WriteToFlat<Char>(separator, &buffer[cursor], |
+ 0, separator_length); |
+ cursor += separator_length; |
+ previous_separator_position++; |
+ } |
+ } |
+ ASSERT(cursor <= buffer.length()); |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) { |
+ NoHandleAllocation ha; |
+ ASSERT(args.length() == 3); |
+ CONVERT_CHECKED(JSArray, elements_array, args[0]); |
+ RUNTIME_ASSERT(elements_array->HasFastElements()); |
+ CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]); |
+ CONVERT_CHECKED(String, separator, args[2]); |
+ // elements_array is fast-mode JSarray of alternating positions |
+ // (increasing order) and strings. |
+ // array_length is length of original array (used to add separators); |
+ // separator is string to put between elements. Assumed to be non-empty. |
+ |
+ // Find total length of join result. |
+ int string_length = 0; |
+ bool is_ascii = true; |
+ int max_string_length = SeqAsciiString::kMaxLength; |
+ bool overflow = false; |
+ CONVERT_NUMBER_CHECKED(int, elements_length, |
+ Int32, elements_array->length()); |
+ RUNTIME_ASSERT((elements_length & 1) == 0); // Even length. |
+ FixedArray* elements = FixedArray::cast(elements_array->elements()); |
+ for (int i = 0; i < elements_length; i += 2) { |
+ RUNTIME_ASSERT(elements->get(i)->IsNumber()); |
+ CONVERT_CHECKED(String, string, elements->get(i + 1)); |
+ int length = string->length(); |
+ if (is_ascii && !string->IsAsciiRepresentation()) { |
+ is_ascii = false; |
+ max_string_length = SeqTwoByteString::kMaxLength; |
+ } |
+ if (length > max_string_length || |
+ max_string_length - length < string_length) { |
+ overflow = true; |
+ break; |
+ } |
+ string_length += length; |
+ } |
+ int separator_length = separator->length(); |
+ if (!overflow && separator_length > 0) { |
+ if (array_length <= 0x7fffffffu) { |
+ int separator_count = static_cast<int>(array_length) - 1; |
+ int remaining_length = max_string_length - string_length; |
+ if ((remaining_length / separator_length) >= separator_count) { |
+ string_length += separator_length * (array_length - 1); |
+ } else { |
+ // Not room for the separators within the maximal string length. |
+ overflow = true; |
+ } |
+ } else { |
+ // Nonempty separator and at least 2^31-1 separators necessary |
+ // means that the string is too large to create. |
+ STATIC_ASSERT(String::kMaxLength < 0x7fffffff); |
+ overflow = true; |
+ } |
+ } |
+ if (overflow) { |
+ // Throw OutOfMemory exception for creating too large a string. |
+ V8::FatalProcessOutOfMemory("Array join result too large."); |
+ } |
+ |
+ if (is_ascii) { |
+ MaybeObject* result_allocation = |
+ isolate->heap()->AllocateRawAsciiString(string_length); |
+ if (result_allocation->IsFailure()) return result_allocation; |
+ SeqAsciiString* result_string = |
+ SeqAsciiString::cast(result_allocation->ToObjectUnchecked()); |
+ JoinSparseArrayWithSeparator<char>(elements, |
+ elements_length, |
+ array_length, |
+ separator, |
+ Vector<char>(result_string->GetChars(), |
+ string_length)); |
+ return result_string; |
+ } else { |
+ MaybeObject* result_allocation = |
+ isolate->heap()->AllocateRawTwoByteString(string_length); |
+ if (result_allocation->IsFailure()) return result_allocation; |
+ SeqTwoByteString* result_string = |
+ SeqTwoByteString::cast(result_allocation->ToObjectUnchecked()); |
+ JoinSparseArrayWithSeparator<uc16>(elements, |
+ elements_length, |
+ array_length, |
+ separator, |
+ Vector<uc16>(result_string->GetChars(), |
+ string_length)); |
+ return result_string; |
+ } |
+} |
+ |
RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberOr) { |
NoHandleAllocation ha; |