| Index: src/value-serializer.cc | 
| diff --git a/src/value-serializer.cc b/src/value-serializer.cc | 
| index a13252db91898c87e84a74313732e1aaf16abfe7..41cd5db227d59cea1e7915eb081ae5e27e3f175d 100644 | 
| --- a/src/value-serializer.cc | 
| +++ b/src/value-serializer.cc | 
| @@ -13,6 +13,7 @@ | 
| #include "src/isolate.h" | 
| #include "src/objects-inl.h" | 
| #include "src/objects.h" | 
| +#include "src/transitions.h" | 
|  | 
| namespace v8 { | 
| namespace internal { | 
| @@ -1062,6 +1063,46 @@ MaybeHandle<String> ValueDeserializer::ReadTwoByteString() { | 
| return string; | 
| } | 
|  | 
| +bool ValueDeserializer::ReadExpectedString(Handle<String> expected) { | 
| +  // In the case of failure, the position in the stream is reset. | 
| +  const uint8_t* original_position = position_; | 
| + | 
| +  SerializationTag tag; | 
| +  uint32_t byte_length; | 
| +  Vector<const uint8_t> bytes; | 
| +  if (!ReadTag().To(&tag) || !ReadVarint<uint32_t>().To(&byte_length) || | 
| +      byte_length > | 
| +          static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) || | 
| +      !ReadRawBytes(byte_length).To(&bytes)) { | 
| +    position_ = original_position; | 
| +    return false; | 
| +  } | 
| + | 
| +  expected = String::Flatten(expected); | 
| +  DisallowHeapAllocation no_gc; | 
| +  String::FlatContent flat = expected->GetFlatContent(); | 
| + | 
| +  // If the bytes are verbatim what is in the flattened string, then the string | 
| +  // is successfully consumed. | 
| +  if (tag == SerializationTag::kUtf8String && flat.IsOneByte()) { | 
| +    Vector<const uint8_t> chars = flat.ToOneByteVector(); | 
| +    if (byte_length == chars.length() && | 
| +        String::IsAscii(chars.begin(), chars.length()) && | 
| +        memcmp(bytes.begin(), chars.begin(), byte_length) == 0) { | 
| +      return true; | 
| +    } | 
| +  } else if (tag == SerializationTag::kTwoByteString && flat.IsTwoByte()) { | 
| +    Vector<const uc16> chars = flat.ToUC16Vector(); | 
| +    if (byte_length == static_cast<unsigned>(chars.length()) * sizeof(uc16) && | 
| +        memcmp(bytes.begin(), chars.begin(), byte_length) == 0) { | 
| +      return true; | 
| +    } | 
| +  } | 
| + | 
| +  position_ = original_position; | 
| +  return false; | 
| +} | 
| + | 
| MaybeHandle<JSObject> ValueDeserializer::ReadJSObject() { | 
| // If we are at the end of the stack, abort. This function may recurse. | 
| STACK_CHECK(isolate_, MaybeHandle<JSObject>()); | 
| @@ -1074,7 +1115,7 @@ MaybeHandle<JSObject> ValueDeserializer::ReadJSObject() { | 
|  | 
| uint32_t num_properties; | 
| uint32_t expected_num_properties; | 
| -  if (!ReadJSObjectProperties(object, SerializationTag::kEndJSObject) | 
| +  if (!ReadJSObjectProperties(object, SerializationTag::kEndJSObject, true) | 
| .To(&num_properties) || | 
| !ReadVarint<uint32_t>().To(&expected_num_properties) || | 
| num_properties != expected_num_properties) { | 
| @@ -1102,7 +1143,7 @@ MaybeHandle<JSArray> ValueDeserializer::ReadSparseJSArray() { | 
| uint32_t num_properties; | 
| uint32_t expected_num_properties; | 
| uint32_t expected_length; | 
| -  if (!ReadJSObjectProperties(array, SerializationTag::kEndSparseJSArray) | 
| +  if (!ReadJSObjectProperties(array, SerializationTag::kEndSparseJSArray, false) | 
| .To(&num_properties) || | 
| !ReadVarint<uint32_t>().To(&expected_num_properties) || | 
| !ReadVarint<uint32_t>().To(&expected_length) || | 
| @@ -1140,7 +1181,7 @@ MaybeHandle<JSArray> ValueDeserializer::ReadDenseJSArray() { | 
| uint32_t num_properties; | 
| uint32_t expected_num_properties; | 
| uint32_t expected_length; | 
| -  if (!ReadJSObjectProperties(array, SerializationTag::kEndDenseJSArray) | 
| +  if (!ReadJSObjectProperties(array, SerializationTag::kEndDenseJSArray, false) | 
| .To(&num_properties) || | 
| !ReadVarint<uint32_t>().To(&expected_num_properties) || | 
| !ReadVarint<uint32_t>().To(&expected_length) || | 
| @@ -1389,9 +1430,127 @@ MaybeHandle<JSObject> ValueDeserializer::ReadHostObject() { | 
| return js_object; | 
| } | 
|  | 
| +// Copies a vector of property values into an object, given the map that should | 
| +// be used. | 
| +static void CommitProperties(Handle<JSObject> object, Handle<Map> map, | 
| +                             const std::vector<Handle<Object>>& properties) { | 
| +  JSObject::AllocateStorageForMap(object, map); | 
| +  DCHECK(!object->map()->is_dictionary_map()); | 
| + | 
| +  DisallowHeapAllocation no_gc; | 
| +  DescriptorArray* descriptors = object->map()->instance_descriptors(); | 
| +  for (unsigned i = 0; i < properties.size(); i++) { | 
| +    object->WriteToField(i, descriptors->GetDetails(i), *properties[i]); | 
| +  } | 
| +} | 
| + | 
| Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties( | 
| -    Handle<JSObject> object, SerializationTag end_tag) { | 
| -  for (uint32_t num_properties = 0;; num_properties++) { | 
| +    Handle<JSObject> object, SerializationTag end_tag, | 
| +    bool can_use_transitions) { | 
| +  uint32_t num_properties = 0; | 
| + | 
| +  // Fast path (following map transitions). | 
| +  if (can_use_transitions) { | 
| +    bool transitioning = true; | 
| +    Handle<Map> map(object->map(), isolate_); | 
| +    DCHECK(!map->is_dictionary_map()); | 
| +    DCHECK(map->instance_descriptors()->IsEmpty()); | 
| +    std::vector<Handle<Object>> properties; | 
| +    properties.reserve(8); | 
| + | 
| +    while (transitioning) { | 
| +      // If there are no more properties, finish. | 
| +      SerializationTag tag; | 
| +      if (!PeekTag().To(&tag)) return Nothing<uint32_t>(); | 
| +      if (tag == end_tag) { | 
| +        ConsumeTag(end_tag); | 
| +        CommitProperties(object, map, properties); | 
| +        CHECK_LT(properties.size(), std::numeric_limits<uint32_t>::max()); | 
| +        return Just(static_cast<uint32_t>(properties.size())); | 
| +      } | 
| + | 
| +      // Determine the key to be used and the target map to transition to, if | 
| +      // possible. Transitioning may abort if the key is not a string, or if no | 
| +      // transition was found. | 
| +      Handle<Object> key; | 
| +      Handle<Map> target; | 
| +      Handle<String> expected_key = TransitionArray::ExpectedTransitionKey(map); | 
| +      if (!expected_key.is_null() && ReadExpectedString(expected_key)) { | 
| +        key = expected_key; | 
| +        target = TransitionArray::ExpectedTransitionTarget(map); | 
| +      } else { | 
| +        if (!ReadObject().ToHandle(&key)) return Nothing<uint32_t>(); | 
| +        if (key->IsString()) { | 
| +          key = | 
| +              isolate_->factory()->InternalizeString(Handle<String>::cast(key)); | 
| +          target = TransitionArray::FindTransitionToField( | 
| +              map, Handle<String>::cast(key)); | 
| +          transitioning = !target.is_null(); | 
| +        } else { | 
| +          transitioning = false; | 
| +        } | 
| +      } | 
| + | 
| +      // Read the value that corresponds to it. | 
| +      Handle<Object> value; | 
| +      if (!ReadObject().ToHandle(&value)) return Nothing<uint32_t>(); | 
| + | 
| +      // If still transitioning and the value fits the field representation | 
| +      // (though generalization may be required), store the property value so | 
| +      // that we can copy them all at once. Otherwise, stop transitioning. | 
| +      if (transitioning) { | 
| +        int descriptor = static_cast<int>(properties.size()); | 
| +        PropertyDetails details = | 
| +            target->instance_descriptors()->GetDetails(descriptor); | 
| +        Representation expected_representation = details.representation(); | 
| +        if (value->FitsRepresentation(expected_representation)) { | 
| +          if (expected_representation.IsHeapObject() && | 
| +              !target->instance_descriptors() | 
| +                   ->GetFieldType(descriptor) | 
| +                   ->NowContains(value)) { | 
| +            Handle<FieldType> value_type = | 
| +                value->OptimalType(isolate_, expected_representation); | 
| +            Map::GeneralizeFieldType(target, descriptor, | 
| +                                     expected_representation, value_type); | 
| +          } | 
| +          DCHECK(target->instance_descriptors() | 
| +                     ->GetFieldType(descriptor) | 
| +                     ->NowContains(value)); | 
| +          properties.push_back(value); | 
| +          map = target; | 
| +          continue; | 
| +        } else { | 
| +          transitioning = false; | 
| +        } | 
| +      } | 
| + | 
| +      // Fell out of transitioning fast path. Commit the properties gathered so | 
| +      // far, and then start setting properties slowly instead. | 
| +      DCHECK(!transitioning); | 
| +      CHECK_LT(properties.size(), std::numeric_limits<uint32_t>::max()); | 
| +      CommitProperties(object, map, properties); | 
| +      num_properties = static_cast<uint32_t>(properties.size()); | 
| + | 
| +      bool success; | 
| +      LookupIterator it = LookupIterator::PropertyOrElement( | 
| +          isolate_, object, key, &success, LookupIterator::OWN); | 
| +      if (!success || | 
| +          JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE) | 
| +              .is_null()) { | 
| +        return Nothing<uint32_t>(); | 
| +      } | 
| +      num_properties++; | 
| +    } | 
| + | 
| +    // At this point, transitioning should be done, but at least one property | 
| +    // should have been written (in the zero-property case, there is an early | 
| +    // return). | 
| +    DCHECK(!transitioning); | 
| +    DCHECK_GE(num_properties, 1u); | 
| +  } | 
| + | 
| +  // Slow path. | 
| +  for (;; num_properties++) { | 
| SerializationTag tag; | 
| if (!PeekTag().To(&tag)) return Nothing<uint32_t>(); | 
| if (tag == end_tag) { | 
|  |