Commit 8148f972 authored by yangguo@chromium.org's avatar yangguo@chromium.org

Stress GC less by allocating exponentially growing string chunks in JSON.stringify.

R=verwaest@chromium.org
BUG=

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12775 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent f9100525
...@@ -42,7 +42,9 @@ class BasicJsonStringifier BASE_EMBEDDED { ...@@ -42,7 +42,9 @@ class BasicJsonStringifier BASE_EMBEDDED {
MaybeObject* Stringify(Handle<Object> object); MaybeObject* Stringify(Handle<Object> object);
private: private:
static const int kPartLength = 8 * 1024; static const int kInitialPartLength = 32;
static const int kMaxPartLength = 16 * 1024;
static const int kPartLengthGrowthFactor = 2;
static const int kStackLimit = 8 * 1024; static const int kStackLimit = 8 * 1024;
enum Result { UNCHANGED, SUCCESS, BAILOUT, CIRCULAR, STACK_OVERFLOW }; enum Result { UNCHANGED, SUCCESS, BAILOUT, CIRCULAR, STACK_OVERFLOW };
...@@ -130,12 +132,23 @@ class BasicJsonStringifier BASE_EMBEDDED { ...@@ -130,12 +132,23 @@ class BasicJsonStringifier BASE_EMBEDDED {
INLINE(Result StackPush(Handle<Object> object)); INLINE(Result StackPush(Handle<Object> object));
INLINE(void StackPop()); INLINE(void StackPop());
INLINE(Handle<String> accumulator()) {
return Handle<String>(String::cast(accumulator_store_->value()));
}
INLINE(void set_accumulator(Handle<String> string)) {
return accumulator_store_->set_value(*string);
}
Isolate* isolate_; Isolate* isolate_;
Handle<String> accumulator_; // 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_; Handle<String> current_part_;
Handle<String> tojson_symbol_; Handle<String> tojson_symbol_;
Handle<JSArray> stack_; Handle<JSArray> stack_;
int current_index_; int current_index_;
int part_length_;
bool is_ascii_; bool is_ascii_;
static const int kJsonQuotesCharactersPerEntry = 8; static const int kJsonQuotesCharactersPerEntry = 8;
...@@ -180,9 +193,11 @@ const char* const BasicJsonStringifier::JsonQuotes = ...@@ -180,9 +193,11 @@ const char* const BasicJsonStringifier::JsonQuotes =
BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate) BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
: isolate_(isolate), current_index_(0), is_ascii_(true) { : isolate_(isolate), current_index_(0), is_ascii_(true) {
accumulator_ = isolate_->factory()->empty_string(); accumulator_store_ = Handle<JSValue>::cast(
isolate_->factory()->ToObject(isolate_->factory()->empty_string()));
part_length_ = kInitialPartLength;
current_part_ = current_part_ =
isolate_->factory()->NewRawAsciiString(kPartLength); isolate_->factory()->NewRawAsciiString(kInitialPartLength);
tojson_symbol_ = isolate_->factory()->LookupAsciiSymbol("toJSON"); tojson_symbol_ = isolate_->factory()->LookupAsciiSymbol("toJSON");
stack_ = isolate_->factory()->NewJSArray(8); stack_ = isolate_->factory()->NewJSArray(8);
} }
...@@ -192,7 +207,7 @@ MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> object) { ...@@ -192,7 +207,7 @@ MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> object) {
switch (Serialize(object)) { switch (Serialize(object)) {
case SUCCESS: case SUCCESS:
ShrinkCurrentPart(); ShrinkCurrentPart();
return *isolate_->factory()->NewConsString(accumulator_, current_part_); return *isolate_->factory()->NewConsString(accumulator(), current_part_);
case UNCHANGED: case UNCHANGED:
return isolate_->heap()->undefined_value(); return isolate_->heap()->undefined_value();
case CIRCULAR: case CIRCULAR:
...@@ -215,7 +230,7 @@ void BasicJsonStringifier::Append_(Char c) { ...@@ -215,7 +230,7 @@ void BasicJsonStringifier::Append_(Char c) {
SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet( SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet(
current_index_++, c); current_index_++, c);
} }
if (current_index_ == kPartLength) Extend<is_ascii>(); if (current_index_ == part_length_) Extend<is_ascii>();
} }
...@@ -228,7 +243,7 @@ void BasicJsonStringifier::AppendUnchecked_(Char c) { ...@@ -228,7 +243,7 @@ void BasicJsonStringifier::AppendUnchecked_(Char c) {
SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet( SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet(
current_index_++, c); current_index_++, c);
} }
ASSERT(current_index_ < kPartLength); ASSERT(current_index_ < part_length_);
} }
...@@ -403,6 +418,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeDouble( ...@@ -403,6 +418,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeDouble(
BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray( BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray(
Handle<JSArray> object) { Handle<JSArray> object) {
HandleScope handle_scope(isolate_);
if (StackPush(object) == CIRCULAR) return CIRCULAR; if (StackPush(object) == CIRCULAR) return CIRCULAR;
int length = Smi::cast(object->length())->value(); int length = Smi::cast(object->length())->value();
Append('['); Append('[');
...@@ -460,12 +476,14 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray( ...@@ -460,12 +476,14 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray(
} }
Append(']'); Append(']');
StackPop(); StackPop();
current_part_ = handle_scope.CloseAndEscape(current_part_);
return SUCCESS; return SUCCESS;
} }
BasicJsonStringifier::Result BasicJsonStringifier::SerializeObject( BasicJsonStringifier::Result BasicJsonStringifier::SerializeObject(
Handle<JSObject> object) { Handle<JSObject> object) {
HandleScope handle_scope(isolate_);
Result stack_push = StackPush(object); Result stack_push = StackPush(object);
if (stack_push != SUCCESS) return stack_push; if (stack_push != SUCCESS) return stack_push;
if (object->IsJSGlobalProxy()) return BAILOUT; if (object->IsJSGlobalProxy()) return BAILOUT;
...@@ -502,12 +520,13 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeObject( ...@@ -502,12 +520,13 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeObject(
} }
Append('}'); Append('}');
StackPop(); StackPop();
current_part_ = handle_scope.CloseAndEscape(current_part_);
return SUCCESS; return SUCCESS;
} }
void BasicJsonStringifier::ShrinkCurrentPart() { void BasicJsonStringifier::ShrinkCurrentPart() {
ASSERT(current_index_ < kPartLength); ASSERT(current_index_ < part_length_);
if (current_index_ == 0) { if (current_index_ == 0) {
current_part_ = isolate_->factory()->empty_string(); current_part_ = isolate_->factory()->empty_string();
return; return;
...@@ -515,10 +534,10 @@ void BasicJsonStringifier::ShrinkCurrentPart() { ...@@ -515,10 +534,10 @@ void BasicJsonStringifier::ShrinkCurrentPart() {
int string_size, allocated_string_size; int string_size, allocated_string_size;
if (is_ascii_) { if (is_ascii_) {
allocated_string_size = SeqAsciiString::SizeFor(kPartLength); allocated_string_size = SeqAsciiString::SizeFor(part_length_);
string_size = SeqAsciiString::SizeFor(current_index_); string_size = SeqAsciiString::SizeFor(current_index_);
} else { } else {
allocated_string_size = SeqTwoByteString::SizeFor(kPartLength); allocated_string_size = SeqTwoByteString::SizeFor(part_length_);
string_size = SeqTwoByteString::SizeFor(current_index_); string_size = SeqTwoByteString::SizeFor(current_index_);
} }
...@@ -538,14 +557,17 @@ void BasicJsonStringifier::ShrinkCurrentPart() { ...@@ -538,14 +557,17 @@ void BasicJsonStringifier::ShrinkCurrentPart() {
template <bool is_ascii> template <bool is_ascii>
void BasicJsonStringifier::Extend() { void BasicJsonStringifier::Extend() {
accumulator_ = set_accumulator(
isolate_->factory()->NewConsString(accumulator_, current_part_); isolate_->factory()->NewConsString(accumulator(), current_part_));
if (part_length_ <= kMaxPartLength / kPartLengthGrowthFactor) {
part_length_ *= kPartLengthGrowthFactor;
}
if (is_ascii) { if (is_ascii) {
current_part_ = current_part_ =
isolate_->factory()->NewRawAsciiString(kPartLength); isolate_->factory()->NewRawAsciiString(part_length_);
} else { } else {
current_part_ = current_part_ =
isolate_->factory()->NewRawTwoByteString(kPartLength); isolate_->factory()->NewRawTwoByteString(part_length_);
} }
current_index_ = 0; current_index_ = 0;
} }
...@@ -553,10 +575,10 @@ void BasicJsonStringifier::Extend() { ...@@ -553,10 +575,10 @@ void BasicJsonStringifier::Extend() {
void BasicJsonStringifier::ChangeEncoding() { void BasicJsonStringifier::ChangeEncoding() {
ShrinkCurrentPart(); ShrinkCurrentPart();
accumulator_ = isolate_->factory()->NewConsString(accumulator_, set_accumulator(
current_part_); isolate_->factory()->NewConsString(accumulator(), current_part_));
current_part_ = current_part_ =
isolate_->factory()->NewRawTwoByteString(kPartLength); isolate_->factory()->NewRawTwoByteString(part_length_);
current_index_ = 0; current_index_ = 0;
is_ascii_ = false; is_ascii_ = false;
} }
...@@ -565,7 +587,7 @@ void BasicJsonStringifier::ChangeEncoding() { ...@@ -565,7 +587,7 @@ void BasicJsonStringifier::ChangeEncoding() {
template <bool is_ascii, typename Char> template <bool is_ascii, typename Char>
void BasicJsonStringifier::SerializeString_(Vector<const Char> vector) { void BasicJsonStringifier::SerializeString_(Vector<const Char> vector) {
int length = vector.length(); int length = vector.length();
if (current_index_ + (length << 3) < (kPartLength - 2)) { if (current_index_ + (length << 3) < (part_length_ - 2)) {
AppendUnchecked_<is_ascii, char>('"'); AppendUnchecked_<is_ascii, char>('"');
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
Char c = vector[i]; Char c = vector[i];
......
...@@ -268,11 +268,14 @@ assertEquals('"toJSON 123"', ...@@ -268,11 +268,14 @@ assertEquals('"toJSON 123"',
JSON.stringify({ toJSON : function() { return 'toJSON 123'; } })); JSON.stringify({ toJSON : function() { return 'toJSON 123'; } }));
assertEquals('{"a":321}', assertEquals('{"a":321}',
JSON.stringify({ a : { toJSON : function() { return 321; } } })); JSON.stringify({ a : { toJSON : function() { return 321; } } }));
var counter = 0;
assertEquals('{"getter":123}', assertEquals('{"getter":123}',
JSON.stringify({ get getter() { return 123; } })); JSON.stringify({ get getter() { counter++; return 123; } }));
assertEquals(1, counter);
assertEquals('{"a":"abc","b":"\u1234bc"}', assertEquals('{"a":"abc","b":"\u1234bc"}',
JSON.stringify({ a : "abc", b : "\u1234bc" })); JSON.stringify({ a : "abc", b : "\u1234bc" }));
var a = { a : 1, b : 2 }; var a = { a : 1, b : 2 };
delete a.a; delete a.a;
assertEquals('{"b":2}', JSON.stringify(a)); assertEquals('{"b":2}', JSON.stringify(a));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment