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() {
|
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();
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user