Unify code for fast and slow path of JSON.stringify.

R=verwaest@chromium.org
BUG=

Review URL: https://chromiumcodereview.appspot.com/12690017

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14023 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2013-03-21 10:53:26 +00:00
parent 53adf3b19c
commit 9e1a7e2e6f
4 changed files with 79 additions and 376 deletions

View File

@ -220,6 +220,7 @@ namespace internal {
V(undefined_string, "undefined") \
V(value_of_string, "valueOf") \
V(stack_string, "stack") \
V(toJSON_string, "toJSON") \
V(InitializeVarGlobal_string, "InitializeVarGlobal") \
V(InitializeConstGlobal_string, "InitializeConstGlobal") \
V(KeyedLoadElementMonomorphic_string, \

View File

@ -41,6 +41,9 @@ class BasicJsonStringifier BASE_EMBEDDED {
MaybeObject* Stringify(Handle<Object> object);
INLINE(static MaybeObject* StringifyString(Isolate* isolate,
Handle<String> object));
private:
static const int kInitialPartLength = 32;
static const int kMaxPartLength = 16 * 1024;
@ -84,6 +87,11 @@ class BasicJsonStringifier BASE_EMBEDDED {
bool deferred_comma,
bool deferred_key);
template <typename StringType>
INLINE(static MaybeObject* StringifyString_(Isolate* isolate,
Handle<String> string,
Handle<String> result));
// Entry point to serialize the object.
INLINE(Result SerializeObject(Handle<Object> obj)) {
return Serialize_<false>(obj, false, factory_->empty_string());
@ -135,18 +143,18 @@ class BasicJsonStringifier BASE_EMBEDDED {
void SerializeString(Handle<String> object);
template <typename SrcChar, typename DestChar>
INLINE(void SerializeStringUnchecked_(const SrcChar* src,
DestChar* dest,
int length));
INLINE(static int SerializeStringUnchecked_(const SrcChar* src,
DestChar* dest,
int length));
template <bool is_ascii, typename Char>
INLINE(void SerializeString_(Handle<String> string));
template <typename Char>
INLINE(bool DoNotEscape(Char c));
INLINE(static bool DoNotEscape(Char c));
template <typename Char>
INLINE(Vector<const Char> GetCharVector(Handle<String> string));
INLINE(static Vector<const Char> GetCharVector(Handle<String> string));
Result StackPush(Handle<Object> object);
void StackPop();
@ -244,15 +252,15 @@ const char* const BasicJsonStringifier::JsonEscapeTable =
"\370\0 \371\0 \372\0 \373\0 "
"\374\0 \375\0 \376\0 \377\0 ";
BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
: isolate_(isolate), current_index_(0), is_ascii_(true) {
factory_ = isolate_->factory();
accumulator_store_ = Handle<JSValue>::cast(
factory_->ToObject(factory_->empty_string()));
part_length_ = kInitialPartLength;
current_part_ = factory_->NewRawOneByteString(kInitialPartLength);
tojson_string_ =
factory_->InternalizeOneByteString(STATIC_ASCII_VECTOR("toJSON"));
current_part_ = factory_->NewRawOneByteString(part_length_);
tojson_string_ = factory_->toJSON_string();
stack_ = factory_->NewJSArray(8);
}
@ -275,6 +283,57 @@ MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> object) {
}
MaybeObject* BasicJsonStringifier::StringifyString(Isolate* isolate,
Handle<String> object) {
static const int kJsonQuoteWorstCaseBlowup = 6;
static const int kSpaceForQuotes = 2;
int worst_case_length =
object->length() * kJsonQuoteWorstCaseBlowup + kSpaceForQuotes;
if (worst_case_length > 32 * KB) { // Slow path if too large.
BasicJsonStringifier stringifier(isolate);
return stringifier.Stringify(object);
}
object = FlattenGetString(object);
if (object->IsSeqOneByteString()) {
return StringifyString_<SeqOneByteString>(
isolate,
object,
isolate->factory()->NewRawOneByteString(worst_case_length));
} else {
return StringifyString_<SeqTwoByteString>(
isolate,
object,
isolate->factory()->NewRawTwoByteString(worst_case_length));
}
}
template <typename StringType>
MaybeObject* BasicJsonStringifier::StringifyString_(Isolate* isolate,
Handle<String> string,
Handle<String> result) {
AssertNoAllocation no_allocation;
int final_size = 0;
StringType* dest = StringType::cast(*result);
dest->Set(final_size++, '\"');
final_size += SerializeStringUnchecked_(StringType::cast(*string)->GetChars(),
dest->GetChars() + 1,
string->length());
dest->Set(final_size++, '\"');
if (isolate->heap()->InNewSpace(*result)) {
// In new space, simply lower the allocation top to fit the actual size.
isolate->heap()->new_space()->ShrinkStringAtAllocationBoundary<StringType>(
*result, final_size);
return *result;
} else {
// Not in new space, need to fill the wasted space with filler objects.
return SeqString::cast(*result)->Truncate(final_size);
}
}
template <bool is_ascii, typename Char>
void BasicJsonStringifier::Append_(Char c) {
if (is_ascii) {
@ -667,10 +726,9 @@ void BasicJsonStringifier::ChangeEncoding() {
template <typename SrcChar, typename DestChar>
void BasicJsonStringifier::SerializeStringUnchecked_(const SrcChar* src,
DestChar* dest,
int length) {
dest += current_index_;
int BasicJsonStringifier::SerializeStringUnchecked_(const SrcChar* src,
DestChar* dest,
int length) {
DestChar* dest_start = dest;
// Assert that uc16 character is not truncated down to 8 bit.
@ -688,7 +746,7 @@ void BasicJsonStringifier::SerializeStringUnchecked_(const SrcChar* src,
}
}
current_index_ += static_cast<int>(dest - dest_start);
return static_cast<int>(dest - dest_start);
}
@ -705,14 +763,14 @@ void BasicJsonStringifier::SerializeString_(Handle<String> string) {
AssertNoAllocation no_allocation;
Vector<const Char> vector = GetCharVector<Char>(string);
if (is_ascii) {
SerializeStringUnchecked_(
current_index_ += SerializeStringUnchecked_(
vector.start(),
SeqOneByteString::cast(*current_part_)->GetChars(),
SeqOneByteString::cast(*current_part_)->GetChars() + current_index_,
length);
} else {
SerializeStringUnchecked_(
current_index_ += SerializeStringUnchecked_(
vector.start(),
SeqTwoByteString::cast(*current_part_)->GetChars(),
SeqTwoByteString::cast(*current_part_)->GetChars() + current_index_,
length);
}
} else {

View File

@ -5171,365 +5171,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_URIUnescape) {
}
static const unsigned int kQuoteTableLength = 128u;
static const int kJsonQuotesCharactersPerEntry = 8;
static const char* const JsonQuotes =
"\\u0000 \\u0001 \\u0002 \\u0003 "
"\\u0004 \\u0005 \\u0006 \\u0007 "
"\\b \\t \\n \\u000b "
"\\f \\r \\u000e \\u000f "
"\\u0010 \\u0011 \\u0012 \\u0013 "
"\\u0014 \\u0015 \\u0016 \\u0017 "
"\\u0018 \\u0019 \\u001a \\u001b "
"\\u001c \\u001d \\u001e \\u001f "
" ! \\\" # "
"$ % & ' "
"( ) * + "
", - . / "
"0 1 2 3 "
"4 5 6 7 "
"8 9 : ; "
"< = > ? "
"@ A B C "
"D E F G "
"H I J K "
"L M N O "
"P Q R S "
"T U V W "
"X Y Z [ "
"\\\\ ] ^ _ "
"` a b c "
"d e f g "
"h i j k "
"l m n o "
"p q r s "
"t u v w "
"x y z { "
"| } ~ \177 ";
// For a string that is less than 32k characters it should always be
// possible to allocate it in new space.
static const int kMaxGuaranteedNewSpaceString = 32 * 1024;
// Doing JSON quoting cannot make the string more than this many times larger.
static const int kJsonQuoteWorstCaseBlowup = 6;
static const int kSpaceForQuotesAndComma = 3;
static const int kSpaceForBrackets = 2;
// Covers the entire ASCII range (all other characters are unchanged by JSON
// quoting).
static const byte JsonQuoteLengths[kQuoteTableLength] = {
6, 6, 6, 6, 6, 6, 6, 6,
2, 2, 2, 6, 2, 2, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6,
1, 1, 2, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
};
template <typename StringType>
MaybeObject* AllocateRawString(Isolate* isolate, int length);
template <>
MaybeObject* AllocateRawString<SeqTwoByteString>(Isolate* isolate, int length) {
return isolate->heap()->AllocateRawTwoByteString(length);
}
template <>
MaybeObject* AllocateRawString<SeqOneByteString>(Isolate* isolate, int length) {
return isolate->heap()->AllocateRawOneByteString(length);
}
template <typename Char, typename StringType, bool comma>
static MaybeObject* SlowQuoteJsonString(Isolate* isolate,
Vector<const Char> characters) {
int length = characters.length();
const Char* read_cursor = characters.start();
const Char* end = read_cursor + length;
const int kSpaceForQuotes = 2 + (comma ? 1 :0);
int quoted_length = kSpaceForQuotes;
while (read_cursor < end) {
Char c = *(read_cursor++);
if (static_cast<unsigned>(c) >= kQuoteTableLength) {
quoted_length++;
} else {
quoted_length += JsonQuoteLengths[static_cast<unsigned>(c)];
}
}
MaybeObject* new_alloc = AllocateRawString<StringType>(isolate,
quoted_length);
Object* new_object;
if (!new_alloc->ToObject(&new_object)) {
return new_alloc;
}
StringType* new_string = StringType::cast(new_object);
Char* write_cursor = reinterpret_cast<Char*>(
new_string->address() + SeqString::kHeaderSize);
if (comma) *(write_cursor++) = ',';
*(write_cursor++) = '"';
read_cursor = characters.start();
while (read_cursor < end) {
Char c = *(read_cursor++);
if (static_cast<unsigned>(c) >= kQuoteTableLength) {
*(write_cursor++) = c;
} else {
int len = JsonQuoteLengths[static_cast<unsigned>(c)];
const char* replacement = JsonQuotes +
static_cast<unsigned>(c) * kJsonQuotesCharactersPerEntry;
for (int i = 0; i < len; i++) {
*write_cursor++ = *replacement++;
}
}
}
*(write_cursor++) = '"';
return new_string;
}
template <typename SinkChar, typename SourceChar>
static inline SinkChar* WriteQuoteJsonString(
Isolate* isolate,
SinkChar* write_cursor,
Vector<const SourceChar> characters) {
// SinkChar is only char if SourceChar is guaranteed to be char.
ASSERT(sizeof(SinkChar) >= sizeof(SourceChar));
const SourceChar* read_cursor = characters.start();
const SourceChar* end = read_cursor + characters.length();
*(write_cursor++) = '"';
while (read_cursor < end) {
SourceChar c = *(read_cursor++);
if (static_cast<unsigned>(c) >= kQuoteTableLength) {
*(write_cursor++) = static_cast<SinkChar>(c);
} else {
int len = JsonQuoteLengths[static_cast<unsigned>(c)];
const char* replacement = JsonQuotes +
static_cast<unsigned>(c) * kJsonQuotesCharactersPerEntry;
write_cursor[0] = replacement[0];
if (len > 1) {
write_cursor[1] = replacement[1];
if (len > 2) {
ASSERT(len == 6);
write_cursor[2] = replacement[2];
write_cursor[3] = replacement[3];
write_cursor[4] = replacement[4];
write_cursor[5] = replacement[5];
}
}
write_cursor += len;
}
}
*(write_cursor++) = '"';
return write_cursor;
}
template <typename Char, typename StringType, bool comma>
static MaybeObject* QuoteJsonString(Isolate* isolate,
Vector<const Char> characters) {
int length = characters.length();
isolate->counters()->quote_json_char_count()->Increment(length);
int worst_case_length =
length * kJsonQuoteWorstCaseBlowup + kSpaceForQuotesAndComma;
if (worst_case_length > kMaxGuaranteedNewSpaceString) {
return SlowQuoteJsonString<Char, StringType, comma>(isolate, characters);
}
MaybeObject* new_alloc = AllocateRawString<StringType>(isolate,
worst_case_length);
Object* new_object;
if (!new_alloc->ToObject(&new_object)) {
return new_alloc;
}
if (!isolate->heap()->new_space()->Contains(new_object)) {
// Even if our string is small enough to fit in new space we still have to
// handle it being allocated in old space as may happen in the third
// attempt. See CALL_AND_RETRY in heap-inl.h and similar code in
// CEntryStub::GenerateCore.
return SlowQuoteJsonString<Char, StringType, comma>(isolate, characters);
}
StringType* new_string = StringType::cast(new_object);
ASSERT(isolate->heap()->new_space()->Contains(new_string));
Char* write_cursor = reinterpret_cast<Char*>(
new_string->address() + SeqString::kHeaderSize);
if (comma) *(write_cursor++) = ',';
write_cursor = WriteQuoteJsonString<Char, Char>(isolate,
write_cursor,
characters);
int final_length = static_cast<int>(
write_cursor - reinterpret_cast<Char*>(
new_string->address() + SeqString::kHeaderSize));
isolate->heap()->new_space()->
template ShrinkStringAtAllocationBoundary<StringType>(
new_string, final_length);
return new_string;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONString) {
NoHandleAllocation ha(isolate);
CONVERT_ARG_CHECKED(String, str, 0);
if (!str->IsFlat()) {
MaybeObject* try_flatten = str->TryFlatten();
Object* flat;
if (!try_flatten->ToObject(&flat)) {
return try_flatten;
}
str = String::cast(flat);
ASSERT(str->IsFlat());
}
String::FlatContent flat = str->GetFlatContent();
ASSERT(flat.IsFlat());
if (flat.IsTwoByte()) {
return QuoteJsonString<uc16, SeqTwoByteString, false>(isolate,
flat.ToUC16Vector());
} else {
return QuoteJsonString<uint8_t, SeqOneByteString, false>(
isolate,
flat.ToOneByteVector());
}
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONStringComma) {
NoHandleAllocation ha(isolate);
CONVERT_ARG_CHECKED(String, str, 0);
if (!str->IsFlat()) {
MaybeObject* try_flatten = str->TryFlatten();
Object* flat;
if (!try_flatten->ToObject(&flat)) {
return try_flatten;
}
str = String::cast(flat);
ASSERT(str->IsFlat());
}
String::FlatContent flat = str->GetFlatContent();
if (flat.IsTwoByte()) {
return QuoteJsonString<uc16, SeqTwoByteString, true>(isolate,
flat.ToUC16Vector());
} else {
return QuoteJsonString<uint8_t, SeqOneByteString, true>(
isolate,
flat.ToOneByteVector());
}
}
template <typename Char, typename StringType>
static MaybeObject* QuoteJsonStringArray(Isolate* isolate,
FixedArray* array,
int worst_case_length) {
int length = array->length();
MaybeObject* new_alloc = AllocateRawString<StringType>(isolate,
worst_case_length);
Object* new_object;
if (!new_alloc->ToObject(&new_object)) {
return new_alloc;
}
if (!isolate->heap()->new_space()->Contains(new_object)) {
// Even if our string is small enough to fit in new space we still have to
// handle it being allocated in old space as may happen in the third
// attempt. See CALL_AND_RETRY in heap-inl.h and similar code in
// CEntryStub::GenerateCore.
return isolate->heap()->undefined_value();
}
AssertNoAllocation no_gc;
StringType* new_string = StringType::cast(new_object);
ASSERT(isolate->heap()->new_space()->Contains(new_string));
Char* write_cursor = reinterpret_cast<Char*>(
new_string->address() + SeqString::kHeaderSize);
*(write_cursor++) = '[';
for (int i = 0; i < length; i++) {
if (i != 0) *(write_cursor++) = ',';
String* str = String::cast(array->get(i));
String::FlatContent content = str->GetFlatContent();
ASSERT(content.IsFlat());
if (content.IsTwoByte()) {
write_cursor = WriteQuoteJsonString<Char, uc16>(isolate,
write_cursor,
content.ToUC16Vector());
} else {
write_cursor =
WriteQuoteJsonString<Char, uint8_t>(isolate,
write_cursor,
content.ToOneByteVector());
}
}
*(write_cursor++) = ']';
int final_length = static_cast<int>(
write_cursor - reinterpret_cast<Char*>(
new_string->address() + SeqString::kHeaderSize));
isolate->heap()->new_space()->
template ShrinkStringAtAllocationBoundary<StringType>(
new_string, final_length);
return new_string;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONStringArray) {
NoHandleAllocation ha(isolate);
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSArray, array, 0);
if (!array->HasFastObjectElements()) {
return isolate->heap()->undefined_value();
}
FixedArray* elements = FixedArray::cast(array->elements());
int n = elements->length();
bool ascii = true;
int total_length = 0;
for (int i = 0; i < n; i++) {
Object* elt = elements->get(i);
if (!elt->IsString()) return isolate->heap()->undefined_value();
String* element = String::cast(elt);
if (!element->IsFlat()) return isolate->heap()->undefined_value();
total_length += element->length();
if (ascii && element->IsTwoByteRepresentation()) {
ascii = false;
}
}
int worst_case_length =
kSpaceForBrackets + n * kSpaceForQuotesAndComma
+ total_length * kJsonQuoteWorstCaseBlowup;
if (worst_case_length > kMaxGuaranteedNewSpaceString) {
return isolate->heap()->undefined_value();
}
if (ascii) {
return QuoteJsonStringArray<char, SeqOneByteString>(isolate,
elements,
worst_case_length);
} else {
return QuoteJsonStringArray<uc16, SeqTwoByteString>(isolate,
elements,
worst_case_length);
}
return BasicJsonStringifier::StringifyString(isolate, string);
}

View File

@ -195,8 +195,6 @@ namespace internal {
F(ParseJson, 1, 1) \
F(BasicJSONStringify, 1, 1) \
F(QuoteJSONString, 1, 1) \
F(QuoteJSONStringComma, 1, 1) \
F(QuoteJSONStringArray, 1, 1) \
\
/* Strings */ \
F(StringCharCodeAt, 2, 1) \