| Index: src/objects.cc
 | 
| diff --git a/src/objects.cc b/src/objects.cc
 | 
| index 2755eaecfec8e3457ba10000499b779d4e1fbf99..1b8b5c5bdc847e1b61794292c5fa6cbb3cd78298 100644
 | 
| --- a/src/objects.cc
 | 
| +++ b/src/objects.cc
 | 
| @@ -1905,6 +1905,9 @@ void Map::ConnectElementsTransition(Handle<Map> parent, Handle<Map> child) {
 | 
|  void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
 | 
|                              int expected_additional_properties) {
 | 
|    if (object->map() == *new_map) return;
 | 
| +  // If this object is a prototype (the callee will check), invalidate any
 | 
| +  // prototype chains involving it.
 | 
| +  InvalidatePrototypeChains(object->map());
 | 
|    Handle<Map> old_map(object->map());
 | 
|    if (object->HasFastProperties()) {
 | 
|      if (!new_map->is_dictionary_map()) {
 | 
| @@ -1932,6 +1935,8 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
 | 
|      object->set_map(*new_map);
 | 
|    }
 | 
|    if (old_map->is_prototype_map()) {
 | 
| +    DCHECK(new_map->is_prototype_map());
 | 
| +    DCHECK(object->map() == *new_map);
 | 
|      new_map->set_prototype_info(old_map->prototype_info());
 | 
|      old_map->set_prototype_info(Smi::FromInt(0));
 | 
|    }
 | 
| @@ -4710,6 +4715,12 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
 | 
|    Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
 | 
|    new_map->set_dictionary_map(false);
 | 
|  
 | 
| +  if (object->map()->is_prototype_map()) {
 | 
| +    DCHECK(new_map->is_prototype_map());
 | 
| +    new_map->set_prototype_info(object->map()->prototype_info());
 | 
| +    object->map()->set_prototype_info(Smi::FromInt(0));
 | 
| +  }
 | 
| +
 | 
|  #if TRACE_MAPS
 | 
|    if (FLAG_trace_maps) {
 | 
|      PrintF("[TraceMaps: SlowToFast from= %p to= %p reason= %s ]\n",
 | 
| @@ -6895,9 +6906,20 @@ Handle<Map> Map::Normalize(Handle<Map> fast_map, PropertyNormalizationMode mode,
 | 
|        // applied to the shared map, dependent code and weak cell cache.
 | 
|        Handle<Map> fresh = Map::CopyNormalized(fast_map, mode);
 | 
|  
 | 
| -      DCHECK(memcmp(fresh->address(),
 | 
| -                    new_map->address(),
 | 
| -                    Map::kCodeCacheOffset) == 0);
 | 
| +      if (new_map->is_prototype_map()) {
 | 
| +        // For prototype maps, the PrototypeInfo is not copied.
 | 
| +        DCHECK(memcmp(fresh->address(), new_map->address(),
 | 
| +                      kTransitionsOrPrototypeInfoOffset) == 0);
 | 
| +        DCHECK(fresh->raw_transitions() == Smi::FromInt(0));
 | 
| +        STATIC_ASSERT(kDescriptorsOffset ==
 | 
| +                      kTransitionsOrPrototypeInfoOffset + kPointerSize);
 | 
| +        DCHECK(memcmp(HeapObject::RawField(*fresh, kDescriptorsOffset),
 | 
| +                      HeapObject::RawField(*new_map, kDescriptorsOffset),
 | 
| +                      kCodeCacheOffset - kDescriptorsOffset) == 0);
 | 
| +      } else {
 | 
| +        DCHECK(memcmp(fresh->address(), new_map->address(),
 | 
| +                      Map::kCodeCacheOffset) == 0);
 | 
| +      }
 | 
|        STATIC_ASSERT(Map::kDependentCodeOffset ==
 | 
|                      Map::kCodeCacheOffset + kPointerSize);
 | 
|        STATIC_ASSERT(Map::kWeakCellCacheOffset ==
 | 
| @@ -8228,8 +8250,8 @@ Handle<WeakFixedArray> WeakFixedArray::Add(
 | 
|      for (int i = 0; i < array->Length(); ++i) {
 | 
|        if (array->Get(i) == *value) return array;
 | 
|      }
 | 
| +#if 0  // Enable this if you want to check your search_for_duplicates flags.
 | 
|    } else {
 | 
| -#ifdef DEBUG
 | 
|      for (int i = 0; i < array->Length(); ++i) {
 | 
|        DCHECK_NE(*value, array->Get(i));
 | 
|      }
 | 
| @@ -10049,7 +10071,74 @@ void JSObject::UnregisterPrototypeUser(Handle<JSObject> prototype,
 | 
|  }
 | 
|  
 | 
|  
 | 
| +static void InvalidatePrototypeChainsInternal(Map* map) {
 | 
| +  if (!map->is_prototype_map()) return;
 | 
| +  Object* maybe_proto_info = map->prototype_info();
 | 
| +  if (!maybe_proto_info->IsPrototypeInfo()) return;
 | 
| +  PrototypeInfo* proto_info = PrototypeInfo::cast(maybe_proto_info);
 | 
| +  Object* maybe_cell = proto_info->validity_cell();
 | 
| +  if (maybe_cell->IsCell()) {
 | 
| +    // Just set the value; the cell will be replaced lazily.
 | 
| +    Cell* cell = Cell::cast(maybe_cell);
 | 
| +    cell->set_value(Smi::FromInt(Map::kPrototypeChainInvalid));
 | 
| +  }
 | 
| +
 | 
| +  Object* maybe_array = proto_info->prototype_users();
 | 
| +  if (!maybe_array->IsWeakFixedArray()) return;
 | 
| +
 | 
| +  WeakFixedArray* users = WeakFixedArray::cast(maybe_array);
 | 
| +  for (int i = 0; i < users->Length(); ++i) {
 | 
| +    Object* maybe_user = users->Get(i);
 | 
| +    if (maybe_user->IsSmi()) continue;
 | 
| +
 | 
| +    // For now, only maps register themselves as users.
 | 
| +    Map* user = Map::cast(maybe_user);
 | 
| +    // Walk the prototype chain (backwards, towards leaf objects) if necessary.
 | 
| +    InvalidatePrototypeChainsInternal(user);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +// static
 | 
| +void JSObject::InvalidatePrototypeChains(Map* map) {
 | 
| +  if (!FLAG_eliminate_prototype_chain_checks) return;
 | 
| +  DisallowHeapAllocation no_gc;
 | 
| +  if (map->IsJSGlobalProxyMap()) {
 | 
| +    PrototypeIterator iter(map);
 | 
| +    map = JSObject::cast(iter.GetCurrent())->map();
 | 
| +  }
 | 
| +  InvalidatePrototypeChainsInternal(map);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  // static
 | 
| +Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
 | 
| +                                                        Isolate* isolate) {
 | 
| +  Handle<Object> maybe_prototype(map->prototype(), isolate);
 | 
| +  if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null();
 | 
| +  Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype);
 | 
| +  if (prototype->IsJSGlobalProxy()) {
 | 
| +    PrototypeIterator iter(isolate, prototype);
 | 
| +    prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
 | 
| +  }
 | 
| +  PrototypeInfo* proto_info =
 | 
| +      PrototypeInfo::cast(prototype->map()->prototype_info());
 | 
| +  Object* maybe_cell = proto_info->validity_cell();
 | 
| +  // Return existing cell if it's still valid.
 | 
| +  if (maybe_cell->IsCell()) {
 | 
| +    Handle<Cell> cell(Cell::cast(maybe_cell), isolate);
 | 
| +    if (cell->value() == Smi::FromInt(Map::kPrototypeChainValid)) {
 | 
| +      return handle(Cell::cast(maybe_cell), isolate);
 | 
| +    }
 | 
| +  }
 | 
| +  // Otherwise create a new cell.
 | 
| +  Handle<Cell> cell = isolate->factory()->NewCell(
 | 
| +      handle(Smi::FromInt(Map::kPrototypeChainValid), isolate));
 | 
| +  proto_info->set_validity_cell(*cell);
 | 
| +  return cell;
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype,
 | 
|                         PrototypeOptimizationMode proto_mode) {
 | 
|    if (map->prototype()->IsJSObject() && FLAG_track_prototype_users) {
 | 
| @@ -10058,10 +10147,10 @@ void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype,
 | 
|    }
 | 
|    if (prototype->IsJSObject()) {
 | 
|      Handle<JSObject> prototype_jsobj = Handle<JSObject>::cast(prototype);
 | 
| +    JSObject::OptimizeAsPrototype(prototype_jsobj, proto_mode);
 | 
|      if (ShouldRegisterAsPrototypeUser(map, prototype_jsobj)) {
 | 
|        JSObject::RegisterPrototypeUser(prototype_jsobj, map);
 | 
|      }
 | 
| -    JSObject::OptimizeAsPrototype(prototype_jsobj, proto_mode);
 | 
|    }
 | 
|    WriteBarrierMode wb_mode =
 | 
|        prototype->IsNull() ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER;
 | 
| 
 |