Eagerly parse expected transitions in JSON.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14191 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
verwaest@chromium.org 2013-04-09 16:49:28 +00:00
parent 79d18ea332
commit 64f34cb761
4 changed files with 188 additions and 43 deletions

View File

@ -102,9 +102,37 @@ class JsonParser BASE_EMBEDDED {
Handle<String> ParseJsonString() { Handle<String> ParseJsonString() {
return ScanJsonString<false>(); return ScanJsonString<false>();
} }
bool ParseJsonString(Handle<String> expected) {
int length = expected->length();
if (source_->length() - position_ - 1 > length) {
AssertNoAllocation no_gc;
String::FlatContent content = expected->GetFlatContent();
if (content.IsAscii()) {
ASSERT_EQ('"', c0_);
const uint8_t* input_chars = seq_source_->GetChars() + position_ + 1;
const uint8_t* expected_chars = content.ToOneByteVector().start();
for (int i = 0; i < length; i++) {
uint8_t c0 = input_chars[i];
if (c0 != expected_chars[i] ||
c0 == '"' || c0 < 0x20 || c0 == '\\') {
return false;
}
}
if (input_chars[length] == '"') {
position_ = position_ + length + 1;
AdvanceSkipWhitespace();
return true;
}
}
}
return false;
}
Handle<String> ParseJsonInternalizedString() { Handle<String> ParseJsonInternalizedString() {
return ScanJsonString<true>(); return ScanJsonString<true>();
} }
template <bool is_internalized> template <bool is_internalized>
Handle<String> ScanJsonString(); Handle<String> ScanJsonString();
// Creates a new string and copies prefix[start..end] into the beginning // Creates a new string and copies prefix[start..end] into the beginning
@ -294,8 +322,13 @@ Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() {
HandleScope scope(isolate()); HandleScope scope(isolate());
Handle<JSObject> json_object = Handle<JSObject> json_object =
factory()->NewJSObject(object_constructor(), pretenure_); factory()->NewJSObject(object_constructor(), pretenure_);
Handle<Map> map(json_object->map());
ZoneScope zone_scope(zone(), DELETE_ON_EXIT);
ZoneList<Handle<Object> > properties(8, zone());
ASSERT_EQ(c0_, '{'); ASSERT_EQ(c0_, '{');
bool transitioning = true;
AdvanceSkipWhitespace(); AdvanceSkipWhitespace();
if (c0_ != '}') { if (c0_ != '}') {
do { do {
@ -339,24 +372,75 @@ Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() {
c0_ = '"'; c0_ = '"';
#endif #endif
Handle<String> key = ParseJsonInternalizedString(); Handle<String> key;
if (key.is_null() || c0_ != ':') return ReportUnexpectedCharacter(); Handle<Object> value;
AdvanceSkipWhitespace(); // Try to follow existing transitions as long as possible. Once we stop
Handle<Object> value = ParseJsonValue(); // transitioning, no transition can be found anymore.
if (value.is_null()) return ReportUnexpectedCharacter(); if (transitioning) {
// First check whether there is a single expected transition. If so, try
// to parse it first.
bool follow_expected = false;
if (seq_ascii) {
key = JSObject::ExpectedTransitionKey(map);
follow_expected = !key.is_null() && ParseJsonString(key);
}
// If the expected transition hits, follow it.
if (follow_expected) {
map = JSObject::ExpectedTransitionTarget(map);
} else {
// If the expected transition failed, parse an internalized string and
// try to find a matching transition.
key = ParseJsonInternalizedString();
if (key.is_null()) return ReportUnexpectedCharacter();
if (JSObject::TryTransitionToField(json_object, key)) { Handle<Map> target = JSObject::FindTransitionToField(map, key);
int index = json_object->LastAddedFieldIndex(); // If a transition was found, follow it and continue.
json_object->FastPropertyAtPut(index, *value); if (!target.is_null()) {
map = target;
} else {
// If no transition was found, commit the intermediate state to the
// object and stop transitioning.
JSObject::TransitionToMap(json_object, map);
int length = properties.length();
for (int i = 0; i < length; i++) {
json_object->FastPropertyAtPut(i, *properties[i]);
}
transitioning = false;
}
}
if (c0_ != ':') return ReportUnexpectedCharacter();
AdvanceSkipWhitespace();
value = ParseJsonValue();
if (value.is_null()) return ReportUnexpectedCharacter();
properties.Add(value, zone());
if (transitioning) continue;
} else { } else {
JSObject::SetLocalPropertyIgnoreAttributes( key = ParseJsonInternalizedString();
json_object, key, value, NONE); if (key.is_null() || c0_ != ':') return ReportUnexpectedCharacter();
AdvanceSkipWhitespace();
value = ParseJsonValue();
if (value.is_null()) return ReportUnexpectedCharacter();
} }
JSObject::SetLocalPropertyIgnoreAttributes(
json_object, key, value, NONE);
} while (MatchSkipWhiteSpace(',')); } while (MatchSkipWhiteSpace(','));
if (c0_ != '}') { if (c0_ != '}') {
return ReportUnexpectedCharacter(); return ReportUnexpectedCharacter();
} }
// If we transitioned until the very end, transition the map now.
if (transitioning) {
JSObject::TransitionToMap(json_object, map);
int length = properties.length();
for (int i = 0; i < length; i++) {
json_object->FastPropertyAtPut(i, *properties[i]);
}
}
} }
AdvanceSkipWhitespace(); AdvanceSkipWhitespace();
return scope.CloseAndEscape(json_object); return scope.CloseAndEscape(json_object);
@ -644,22 +728,32 @@ Handle<String> JsonParser<seq_ascii>::ScanJsonString() {
uint32_t capacity = string_table->Capacity(); uint32_t capacity = string_table->Capacity();
uint32_t entry = StringTable::FirstProbe(hash, capacity); uint32_t entry = StringTable::FirstProbe(hash, capacity);
uint32_t count = 1; uint32_t count = 1;
Handle<String> result;
while (true) { while (true) {
Object* element = string_table->KeyAt(entry); Object* element = string_table->KeyAt(entry);
if (element == isolate()->heap()->undefined_value()) { if (element == isolate()->heap()->undefined_value()) {
// Lookup failure. // Lookup failure.
result = factory()->InternalizeOneByteString(
seq_source_, position_, length);
break; break;
} }
if (element != isolate()->heap()->the_hole_value() && if (element != isolate()->heap()->the_hole_value() &&
String::cast(element)->IsOneByteEqualTo(string_vector)) { String::cast(element)->IsOneByteEqualTo(string_vector)) {
// Lookup success, update the current position. result = Handle<String>(String::cast(element), isolate());
position_ = position; #ifdef DEBUG
// Advance past the last '"'. uint32_t hash_field =
AdvanceSkipWhitespace(); (hash << String::kHashShift) | String::kIsNotArrayIndexMask;
return Handle<String>(String::cast(element), isolate()); ASSERT_EQ(static_cast<int>(result->Hash()),
static_cast<int>(hash_field >> String::kHashShift));
#endif
break;
} }
entry = StringTable::NextProbe(entry, count++, capacity); entry = StringTable::NextProbe(entry, count++, capacity);
} }
position_ = position;
// Advance past the last '"'.
AdvanceSkipWhitespace();
return result;
} }
int beg_pos = position_; int beg_pos = position_;
@ -682,14 +776,10 @@ Handle<String> JsonParser<seq_ascii>::ScanJsonString() {
} }
} while (c0_ != '"'); } while (c0_ != '"');
int length = position_ - beg_pos; int length = position_ - beg_pos;
Handle<String> result; Handle<String> result = factory()->NewRawOneByteString(length, pretenure_);
if (seq_ascii && is_internalized) { uint8_t* dest = SeqOneByteString::cast(*result)->GetChars();
result = factory()->InternalizeOneByteString(seq_source_, beg_pos, length); String::WriteToFlat(*source_, dest, beg_pos, position_);
} else {
result = factory()->NewRawOneByteString(length, pretenure_);
uint8_t* dest = SeqOneByteString::cast(*result)->GetChars();
String::WriteToFlat(*source_, dest, beg_pos, position_);
}
ASSERT_EQ('"', c0_); ASSERT_EQ('"', c0_);
// Advance past the last '"'. // Advance past the last '"'.
AdvanceSkipWhitespace(); AdvanceSkipWhitespace();

View File

@ -1488,22 +1488,59 @@ MaybeObject* JSObject::AddFastPropertyUsingMap(Map* map) {
} }
bool JSObject::TryTransitionToField(Handle<JSObject> object, MaybeObject* JSObject::TransitionToMap(Map* map) {
Handle<Name> key) { ASSERT(this->map()->inobject_properties() == map->inobject_properties());
if (!object->map()->HasTransitionArray()) return false; ElementsKind expected_kind = this->map()->elements_kind();
Handle<Map> target; if (map->elements_kind() != expected_kind) {
{ MaybeObject* maybe_map = map->AsElementsKind(expected_kind);
AssertNoAllocation no_allocation; if (!maybe_map->To(&map)) return maybe_map;
TransitionArray* transitions = object->map()->transitions();
int transition = transitions->Search(*key);
if (transition == TransitionArray::kNotFound) return false;
PropertyDetails target_details = transitions->GetTargetDetails(transition);
if (target_details.type() != FIELD) return false;
if (target_details.attributes() != NONE) return false;
target = Handle<Map>(transitions->GetTarget(transition));
} }
JSObject::AddFastPropertyUsingMap(object, target); int total_size =
return true; map->NumberOfOwnDescriptors() + map->unused_property_fields();
int out_of_object = total_size - map->inobject_properties();
if (out_of_object != properties()->length()) {
FixedArray* new_properties;
MaybeObject* maybe_properties = properties()->CopySize(out_of_object);
if (!maybe_properties->To(&new_properties)) return maybe_properties;
set_properties(new_properties);
}
set_map(map);
return this;
}
Handle<String> JSObject::ExpectedTransitionKey(Handle<Map> map) {
AssertNoAllocation no_gc;
if (!map->HasTransitionArray()) return Handle<String>::null();
TransitionArray* transitions = map->transitions();
if (!transitions->IsSimpleTransition()) return Handle<String>::null();
int transition = TransitionArray::kSimpleTransitionIndex;
PropertyDetails details = transitions->GetTargetDetails(transition);
Name* name = transitions->GetKey(transition);
if (details.type() != FIELD) return Handle<String>::null();
if (details.attributes() != NONE) return Handle<String>::null();
if (!name->IsString()) return Handle<String>::null();
return Handle<String>(String::cast(name));
}
Handle<Map> JSObject::ExpectedTransitionTarget(Handle<Map> map) {
ASSERT(!ExpectedTransitionKey(map).is_null());
return Handle<Map>(map->transitions()->GetTarget(
TransitionArray::kSimpleTransitionIndex));
}
Handle<Map> JSObject::FindTransitionToField(Handle<Map> map, Handle<Name> key) {
AssertNoAllocation no_allocation;
if (!map->HasTransitionArray()) return Handle<Map>::null();
TransitionArray* transitions = map->transitions();
int transition = transitions->Search(*key);
if (transition == TransitionArray::kNotFound) return Handle<Map>::null();
PropertyDetails target_details = transitions->GetTargetDetails(transition);
if (target_details.type() != FIELD) return Handle<Map>::null();
if (target_details.attributes() != NONE) return Handle<Map>::null();
return Handle<Map>(transitions->GetTarget(transition));
} }

View File

@ -2602,13 +2602,18 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
return start_map->CopyAsElementsKind(to_kind, OMIT_TRANSITION); return start_map->CopyAsElementsKind(to_kind, OMIT_TRANSITION);
} }
Map* closest_map = FindClosestElementsTransition(start_map, to_kind); return start_map->AsElementsKind(to_kind);
}
if (closest_map->elements_kind() == to_kind) {
MaybeObject* Map::AsElementsKind(ElementsKind kind) {
Map* closest_map = FindClosestElementsTransition(this, kind);
if (closest_map->elements_kind() == kind) {
return closest_map; return closest_map;
} }
return AddMissingElementsTransitions(closest_map, to_kind); return AddMissingElementsTransitions(closest_map, kind);
} }
@ -3073,6 +3078,13 @@ void JSObject::AddFastPropertyUsingMap(Handle<JSObject> object,
} }
void JSObject::TransitionToMap(Handle<JSObject> object, Handle<Map> map) {
CALL_HEAP_FUNCTION_VOID(
object->GetIsolate(),
object->TransitionToMap(*map));
}
MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup, MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
Name* name_raw, Name* name_raw,
Object* value_raw, Object* value_raw,

View File

@ -1770,10 +1770,13 @@ class JSObject: public JSReceiver {
Handle<Object> value, Handle<Object> value,
PropertyAttributes attributes); PropertyAttributes attributes);
static inline Handle<String> ExpectedTransitionKey(Handle<Map> map);
static inline Handle<Map> ExpectedTransitionTarget(Handle<Map> map);
// Try to follow an existing transition to a field with attributes NONE. The // Try to follow an existing transition to a field with attributes NONE. The
// return value indicates whether the transition was successful. // return value indicates whether the transition was successful.
static inline bool TryTransitionToField(Handle<JSObject> object, static inline Handle<Map> FindTransitionToField(Handle<Map> map,
Handle<Name> key); Handle<Name> key);
inline int LastAddedFieldIndex(); inline int LastAddedFieldIndex();
@ -1781,6 +1784,8 @@ class JSObject: public JSReceiver {
// passed map. This also extends the property backing store if necessary. // passed map. This also extends the property backing store if necessary.
static void AddFastPropertyUsingMap(Handle<JSObject> object, Handle<Map> map); static void AddFastPropertyUsingMap(Handle<JSObject> object, Handle<Map> map);
inline MUST_USE_RESULT MaybeObject* AddFastPropertyUsingMap(Map* map); inline MUST_USE_RESULT MaybeObject* AddFastPropertyUsingMap(Map* map);
static void TransitionToMap(Handle<JSObject> object, Handle<Map> map);
inline MUST_USE_RESULT MaybeObject* TransitionToMap(Map* map);
// Can cause GC. // Can cause GC.
MUST_USE_RESULT MaybeObject* SetLocalPropertyIgnoreAttributes( MUST_USE_RESULT MaybeObject* SetLocalPropertyIgnoreAttributes(
@ -5244,6 +5249,7 @@ class Map: public HeapObject {
Descriptor* descriptor, Descriptor* descriptor,
int index, int index,
TransitionFlag flag); TransitionFlag flag);
MUST_USE_RESULT MaybeObject* AsElementsKind(ElementsKind kind);
MUST_USE_RESULT MaybeObject* CopyAsElementsKind(ElementsKind kind, MUST_USE_RESULT MaybeObject* CopyAsElementsKind(ElementsKind kind,
TransitionFlag flag); TransitionFlag flag);