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:
parent
79d18ea332
commit
64f34cb761
@ -102,9 +102,37 @@ class JsonParser BASE_EMBEDDED {
|
||||
Handle<String> ParseJsonString() {
|
||||
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() {
|
||||
return ScanJsonString<true>();
|
||||
}
|
||||
|
||||
template <bool is_internalized>
|
||||
Handle<String> ScanJsonString();
|
||||
// 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());
|
||||
Handle<JSObject> json_object =
|
||||
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_, '{');
|
||||
|
||||
bool transitioning = true;
|
||||
|
||||
AdvanceSkipWhitespace();
|
||||
if (c0_ != '}') {
|
||||
do {
|
||||
@ -339,24 +372,75 @@ Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() {
|
||||
c0_ = '"';
|
||||
#endif
|
||||
|
||||
Handle<String> key = ParseJsonInternalizedString();
|
||||
if (key.is_null() || c0_ != ':') return ReportUnexpectedCharacter();
|
||||
Handle<String> key;
|
||||
Handle<Object> value;
|
||||
|
||||
AdvanceSkipWhitespace();
|
||||
Handle<Object> value = ParseJsonValue();
|
||||
if (value.is_null()) return ReportUnexpectedCharacter();
|
||||
// Try to follow existing transitions as long as possible. Once we stop
|
||||
// transitioning, no transition can be found anymore.
|
||||
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)) {
|
||||
int index = json_object->LastAddedFieldIndex();
|
||||
json_object->FastPropertyAtPut(index, *value);
|
||||
Handle<Map> target = JSObject::FindTransitionToField(map, key);
|
||||
// If a transition was found, follow it and continue.
|
||||
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 {
|
||||
JSObject::SetLocalPropertyIgnoreAttributes(
|
||||
json_object, key, value, NONE);
|
||||
key = ParseJsonInternalizedString();
|
||||
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(','));
|
||||
if (c0_ != '}') {
|
||||
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();
|
||||
return scope.CloseAndEscape(json_object);
|
||||
@ -644,22 +728,32 @@ Handle<String> JsonParser<seq_ascii>::ScanJsonString() {
|
||||
uint32_t capacity = string_table->Capacity();
|
||||
uint32_t entry = StringTable::FirstProbe(hash, capacity);
|
||||
uint32_t count = 1;
|
||||
Handle<String> result;
|
||||
while (true) {
|
||||
Object* element = string_table->KeyAt(entry);
|
||||
if (element == isolate()->heap()->undefined_value()) {
|
||||
// Lookup failure.
|
||||
result = factory()->InternalizeOneByteString(
|
||||
seq_source_, position_, length);
|
||||
break;
|
||||
}
|
||||
if (element != isolate()->heap()->the_hole_value() &&
|
||||
String::cast(element)->IsOneByteEqualTo(string_vector)) {
|
||||
// Lookup success, update the current position.
|
||||
position_ = position;
|
||||
// Advance past the last '"'.
|
||||
AdvanceSkipWhitespace();
|
||||
return Handle<String>(String::cast(element), isolate());
|
||||
result = Handle<String>(String::cast(element), isolate());
|
||||
#ifdef DEBUG
|
||||
uint32_t hash_field =
|
||||
(hash << String::kHashShift) | String::kIsNotArrayIndexMask;
|
||||
ASSERT_EQ(static_cast<int>(result->Hash()),
|
||||
static_cast<int>(hash_field >> String::kHashShift));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
entry = StringTable::NextProbe(entry, count++, capacity);
|
||||
}
|
||||
position_ = position;
|
||||
// Advance past the last '"'.
|
||||
AdvanceSkipWhitespace();
|
||||
return result;
|
||||
}
|
||||
|
||||
int beg_pos = position_;
|
||||
@ -682,14 +776,10 @@ Handle<String> JsonParser<seq_ascii>::ScanJsonString() {
|
||||
}
|
||||
} while (c0_ != '"');
|
||||
int length = position_ - beg_pos;
|
||||
Handle<String> result;
|
||||
if (seq_ascii && is_internalized) {
|
||||
result = factory()->InternalizeOneByteString(seq_source_, beg_pos, length);
|
||||
} else {
|
||||
result = factory()->NewRawOneByteString(length, pretenure_);
|
||||
uint8_t* dest = SeqOneByteString::cast(*result)->GetChars();
|
||||
String::WriteToFlat(*source_, dest, beg_pos, position_);
|
||||
}
|
||||
Handle<String> result = factory()->NewRawOneByteString(length, pretenure_);
|
||||
uint8_t* dest = SeqOneByteString::cast(*result)->GetChars();
|
||||
String::WriteToFlat(*source_, dest, beg_pos, position_);
|
||||
|
||||
ASSERT_EQ('"', c0_);
|
||||
// Advance past the last '"'.
|
||||
AdvanceSkipWhitespace();
|
||||
|
@ -1488,22 +1488,59 @@ MaybeObject* JSObject::AddFastPropertyUsingMap(Map* map) {
|
||||
}
|
||||
|
||||
|
||||
bool JSObject::TryTransitionToField(Handle<JSObject> object,
|
||||
Handle<Name> key) {
|
||||
if (!object->map()->HasTransitionArray()) return false;
|
||||
Handle<Map> target;
|
||||
{
|
||||
AssertNoAllocation no_allocation;
|
||||
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));
|
||||
MaybeObject* JSObject::TransitionToMap(Map* map) {
|
||||
ASSERT(this->map()->inobject_properties() == map->inobject_properties());
|
||||
ElementsKind expected_kind = this->map()->elements_kind();
|
||||
if (map->elements_kind() != expected_kind) {
|
||||
MaybeObject* maybe_map = map->AsElementsKind(expected_kind);
|
||||
if (!maybe_map->To(&map)) return maybe_map;
|
||||
}
|
||||
JSObject::AddFastPropertyUsingMap(object, target);
|
||||
return true;
|
||||
int total_size =
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -2602,13 +2602,18 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
|
||||
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 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,
|
||||
Name* name_raw,
|
||||
Object* value_raw,
|
||||
|
@ -1770,10 +1770,13 @@ class JSObject: public JSReceiver {
|
||||
Handle<Object> value,
|
||||
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
|
||||
// return value indicates whether the transition was successful.
|
||||
static inline bool TryTransitionToField(Handle<JSObject> object,
|
||||
Handle<Name> key);
|
||||
static inline Handle<Map> FindTransitionToField(Handle<Map> map,
|
||||
Handle<Name> key);
|
||||
|
||||
inline int LastAddedFieldIndex();
|
||||
|
||||
@ -1781,6 +1784,8 @@ class JSObject: public JSReceiver {
|
||||
// passed map. This also extends the property backing store if necessary.
|
||||
static void AddFastPropertyUsingMap(Handle<JSObject> object, Handle<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.
|
||||
MUST_USE_RESULT MaybeObject* SetLocalPropertyIgnoreAttributes(
|
||||
@ -5244,6 +5249,7 @@ class Map: public HeapObject {
|
||||
Descriptor* descriptor,
|
||||
int index,
|
||||
TransitionFlag flag);
|
||||
MUST_USE_RESULT MaybeObject* AsElementsKind(ElementsKind kind);
|
||||
MUST_USE_RESULT MaybeObject* CopyAsElementsKind(ElementsKind kind,
|
||||
TransitionFlag flag);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user