"src/runtime/runtime-utils.h",
"src/runtime/runtime.cc",
"src/runtime/runtime.h",
- "src/runtime/string-builder.h",
"src/safepoint-table.cc",
"src/safepoint-table.h",
"src/sampler.cc",
"src/snapshot-source-sink.cc",
"src/snapshot-source-sink.h",
"src/snapshot.h",
+ "src/string-builder.h",
+ "src/string-builder.c",
"src/string-search.cc",
"src/string-search.h",
"src/string-stream.cc",
#include "src/v8.h"
#include "src/conversions.h"
+#include "src/string-builder.h"
#include "src/utils.h"
namespace v8 {
Handle<String> object));
private:
- static const int kInitialPartLength = 32;
- static const int kMaxPartLength = 16 * 1024;
- static const int kPartLengthGrowthFactor = 2;
-
enum Result { UNCHANGED, SUCCESS, EXCEPTION };
- void Accumulate();
-
- void Extend();
-
- void ChangeEncoding();
-
- INLINE(void ShrinkCurrentPart());
-
- template <bool is_one_byte, typename Char>
- INLINE(void Append_(Char c));
-
- template <bool is_one_byte, typename Char>
- INLINE(void Append_(const Char* chars));
-
- INLINE(void Append(uint8_t c)) {
- if (is_one_byte_) {
- Append_<true>(c);
- } else {
- Append_<false>(c);
- }
- }
-
- INLINE(void AppendOneByte(const char* chars)) {
- if (is_one_byte_) {
- Append_<true>(reinterpret_cast<const uint8_t*>(chars));
- } else {
- Append_<false>(reinterpret_cast<const uint8_t*>(chars));
- }
- }
-
MUST_USE_RESULT MaybeHandle<Object> ApplyToJsonFunction(
Handle<Object> object,
Handle<Object> key);
bool deferred_comma,
bool deferred_key);
- template <typename ResultType, typename Char>
- INLINE(static Handle<String> StringifyString_(Isolate* isolate,
- Vector<Char> vector,
- Handle<String> result));
-
// Entry point to serialize the object.
INLINE(Result SerializeObject(Handle<Object> obj)) {
- return Serialize_<false>(obj, false, factory_->empty_string());
+ return Serialize_<false>(obj, false, factory()->empty_string());
}
// Serialize an array element.
Result Serialize_(Handle<Object> object, bool comma, Handle<Object> key);
void SerializeDeferredKey(bool deferred_comma, Handle<Object> deferred_key) {
- if (deferred_comma) Append(',');
+ if (deferred_comma) builder_.AppendCharacter(',');
SerializeString(Handle<String>::cast(deferred_key));
- Append(':');
+ builder_.AppendCharacter(':');
}
Result SerializeSmi(Smi* object);
void SerializeString(Handle<String> object);
template <typename SrcChar, typename DestChar>
- INLINE(static int SerializeStringUnchecked_(const SrcChar* src,
- DestChar* dest,
- int length));
+ INLINE(static void SerializeStringUnchecked_(
+ Vector<const SrcChar> src,
+ IncrementalStringBuilder::NoExtend<DestChar>* dest));
- template <bool is_one_byte, typename Char>
+ template <typename SrcChar, typename DestChar>
INLINE(void SerializeString_(Handle<String> string));
template <typename Char>
Result StackPush(Handle<Object> object);
void StackPop();
- INLINE(Handle<String> accumulator()) {
- return Handle<String>(String::cast(accumulator_store_->value()), isolate_);
- }
-
- INLINE(void set_accumulator(Handle<String> string)) {
- return accumulator_store_->set_value(*string);
- }
+ Factory* factory() { return isolate_->factory(); }
Isolate* isolate_;
- Factory* factory_;
- // We use a value wrapper for the string accumulator to keep the
- // (indirect) handle to it in the outermost handle scope.
- Handle<JSValue> accumulator_store_;
- Handle<String> current_part_;
+ IncrementalStringBuilder builder_;
Handle<String> tojson_string_;
Handle<JSArray> stack_;
- int current_index_;
- int part_length_;
- bool is_one_byte_;
- bool overflowed_;
static const int kJsonEscapeTableEntrySize = 8;
static const char* const JsonEscapeTable;
BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
- : isolate_(isolate),
- current_index_(0),
- is_one_byte_(true),
- overflowed_(false) {
- factory_ = isolate_->factory();
- accumulator_store_ = Handle<JSValue>::cast(
- Object::ToObject(isolate, factory_->empty_string()).ToHandleChecked());
- part_length_ = kInitialPartLength;
- current_part_ = factory_->NewRawOneByteString(part_length_).ToHandleChecked();
- tojson_string_ = factory_->toJSON_string();
- stack_ = factory_->NewJSArray(8);
+ : isolate_(isolate), builder_(isolate) {
+ tojson_string_ = factory()->toJSON_string();
+ stack_ = factory()->NewJSArray(8);
}
MaybeHandle<Object> BasicJsonStringifier::Stringify(Handle<Object> object) {
Result result = SerializeObject(object);
- if (result == UNCHANGED) return isolate_->factory()->undefined_value();
- if (result == SUCCESS) {
- ShrinkCurrentPart();
- Accumulate();
- if (overflowed_) {
- THROW_NEW_ERROR(isolate_, NewInvalidStringLengthError(), Object);
- }
- return accumulator();
- }
+ if (result == UNCHANGED) return factory()->undefined_value();
+ if (result == SUCCESS) return builder_.Finish();
DCHECK(result == EXCEPTION);
return MaybeHandle<Object>();
}
object = String::Flatten(object);
DCHECK(object->IsFlat());
+ Handle<SeqString> result;
if (object->IsOneByteRepresentationUnderneath()) {
- Handle<String> result = isolate->factory()->NewRawOneByteString(
- worst_case_length).ToHandleChecked();
- DisallowHeapAllocation no_gc;
- return StringifyString_<SeqOneByteString>(
- isolate,
- object->GetFlatContent().ToOneByteVector(),
- result);
- } else {
- Handle<String> result = isolate->factory()->NewRawTwoByteString(
- worst_case_length).ToHandleChecked();
- DisallowHeapAllocation no_gc;
- return StringifyString_<SeqTwoByteString>(
- isolate,
- object->GetFlatContent().ToUC16Vector(),
- result);
- }
-}
-
-
-template <typename ResultType, typename Char>
-Handle<String> BasicJsonStringifier::StringifyString_(Isolate* isolate,
- Vector<Char> vector,
- Handle<String> result) {
- DisallowHeapAllocation no_gc;
- int final_size = 0;
- ResultType* dest = ResultType::cast(*result);
- dest->Set(final_size++, '\"');
- final_size += SerializeStringUnchecked_(vector.start(),
- dest->GetChars() + 1,
- vector.length());
- dest->Set(final_size++, '\"');
- return SeqString::Truncate(Handle<SeqString>::cast(result), final_size);
-}
-
-
-template <bool is_one_byte, typename Char>
-void BasicJsonStringifier::Append_(Char c) {
- if (is_one_byte) {
- SeqOneByteString::cast(*current_part_)->SeqOneByteStringSet(
- current_index_++, c);
+ result = isolate->factory()
+ ->NewRawOneByteString(worst_case_length)
+ .ToHandleChecked();
+ IncrementalStringBuilder::NoExtendString<uint8_t> no_extend(
+ result, worst_case_length);
+ no_extend.Append('\"');
+ SerializeStringUnchecked_(object->GetFlatContent().ToOneByteVector(),
+ &no_extend);
+ no_extend.Append('\"');
} else {
- SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet(
- current_index_++, c);
+ result = isolate->factory()
+ ->NewRawTwoByteString(worst_case_length)
+ .ToHandleChecked();
+ IncrementalStringBuilder::NoExtendString<uc16> no_extend(result,
+ worst_case_length);
+ no_extend.Append('\"');
+ SerializeStringUnchecked_(object->GetFlatContent().ToUC16Vector(),
+ &no_extend);
+ no_extend.Append('\"');
}
- if (current_index_ == part_length_) Extend();
-}
-
-
-template <bool is_one_byte, typename Char>
-void BasicJsonStringifier::Append_(const Char* chars) {
- for (; *chars != '\0'; chars++) Append_<is_one_byte, Char>(*chars);
+ return result;
}
if (!fun->IsJSFunction()) return object;
// Call toJSON function.
- if (key->IsSmi()) key = factory_->NumberToString(key);
+ if (key->IsSmi()) key = factory()->NumberToString(key);
Handle<Object> argv[] = { key };
HandleScope scope(isolate_);
ASSIGN_RETURN_ON_EXCEPTION(
if (elements->get(i) == *object) {
AllowHeapAllocation allow_to_return_error;
Handle<Object> error;
- MaybeHandle<Object> maybe_error = factory_->NewTypeError(
+ MaybeHandle<Object> maybe_error = factory()->NewTypeError(
"circular_structure", HandleVector<Object>(NULL, 0));
if (maybe_error.ToHandle(&error)) isolate_->Throw(*error);
return EXCEPTION;
switch (Oddball::cast(*object)->kind()) {
case Oddball::kFalse:
if (deferred_string_key) SerializeDeferredKey(comma, key);
- AppendOneByte("false");
+ builder_.AppendCString("false");
return SUCCESS;
case Oddball::kTrue:
if (deferred_string_key) SerializeDeferredKey(comma, key);
- AppendOneByte("true");
+ builder_.AppendCString("true");
return SUCCESS;
case Oddball::kNull:
if (deferred_string_key) SerializeDeferredKey(comma, key);
- AppendOneByte("null");
+ builder_.AppendCString("null");
return SUCCESS;
default:
return UNCHANGED;
EXCEPTION);
if (result->IsUndefined()) return UNCHANGED;
if (deferred_key) {
- if (key->IsSmi()) key = factory_->NumberToString(key);
+ if (key->IsSmi()) key = factory()->NumberToString(key);
SerializeDeferredKey(deferred_comma, key);
}
- Handle<String> result_string = Handle<String>::cast(result);
- // Shrink current part, attach it to the accumulator, also attach the result
- // string to the accumulator, and allocate a new part.
- ShrinkCurrentPart(); // Shrink.
- part_length_ = kInitialPartLength; // Allocate conservatively.
- Extend(); // Attach current part and allocate new part.
- // Attach result string to the accumulator.
- Handle<String> cons;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate_, cons,
- factory_->NewConsString(accumulator(), result_string),
- EXCEPTION);
- set_accumulator(cons);
+ builder_.AppendString(Handle<String>::cast(result));
return SUCCESS;
}
DCHECK(class_name == isolate_->heap()->Boolean_string());
Object* value = JSValue::cast(*object)->value();
DCHECK(value->IsBoolean());
- AppendOneByte(value->IsTrue() ? "true" : "false");
+ builder_.AppendCString(value->IsTrue() ? "true" : "false");
}
return SUCCESS;
}
static const int kBufferSize = 100;
char chars[kBufferSize];
Vector<char> buffer(chars, kBufferSize);
- AppendOneByte(IntToCString(object->value(), buffer));
+ builder_.AppendCString(IntToCString(object->value(), buffer));
return SUCCESS;
}
BasicJsonStringifier::Result BasicJsonStringifier::SerializeDouble(
double number) {
if (std::isinf(number) || std::isnan(number)) {
- AppendOneByte("null");
+ builder_.AppendCString("null");
return SUCCESS;
}
static const int kBufferSize = 100;
char chars[kBufferSize];
Vector<char> buffer(chars, kBufferSize);
- AppendOneByte(DoubleToCString(number, buffer));
+ builder_.AppendCString(DoubleToCString(number, buffer));
return SUCCESS;
}
if (stack_push != SUCCESS) return stack_push;
uint32_t length = 0;
CHECK(object->length()->ToArrayIndex(&length));
- Append('[');
+ builder_.AppendCharacter('[');
switch (object->GetElementsKind()) {
case FAST_SMI_ELEMENTS: {
Handle<FixedArray> elements(
FixedArray::cast(object->elements()), isolate_);
for (uint32_t i = 0; i < length; i++) {
- if (i > 0) Append(',');
+ if (i > 0) builder_.AppendCharacter(',');
SerializeSmi(Smi::cast(elements->get(i)));
}
break;
Handle<FixedDoubleArray> elements(
FixedDoubleArray::cast(object->elements()), isolate_);
for (uint32_t i = 0; i < length; i++) {
- if (i > 0) Append(',');
+ if (i > 0) builder_.AppendCharacter(',');
SerializeDouble(elements->get_scalar(i));
}
break;
Handle<FixedArray> elements(
FixedArray::cast(object->elements()), isolate_);
for (uint32_t i = 0; i < length; i++) {
- if (i > 0) Append(',');
+ if (i > 0) builder_.AppendCharacter(',');
Result result =
SerializeElement(isolate_,
Handle<Object>(elements->get(i), isolate_),
i);
if (result == SUCCESS) continue;
if (result == UNCHANGED) {
- AppendOneByte("null");
+ builder_.AppendCString("null");
} else {
return result;
}
break;
}
}
- Append(']');
+ builder_.AppendCharacter(']');
StackPop();
- current_part_ = handle_scope.CloseAndEscape(current_part_);
return SUCCESS;
}
BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArraySlow(
Handle<JSArray> object, uint32_t length) {
for (uint32_t i = 0; i < length; i++) {
- if (i > 0) Append(',');
+ if (i > 0) builder_.AppendCharacter(',');
Handle<Object> element;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, element,
Object::GetElement(isolate_, object, i),
EXCEPTION);
if (element->IsUndefined()) {
- AppendOneByte("null");
+ builder_.AppendCString("null");
} else {
Result result = SerializeElement(isolate_, element, i);
if (result == SUCCESS) continue;
if (result == UNCHANGED) {
- AppendOneByte("null");
+ builder_.AppendCString("null");
} else {
return result;
}
if (stack_push != SUCCESS) return stack_push;
DCHECK(!object->IsJSGlobalProxy() && !object->IsGlobalObject());
- Append('{');
+ builder_.AppendCharacter('{');
bool comma = false;
if (object->HasFastProperties() &&
maybe_property = Object::GetPropertyOrElement(object, key_handle);
} else {
DCHECK(key->IsNumber());
- key_handle = factory_->NumberToString(Handle<Object>(key, isolate_));
+ key_handle = factory()->NumberToString(Handle<Object>(key, isolate_));
uint32_t index;
if (key->IsSmi()) {
maybe_property = Object::GetElement(
}
}
- Append('}');
+ builder_.AppendCharacter('}');
StackPop();
- current_part_ = handle_scope.CloseAndEscape(current_part_);
return SUCCESS;
}
-void BasicJsonStringifier::ShrinkCurrentPart() {
- DCHECK(current_index_ < part_length_);
- current_part_ = SeqString::Truncate(Handle<SeqString>::cast(current_part_),
- current_index_);
-}
-
-
-void BasicJsonStringifier::Accumulate() {
- if (accumulator()->length() + current_part_->length() > String::kMaxLength) {
- // Screw it. Simply set the flag and carry on. Throw exception at the end.
- set_accumulator(factory_->empty_string());
- overflowed_ = true;
- } else {
- set_accumulator(factory_->NewConsString(accumulator(),
- current_part_).ToHandleChecked());
- }
-}
-
-
-void BasicJsonStringifier::Extend() {
- Accumulate();
- if (part_length_ <= kMaxPartLength / kPartLengthGrowthFactor) {
- part_length_ *= kPartLengthGrowthFactor;
- }
- if (is_one_byte_) {
- current_part_ =
- factory_->NewRawOneByteString(part_length_).ToHandleChecked();
- } else {
- current_part_ =
- factory_->NewRawTwoByteString(part_length_).ToHandleChecked();
- }
- DCHECK(!current_part_.is_null());
- current_index_ = 0;
-}
-
-
-void BasicJsonStringifier::ChangeEncoding() {
- ShrinkCurrentPart();
- Accumulate();
- current_part_ =
- factory_->NewRawTwoByteString(part_length_).ToHandleChecked();
- DCHECK(!current_part_.is_null());
- current_index_ = 0;
- is_one_byte_ = false;
-}
-
-
template <typename SrcChar, typename DestChar>
-int BasicJsonStringifier::SerializeStringUnchecked_(const SrcChar* src,
- DestChar* dest,
- int length) {
- DestChar* dest_start = dest;
-
+void BasicJsonStringifier::SerializeStringUnchecked_(
+ Vector<const SrcChar> src,
+ IncrementalStringBuilder::NoExtend<DestChar>* dest) {
// Assert that uc16 character is not truncated down to 8 bit.
// The <uc16, char> version of this method must not be called.
- DCHECK(sizeof(*dest) >= sizeof(*src));
+ DCHECK(sizeof(DestChar) >= sizeof(SrcChar));
- for (int i = 0; i < length; i++) {
+ for (int i = 0; i < src.length(); i++) {
SrcChar c = src[i];
if (DoNotEscape(c)) {
- *(dest++) = static_cast<DestChar>(c);
+ dest->Append(c);
} else {
- const uint8_t* chars = reinterpret_cast<const uint8_t*>(
- &JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
- while (*chars != '\0') *(dest++) = *(chars++);
+ dest->AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
}
}
-
- return static_cast<int>(dest - dest_start);
}
-template <bool is_one_byte, typename Char>
+template <typename SrcChar, typename DestChar>
void BasicJsonStringifier::SerializeString_(Handle<String> string) {
int length = string->length();
- Append_<is_one_byte, char>('"');
+ builder_.Append<uint8_t, DestChar>('"');
// We make a rough estimate to find out if the current string can be
// serialized without allocating a new string part. The worst case length of
// an escaped character is 6. Shifting the remainin string length right by 3
// is a more pessimistic estimate, but faster to calculate.
-
- if (((part_length_ - current_index_) >> 3) > length) {
+ int worst_case_length = length << 3;
+ if (builder_.CurrentPartCanFit(worst_case_length)) {
DisallowHeapAllocation no_gc;
- Vector<const Char> vector = GetCharVector<Char>(string);
- if (is_one_byte) {
- current_index_ += SerializeStringUnchecked_(
- vector.start(),
- SeqOneByteString::cast(*current_part_)->GetChars() + current_index_,
- length);
- } else {
- current_index_ += SerializeStringUnchecked_(
- vector.start(),
- SeqTwoByteString::cast(*current_part_)->GetChars() + current_index_,
- length);
- }
+ Vector<const SrcChar> vector = GetCharVector<SrcChar>(string);
+ IncrementalStringBuilder::NoExtendBuilder<DestChar> no_extend(
+ &builder_, worst_case_length);
+ SerializeStringUnchecked_(vector, &no_extend);
} else {
String* string_location = NULL;
- Vector<const Char> vector(NULL, 0);
+ Vector<const SrcChar> vector(NULL, 0);
for (int i = 0; i < length; i++) {
// If GC moved the string, we need to refresh the vector.
if (*string != string_location) {
DisallowHeapAllocation no_gc;
// This does not actually prevent the string from being relocated later.
- vector = GetCharVector<Char>(string);
+ vector = GetCharVector<SrcChar>(string);
string_location = *string;
}
- Char c = vector[i];
+ SrcChar c = vector[i];
if (DoNotEscape(c)) {
- Append_<is_one_byte, Char>(c);
+ builder_.Append<SrcChar, DestChar>(c);
} else {
- Append_<is_one_byte, uint8_t>(reinterpret_cast<const uint8_t*>(
- &JsonEscapeTable[c * kJsonEscapeTableEntrySize]));
+ builder_.AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
}
}
}
- Append_<is_one_byte, uint8_t>('"');
+ builder_.Append<uint8_t, DestChar>('"');
}
void BasicJsonStringifier::SerializeString(Handle<String> object) {
object = String::Flatten(object);
- if (is_one_byte_) {
+ if (builder_.CurrentEncoding() == String::ONE_BYTE_ENCODING) {
if (object->IsOneByteRepresentationUnderneath()) {
- SerializeString_<true, uint8_t>(object);
+ SerializeString_<uint8_t, uint8_t>(object);
} else {
- ChangeEncoding();
+ builder_.ChangeEncoding();
SerializeString(object);
}
} else {
if (object->IsOneByteRepresentationUnderneath()) {
- SerializeString_<false, uint8_t>(object);
+ SerializeString_<uint8_t, uc16>(object);
} else {
- SerializeString_<false, uc16>(object);
+ SerializeString_<uc16, uc16>(object);
}
}
}
#include "src/jsregexp-inl.h"
#include "src/jsregexp.h"
#include "src/runtime/runtime-utils.h"
-#include "src/runtime/string-builder.h"
+#include "src/string-builder.h"
#include "src/string-search.h"
namespace v8 {
#include "src/jsregexp-inl.h"
#include "src/jsregexp.h"
#include "src/runtime/runtime-utils.h"
-#include "src/runtime/string-builder.h"
+#include "src/string-builder.h"
#include "src/string-search.h"
namespace v8 {
+++ /dev/null
-// Copyright 2014 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef V8_RUNTIME_STRING_BUILDER_H_
-#define V8_RUNTIME_STRING_BUILDER_H_
-
-namespace v8 {
-namespace internal {
-
-const int kStringBuilderConcatHelperLengthBits = 11;
-const int kStringBuilderConcatHelperPositionBits = 19;
-
-typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
- StringBuilderSubstringLength;
-typedef BitField<int, kStringBuilderConcatHelperLengthBits,
- kStringBuilderConcatHelperPositionBits>
- StringBuilderSubstringPosition;
-
-
-template <typename sinkchar>
-static inline void StringBuilderConcatHelper(String* special, sinkchar* sink,
- FixedArray* fixed_array,
- int array_length) {
- DisallowHeapAllocation no_gc;
- int position = 0;
- for (int i = 0; i < array_length; i++) {
- Object* element = fixed_array->get(i);
- if (element->IsSmi()) {
- // Smi encoding of position and length.
- int encoded_slice = Smi::cast(element)->value();
- int pos;
- int len;
- if (encoded_slice > 0) {
- // Position and length encoded in one smi.
- pos = StringBuilderSubstringPosition::decode(encoded_slice);
- len = StringBuilderSubstringLength::decode(encoded_slice);
- } else {
- // Position and length encoded in two smis.
- Object* obj = fixed_array->get(++i);
- DCHECK(obj->IsSmi());
- pos = Smi::cast(obj)->value();
- len = -encoded_slice;
- }
- String::WriteToFlat(special, sink + position, pos, pos + len);
- position += len;
- } else {
- String* string = String::cast(element);
- int element_length = string->length();
- String::WriteToFlat(string, sink + position, 0, element_length);
- position += element_length;
- }
- }
-}
-
-
-// Returns the result length of the concatenation.
-// On illegal argument, -1 is returned.
-static inline int StringBuilderConcatLength(int special_length,
- FixedArray* fixed_array,
- int array_length, bool* one_byte) {
- DisallowHeapAllocation no_gc;
- int position = 0;
- for (int i = 0; i < array_length; i++) {
- int increment = 0;
- Object* elt = fixed_array->get(i);
- if (elt->IsSmi()) {
- // Smi encoding of position and length.
- int smi_value = Smi::cast(elt)->value();
- int pos;
- int len;
- if (smi_value > 0) {
- // Position and length encoded in one smi.
- pos = StringBuilderSubstringPosition::decode(smi_value);
- len = StringBuilderSubstringLength::decode(smi_value);
- } else {
- // Position and length encoded in two smis.
- len = -smi_value;
- // Get the position and check that it is a positive smi.
- i++;
- if (i >= array_length) return -1;
- Object* next_smi = fixed_array->get(i);
- if (!next_smi->IsSmi()) return -1;
- pos = Smi::cast(next_smi)->value();
- if (pos < 0) return -1;
- }
- DCHECK(pos >= 0);
- DCHECK(len >= 0);
- if (pos > special_length || len > special_length - pos) return -1;
- increment = len;
- } else if (elt->IsString()) {
- String* element = String::cast(elt);
- int element_length = element->length();
- increment = element_length;
- if (*one_byte && !element->HasOnlyOneByteChars()) {
- *one_byte = false;
- }
- } else {
- return -1;
- }
- if (increment > String::kMaxLength - position) {
- return kMaxInt; // Provoke throw on allocation.
- }
- position += increment;
- }
- return position;
-}
-
-
-class FixedArrayBuilder {
- public:
- explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity)
- : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)),
- length_(0),
- has_non_smi_elements_(false) {
- // Require a non-zero initial size. Ensures that doubling the size to
- // extend the array will work.
- DCHECK(initial_capacity > 0);
- }
-
- explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
- : array_(backing_store), length_(0), has_non_smi_elements_(false) {
- // Require a non-zero initial size. Ensures that doubling the size to
- // extend the array will work.
- DCHECK(backing_store->length() > 0);
- }
-
- bool HasCapacity(int elements) {
- int length = array_->length();
- int required_length = length_ + elements;
- return (length >= required_length);
- }
-
- void EnsureCapacity(int elements) {
- int length = array_->length();
- int required_length = length_ + elements;
- if (length < required_length) {
- int new_length = length;
- do {
- new_length *= 2;
- } while (new_length < required_length);
- Handle<FixedArray> extended_array =
- array_->GetIsolate()->factory()->NewFixedArrayWithHoles(new_length);
- array_->CopyTo(0, *extended_array, 0, length_);
- array_ = extended_array;
- }
- }
-
- void Add(Object* value) {
- DCHECK(!value->IsSmi());
- DCHECK(length_ < capacity());
- array_->set(length_, value);
- length_++;
- has_non_smi_elements_ = true;
- }
-
- void Add(Smi* value) {
- DCHECK(value->IsSmi());
- DCHECK(length_ < capacity());
- array_->set(length_, value);
- length_++;
- }
-
- Handle<FixedArray> array() { return array_; }
-
- int length() { return length_; }
-
- int capacity() { return array_->length(); }
-
- Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
- JSArray::SetContent(target_array, array_);
- target_array->set_length(Smi::FromInt(length_));
- return target_array;
- }
-
-
- private:
- Handle<FixedArray> array_;
- int length_;
- bool has_non_smi_elements_;
-};
-
-
-class ReplacementStringBuilder {
- public:
- ReplacementStringBuilder(Heap* heap, Handle<String> subject,
- int estimated_part_count)
- : heap_(heap),
- array_builder_(heap->isolate(), estimated_part_count),
- subject_(subject),
- character_count_(0),
- is_one_byte_(subject->IsOneByteRepresentation()) {
- // Require a non-zero initial size. Ensures that doubling the size to
- // extend the array will work.
- DCHECK(estimated_part_count > 0);
- }
-
- static inline void AddSubjectSlice(FixedArrayBuilder* builder, int from,
- int to) {
- DCHECK(from >= 0);
- int length = to - from;
- DCHECK(length > 0);
- if (StringBuilderSubstringLength::is_valid(length) &&
- StringBuilderSubstringPosition::is_valid(from)) {
- int encoded_slice = StringBuilderSubstringLength::encode(length) |
- StringBuilderSubstringPosition::encode(from);
- builder->Add(Smi::FromInt(encoded_slice));
- } else {
- // Otherwise encode as two smis.
- builder->Add(Smi::FromInt(-length));
- builder->Add(Smi::FromInt(from));
- }
- }
-
-
- void EnsureCapacity(int elements) { array_builder_.EnsureCapacity(elements); }
-
-
- void AddSubjectSlice(int from, int to) {
- AddSubjectSlice(&array_builder_, from, to);
- IncrementCharacterCount(to - from);
- }
-
-
- void AddString(Handle<String> string) {
- int length = string->length();
- DCHECK(length > 0);
- AddElement(*string);
- if (!string->IsOneByteRepresentation()) {
- is_one_byte_ = false;
- }
- IncrementCharacterCount(length);
- }
-
-
- MaybeHandle<String> ToString() {
- Isolate* isolate = heap_->isolate();
- if (array_builder_.length() == 0) {
- return isolate->factory()->empty_string();
- }
-
- Handle<String> joined_string;
- if (is_one_byte_) {
- Handle<SeqOneByteString> seq;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, seq,
- isolate->factory()->NewRawOneByteString(character_count_), String);
-
- DisallowHeapAllocation no_gc;
- uint8_t* char_buffer = seq->GetChars();
- StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
- array_builder_.length());
- joined_string = Handle<String>::cast(seq);
- } else {
- // Two-byte.
- Handle<SeqTwoByteString> seq;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, seq,
- isolate->factory()->NewRawTwoByteString(character_count_), String);
-
- DisallowHeapAllocation no_gc;
- uc16* char_buffer = seq->GetChars();
- StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
- array_builder_.length());
- joined_string = Handle<String>::cast(seq);
- }
- return joined_string;
- }
-
-
- void IncrementCharacterCount(int by) {
- if (character_count_ > String::kMaxLength - by) {
- STATIC_ASSERT(String::kMaxLength < kMaxInt);
- character_count_ = kMaxInt;
- } else {
- character_count_ += by;
- }
- }
-
- private:
- void AddElement(Object* element) {
- DCHECK(element->IsSmi() || element->IsString());
- DCHECK(array_builder_.capacity() > array_builder_.length());
- array_builder_.Add(element);
- }
-
- Heap* heap_;
- FixedArrayBuilder array_builder_;
- Handle<String> subject_;
- int character_count_;
- bool is_one_byte_;
-};
-}
-} // namespace v8::internal
-
-#endif // V8_RUNTIME_STRING_BUILDER_H_
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/string-builder.h"
+
+namespace v8 {
+namespace internal {
+
+MaybeHandle<String> ReplacementStringBuilder::ToString() {
+ Isolate* isolate = heap_->isolate();
+ if (array_builder_.length() == 0) {
+ return isolate->factory()->empty_string();
+ }
+
+ Handle<String> joined_string;
+ if (is_one_byte_) {
+ Handle<SeqOneByteString> seq;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, seq, isolate->factory()->NewRawOneByteString(character_count_),
+ String);
+
+ DisallowHeapAllocation no_gc;
+ uint8_t* char_buffer = seq->GetChars();
+ StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
+ array_builder_.length());
+ joined_string = Handle<String>::cast(seq);
+ } else {
+ // Two-byte.
+ Handle<SeqTwoByteString> seq;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, seq, isolate->factory()->NewRawTwoByteString(character_count_),
+ String);
+
+ DisallowHeapAllocation no_gc;
+ uc16* char_buffer = seq->GetChars();
+ StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
+ array_builder_.length());
+ joined_string = Handle<String>::cast(seq);
+ }
+ return joined_string;
+}
+
+
+IncrementalStringBuilder::IncrementalStringBuilder(Isolate* isolate)
+ : isolate_(isolate),
+ encoding_(String::ONE_BYTE_ENCODING),
+ overflowed_(false),
+ part_length_(kInitialPartLength),
+ current_index_(0) {
+ // Create an accumulator handle starting with the empty string.
+ accumulator_ = Handle<String>(isolate->heap()->empty_string(), isolate);
+ current_part_ =
+ factory()->NewRawOneByteString(part_length_).ToHandleChecked();
+}
+
+
+void IncrementalStringBuilder::Accumulate() {
+ // Only accumulate fully written strings. Shrink first if necessary.
+ DCHECK_EQ(current_index_, current_part()->length());
+ Handle<String> new_accumulator;
+ if (accumulator()->length() + current_part()->length() > String::kMaxLength) {
+ // Set the flag and carry on. Delay throwing the exception till the end.
+ new_accumulator = factory()->empty_string();
+ overflowed_ = true;
+ } else {
+ new_accumulator = factory()
+ ->NewConsString(accumulator(), current_part())
+ .ToHandleChecked();
+ }
+ set_accumulator(new_accumulator);
+}
+
+
+void IncrementalStringBuilder::Extend() {
+ Accumulate();
+ if (part_length_ <= kMaxPartLength / kPartLengthGrowthFactor) {
+ part_length_ *= kPartLengthGrowthFactor;
+ }
+ Handle<String> new_part;
+ if (encoding_ == String::ONE_BYTE_ENCODING) {
+ new_part = factory()->NewRawOneByteString(part_length_).ToHandleChecked();
+ } else {
+ new_part = factory()->NewRawTwoByteString(part_length_).ToHandleChecked();
+ }
+ // Reuse the same handle to avoid being invalidated when exiting handle scope.
+ set_current_part(new_part);
+ current_index_ = 0;
+}
+
+
+MaybeHandle<String> IncrementalStringBuilder::Finish() {
+ ShrinkCurrentPart();
+ Accumulate();
+ if (overflowed_) {
+ THROW_NEW_ERROR(isolate_, NewInvalidStringLengthError(), String);
+ }
+ return accumulator();
+}
+
+
+void IncrementalStringBuilder::AppendString(Handle<String> string) {
+ ShrinkCurrentPart();
+ part_length_ = kInitialPartLength; // Allocate conservatively.
+ Extend(); // Attach current part and allocate new part.
+ Handle<String> concat =
+ factory()->NewConsString(accumulator(), string).ToHandleChecked();
+ set_accumulator(concat);
+}
+}
+} // namespace v8::internal
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_STRING_BUILDER_H_
+#define V8_STRING_BUILDER_H_
+
+#include "src/v8.h"
+
+namespace v8 {
+namespace internal {
+
+const int kStringBuilderConcatHelperLengthBits = 11;
+const int kStringBuilderConcatHelperPositionBits = 19;
+
+typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
+ StringBuilderSubstringLength;
+typedef BitField<int, kStringBuilderConcatHelperLengthBits,
+ kStringBuilderConcatHelperPositionBits>
+ StringBuilderSubstringPosition;
+
+
+template <typename sinkchar>
+static inline void StringBuilderConcatHelper(String* special, sinkchar* sink,
+ FixedArray* fixed_array,
+ int array_length) {
+ DisallowHeapAllocation no_gc;
+ int position = 0;
+ for (int i = 0; i < array_length; i++) {
+ Object* element = fixed_array->get(i);
+ if (element->IsSmi()) {
+ // Smi encoding of position and length.
+ int encoded_slice = Smi::cast(element)->value();
+ int pos;
+ int len;
+ if (encoded_slice > 0) {
+ // Position and length encoded in one smi.
+ pos = StringBuilderSubstringPosition::decode(encoded_slice);
+ len = StringBuilderSubstringLength::decode(encoded_slice);
+ } else {
+ // Position and length encoded in two smis.
+ Object* obj = fixed_array->get(++i);
+ DCHECK(obj->IsSmi());
+ pos = Smi::cast(obj)->value();
+ len = -encoded_slice;
+ }
+ String::WriteToFlat(special, sink + position, pos, pos + len);
+ position += len;
+ } else {
+ String* string = String::cast(element);
+ int element_length = string->length();
+ String::WriteToFlat(string, sink + position, 0, element_length);
+ position += element_length;
+ }
+ }
+}
+
+
+// Returns the result length of the concatenation.
+// On illegal argument, -1 is returned.
+static inline int StringBuilderConcatLength(int special_length,
+ FixedArray* fixed_array,
+ int array_length, bool* one_byte) {
+ DisallowHeapAllocation no_gc;
+ int position = 0;
+ for (int i = 0; i < array_length; i++) {
+ int increment = 0;
+ Object* elt = fixed_array->get(i);
+ if (elt->IsSmi()) {
+ // Smi encoding of position and length.
+ int smi_value = Smi::cast(elt)->value();
+ int pos;
+ int len;
+ if (smi_value > 0) {
+ // Position and length encoded in one smi.
+ pos = StringBuilderSubstringPosition::decode(smi_value);
+ len = StringBuilderSubstringLength::decode(smi_value);
+ } else {
+ // Position and length encoded in two smis.
+ len = -smi_value;
+ // Get the position and check that it is a positive smi.
+ i++;
+ if (i >= array_length) return -1;
+ Object* next_smi = fixed_array->get(i);
+ if (!next_smi->IsSmi()) return -1;
+ pos = Smi::cast(next_smi)->value();
+ if (pos < 0) return -1;
+ }
+ DCHECK(pos >= 0);
+ DCHECK(len >= 0);
+ if (pos > special_length || len > special_length - pos) return -1;
+ increment = len;
+ } else if (elt->IsString()) {
+ String* element = String::cast(elt);
+ int element_length = element->length();
+ increment = element_length;
+ if (*one_byte && !element->HasOnlyOneByteChars()) {
+ *one_byte = false;
+ }
+ } else {
+ return -1;
+ }
+ if (increment > String::kMaxLength - position) {
+ return kMaxInt; // Provoke throw on allocation.
+ }
+ position += increment;
+ }
+ return position;
+}
+
+
+class FixedArrayBuilder {
+ public:
+ explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity)
+ : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)),
+ length_(0),
+ has_non_smi_elements_(false) {
+ // Require a non-zero initial size. Ensures that doubling the size to
+ // extend the array will work.
+ DCHECK(initial_capacity > 0);
+ }
+
+ explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
+ : array_(backing_store), length_(0), has_non_smi_elements_(false) {
+ // Require a non-zero initial size. Ensures that doubling the size to
+ // extend the array will work.
+ DCHECK(backing_store->length() > 0);
+ }
+
+ bool HasCapacity(int elements) {
+ int length = array_->length();
+ int required_length = length_ + elements;
+ return (length >= required_length);
+ }
+
+ void EnsureCapacity(int elements) {
+ int length = array_->length();
+ int required_length = length_ + elements;
+ if (length < required_length) {
+ int new_length = length;
+ do {
+ new_length *= 2;
+ } while (new_length < required_length);
+ Handle<FixedArray> extended_array =
+ array_->GetIsolate()->factory()->NewFixedArrayWithHoles(new_length);
+ array_->CopyTo(0, *extended_array, 0, length_);
+ array_ = extended_array;
+ }
+ }
+
+ void Add(Object* value) {
+ DCHECK(!value->IsSmi());
+ DCHECK(length_ < capacity());
+ array_->set(length_, value);
+ length_++;
+ has_non_smi_elements_ = true;
+ }
+
+ void Add(Smi* value) {
+ DCHECK(value->IsSmi());
+ DCHECK(length_ < capacity());
+ array_->set(length_, value);
+ length_++;
+ }
+
+ Handle<FixedArray> array() { return array_; }
+
+ int length() { return length_; }
+
+ int capacity() { return array_->length(); }
+
+ Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
+ JSArray::SetContent(target_array, array_);
+ target_array->set_length(Smi::FromInt(length_));
+ return target_array;
+ }
+
+
+ private:
+ Handle<FixedArray> array_;
+ int length_;
+ bool has_non_smi_elements_;
+};
+
+
+class ReplacementStringBuilder {
+ public:
+ ReplacementStringBuilder(Heap* heap, Handle<String> subject,
+ int estimated_part_count)
+ : heap_(heap),
+ array_builder_(heap->isolate(), estimated_part_count),
+ subject_(subject),
+ character_count_(0),
+ is_one_byte_(subject->IsOneByteRepresentation()) {
+ // Require a non-zero initial size. Ensures that doubling the size to
+ // extend the array will work.
+ DCHECK(estimated_part_count > 0);
+ }
+
+ static inline void AddSubjectSlice(FixedArrayBuilder* builder, int from,
+ int to) {
+ DCHECK(from >= 0);
+ int length = to - from;
+ DCHECK(length > 0);
+ if (StringBuilderSubstringLength::is_valid(length) &&
+ StringBuilderSubstringPosition::is_valid(from)) {
+ int encoded_slice = StringBuilderSubstringLength::encode(length) |
+ StringBuilderSubstringPosition::encode(from);
+ builder->Add(Smi::FromInt(encoded_slice));
+ } else {
+ // Otherwise encode as two smis.
+ builder->Add(Smi::FromInt(-length));
+ builder->Add(Smi::FromInt(from));
+ }
+ }
+
+
+ void EnsureCapacity(int elements) { array_builder_.EnsureCapacity(elements); }
+
+
+ void AddSubjectSlice(int from, int to) {
+ AddSubjectSlice(&array_builder_, from, to);
+ IncrementCharacterCount(to - from);
+ }
+
+
+ void AddString(Handle<String> string) {
+ int length = string->length();
+ DCHECK(length > 0);
+ AddElement(*string);
+ if (!string->IsOneByteRepresentation()) {
+ is_one_byte_ = false;
+ }
+ IncrementCharacterCount(length);
+ }
+
+
+ MaybeHandle<String> ToString();
+
+
+ void IncrementCharacterCount(int by) {
+ if (character_count_ > String::kMaxLength - by) {
+ STATIC_ASSERT(String::kMaxLength < kMaxInt);
+ character_count_ = kMaxInt;
+ } else {
+ character_count_ += by;
+ }
+ }
+
+ private:
+ void AddElement(Object* element) {
+ DCHECK(element->IsSmi() || element->IsString());
+ DCHECK(array_builder_.capacity() > array_builder_.length());
+ array_builder_.Add(element);
+ }
+
+ Heap* heap_;
+ FixedArrayBuilder array_builder_;
+ Handle<String> subject_;
+ int character_count_;
+ bool is_one_byte_;
+};
+
+
+class IncrementalStringBuilder {
+ public:
+ explicit IncrementalStringBuilder(Isolate* isolate);
+
+ INLINE(String::Encoding CurrentEncoding()) { return encoding_; }
+
+ template <typename SrcChar, typename DestChar>
+ INLINE(void Append(SrcChar c));
+
+ INLINE(void AppendCharacter(uint8_t c)) {
+ if (encoding_ == String::ONE_BYTE_ENCODING) {
+ Append<uint8_t, uint8_t>(c);
+ } else {
+ Append<uint8_t, uc16>(c);
+ }
+ }
+
+ INLINE(void AppendCString(const char* s)) {
+ const uint8_t* u = reinterpret_cast<const uint8_t*>(s);
+ if (encoding_ == String::ONE_BYTE_ENCODING) {
+ while (*u != '\0') Append<uint8_t, uint8_t>(*(u++));
+ } else {
+ while (*u != '\0') Append<uint8_t, uc16>(*(u++));
+ }
+ }
+
+ INLINE(bool CurrentPartCanFit(int length)) {
+ return part_length_ - current_index_ > length;
+ }
+
+ void AppendString(Handle<String> string);
+
+ MaybeHandle<String> Finish();
+
+ // Change encoding to two-byte.
+ void ChangeEncoding() {
+ DCHECK_EQ(String::ONE_BYTE_ENCODING, encoding_);
+ ShrinkCurrentPart();
+ encoding_ = String::TWO_BYTE_ENCODING;
+ Extend();
+ }
+
+ template <typename DestChar>
+ class NoExtend {
+ public:
+ explicit NoExtend(Handle<String> string, int offset) {
+ DCHECK(string->IsSeqOneByteString() || string->IsSeqTwoByteString());
+ if (sizeof(DestChar) == 1) {
+ start_ = reinterpret_cast<DestChar*>(
+ Handle<SeqOneByteString>::cast(string)->GetChars() + offset);
+ } else {
+ start_ = reinterpret_cast<DestChar*>(
+ Handle<SeqTwoByteString>::cast(string)->GetChars() + offset);
+ }
+ cursor_ = start_;
+ }
+
+ INLINE(void Append(DestChar c)) { *(cursor_++) = c; }
+ INLINE(void AppendCString(const char* s)) {
+ const uint8_t* u = reinterpret_cast<const uint8_t*>(s);
+ while (*u != '\0') Append(*(u++));
+ }
+
+ int written() { return cursor_ - start_; }
+
+ private:
+ DestChar* start_;
+ DestChar* cursor_;
+ DisallowHeapAllocation no_gc_;
+ };
+
+ template <typename DestChar>
+ class NoExtendString : public NoExtend<DestChar> {
+ public:
+ NoExtendString(Handle<String> string, int required_length)
+ : NoExtend<DestChar>(string, 0), string_(string) {
+ DCHECK(string->length() >= required_length);
+ }
+
+ ~NoExtendString() {
+ Handle<SeqString> string = Handle<SeqString>::cast(string_);
+ int length = NoExtend<DestChar>::written();
+ *string_.location() = *SeqString::Truncate(string, length);
+ }
+
+ private:
+ Handle<String> string_;
+ };
+
+ template <typename DestChar>
+ class NoExtendBuilder : public NoExtend<DestChar> {
+ public:
+ NoExtendBuilder(IncrementalStringBuilder* builder, int required_length)
+ : NoExtend<DestChar>(builder->current_part(), builder->current_index_),
+ builder_(builder) {
+ DCHECK(builder->CurrentPartCanFit(required_length));
+ }
+
+ ~NoExtendBuilder() {
+ builder_->current_index_ += NoExtend<DestChar>::written();
+ }
+
+ private:
+ IncrementalStringBuilder* builder_;
+ };
+
+ private:
+ Factory* factory() { return isolate_->factory(); }
+
+ INLINE(Handle<String> accumulator()) { return accumulator_; }
+
+ INLINE(void set_accumulator(Handle<String> string)) {
+ *accumulator_.location() = *string;
+ }
+
+ INLINE(Handle<String> current_part()) { return current_part_; }
+
+ INLINE(void set_current_part(Handle<String> string)) {
+ *current_part_.location() = *string;
+ }
+
+ // Add the current part to the accumulator.
+ void Accumulate();
+
+ // Finish the current part and allocate a new part.
+ void Extend();
+
+ // Shrink current part to the right size.
+ void ShrinkCurrentPart() {
+ DCHECK(current_index_ < part_length_);
+ set_current_part(SeqString::Truncate(
+ Handle<SeqString>::cast(current_part()), current_index_));
+ }
+
+ static const int kInitialPartLength = 32;
+ static const int kMaxPartLength = 16 * 1024;
+ static const int kPartLengthGrowthFactor = 2;
+
+ Isolate* isolate_;
+ String::Encoding encoding_;
+ bool overflowed_;
+ int part_length_;
+ int current_index_;
+ Handle<String> accumulator_;
+ Handle<String> current_part_;
+};
+
+
+template <typename SrcChar, typename DestChar>
+void IncrementalStringBuilder::Append(SrcChar c) {
+ DCHECK_EQ(encoding_ == String::ONE_BYTE_ENCODING, sizeof(DestChar) == 1);
+ if (sizeof(DestChar) == 1) {
+ DCHECK_EQ(String::ONE_BYTE_ENCODING, encoding_);
+ SeqOneByteString::cast(*current_part_)
+ ->SeqOneByteStringSet(current_index_++, c);
+ } else {
+ DCHECK_EQ(String::TWO_BYTE_ENCODING, encoding_);
+ SeqTwoByteString::cast(*current_part_)
+ ->SeqTwoByteStringSet(current_index_++, c);
+ }
+ if (current_index_ == part_length_) Extend();
+}
+}
+} // namespace v8::internal
+
+#endif // V8_STRING_BUILDER_H_
'../../src/runtime/runtime-utils.h',
'../../src/runtime/runtime.cc',
'../../src/runtime/runtime.h',
- '../../src/runtime/string-builder.h',
'../../src/safepoint-table.cc',
'../../src/safepoint-table.h',
'../../src/sampler.cc',
'../../src/snapshot.h',
'../../src/snapshot-source-sink.cc',
'../../src/snapshot-source-sink.h',
+ '../../src/string-builder.h',
+ '../../src/string-builder.cc',
'../../src/string-search.cc',
'../../src/string-search.h',
'../../src/string-stream.cc',