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; |