Commit ee838901 authored by ulan's avatar ulan Committed by Commit bot

[heap] Expand verification of layout changes to non-JSObject objects.

On map change of an object this patch checks that
- either GC was notified about this change,
- or the change leaves the slot set of the object the same.

BUG=chromium:694255

Review-Url: https://codereview.chromium.org/2886223002
Cr-Commit-Position: refs/heads/master@{#45427}
parent 233b6926
...@@ -4293,14 +4293,47 @@ void Heap::NotifyObjectLayoutChange(HeapObject* object, ...@@ -4293,14 +4293,47 @@ void Heap::NotifyObjectLayoutChange(HeapObject* object,
} }
#ifdef VERIFY_HEAP #ifdef VERIFY_HEAP
// Helper class for collecting slot addresses.
class SlotCollectingVisitor final : public ObjectVisitor {
public:
void VisitPointers(HeapObject* host, Object** start, Object** end) override {
for (Object** p = start; p < end; p++) {
slots_.push_back(p);
}
}
int number_of_slots() { return static_cast<int>(slots_.size()); }
Object** slot(int i) { return slots_[i]; }
private:
std::vector<Object**> slots_;
};
void Heap::VerifyObjectLayoutChange(HeapObject* object, Map* new_map) { void Heap::VerifyObjectLayoutChange(HeapObject* object, Map* new_map) {
// Check that Heap::NotifyObjectLayout was called for object transitions // Check that Heap::NotifyObjectLayout was called for object transitions
// that are not safe for concurrent marking. // that are not safe for concurrent marking.
// If you see this check triggering for a freshly allocated object, // If you see this check triggering for a freshly allocated object,
// use object->set_map_after_allocation() to initialize its map. // use object->set_map_after_allocation() to initialize its map.
if (pending_layout_change_object_ == nullptr) { if (pending_layout_change_object_ == nullptr) {
DCHECK(!object->IsJSObject() || if (object->IsJSObject()) {
!object->map()->TransitionRequiresSynchronizationWithGC(new_map)); DCHECK(!object->map()->TransitionRequiresSynchronizationWithGC(new_map));
} else {
// Check that the set of slots before and after the transition match.
SlotCollectingVisitor old_visitor;
object->IterateFast(&old_visitor);
MapWord old_map_word = object->map_word();
// Temporarily set the new map to iterate new slots.
object->set_map_word(MapWord::FromMap(new_map));
SlotCollectingVisitor new_visitor;
object->IterateFast(&new_visitor);
// Restore the old map.
object->set_map_word(old_map_word);
DCHECK_EQ(new_visitor.number_of_slots(), old_visitor.number_of_slots());
for (int i = 0; i < new_visitor.number_of_slots(); i++) {
DCHECK_EQ(new_visitor.slot(i), old_visitor.slot(i));
}
}
} else { } else {
DCHECK_EQ(pending_layout_change_object_, object); DCHECK_EQ(pending_layout_change_object_, object);
pending_layout_change_object_ = nullptr; pending_layout_change_object_ = nullptr;
......
...@@ -1248,11 +1248,9 @@ class Heap { ...@@ -1248,11 +1248,9 @@ class Heap {
// The runtime uses this function to notify potentially unsafe object layout // The runtime uses this function to notify potentially unsafe object layout
// changes that require special synchronization with the concurrent marker. // changes that require special synchronization with the concurrent marker.
// A layout change is unsafe if
// - it removes a tagged in-object field.
// - it replaces a tagged in-objects field with an untagged in-object field.
void NotifyObjectLayoutChange(HeapObject* object, void NotifyObjectLayoutChange(HeapObject* object,
const DisallowHeapAllocation&); const DisallowHeapAllocation&);
#ifdef VERIFY_HEAP #ifdef VERIFY_HEAP
// This function checks that either // This function checks that either
// - the map transition is safe, // - the map transition is safe,
......
...@@ -873,8 +873,11 @@ void IncrementalMarking::VisitObject(Map* map, HeapObject* obj, int size) { ...@@ -873,8 +873,11 @@ void IncrementalMarking::VisitObject(Map* map, HeapObject* obj, int size) {
// 1. The object is a fixed array with the progress bar. // 1. The object is a fixed array with the progress bar.
// 2. The object is a JSObject that was colored black before // 2. The object is a JSObject that was colored black before
// unsafe layout change. // unsafe layout change.
// 3. The object is a string that was colored black before
// unsafe layout change.
if (!ObjectMarking::GreyToBlack<kAtomicity>(obj, marking_state(obj))) { if (!ObjectMarking::GreyToBlack<kAtomicity>(obj, marking_state(obj))) {
DCHECK(IsFixedArrayWithProgressBar(obj) || obj->IsJSObject()); DCHECK(IsFixedArrayWithProgressBar(obj) || obj->IsJSObject() ||
obj->IsString());
} }
DCHECK(ObjectMarking::IsBlack<kAtomicity>(obj, marking_state(obj))); DCHECK(ObjectMarking::IsBlack<kAtomicity>(obj, marking_state(obj)));
WhiteToGreyAndPush(map); WhiteToGreyAndPush(map);
......
...@@ -2476,6 +2476,7 @@ Handle<String> String::SlowFlatten(Handle<ConsString> cons, ...@@ -2476,6 +2476,7 @@ Handle<String> String::SlowFlatten(Handle<ConsString> cons,
bool String::MakeExternal(v8::String::ExternalStringResource* resource) { bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
DisallowHeapAllocation no_allocation;
// Externalizing twice leaks the external resource, so it's // Externalizing twice leaks the external resource, so it's
// prohibited by the API. // prohibited by the API.
DCHECK(!this->IsExternalString()); DCHECK(!this->IsExternalString());
...@@ -2498,7 +2499,9 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { ...@@ -2498,7 +2499,9 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
bool is_one_byte = this->IsOneByteRepresentation(); bool is_one_byte = this->IsOneByteRepresentation();
bool is_internalized = this->IsInternalizedString(); bool is_internalized = this->IsInternalizedString();
bool has_pointers = StringShape(this).IsIndirect(); bool has_pointers = StringShape(this).IsIndirect();
if (has_pointers) {
heap->NotifyObjectLayoutChange(this, no_allocation);
}
// Morph the string to an external string by replacing the map and // Morph the string to an external string by replacing the map and
// reinitializing the fields. This won't work if the space the existing // reinitializing the fields. This won't work if the space the existing
// string occupies is too small for a regular external string. // string occupies is too small for a regular external string.
...@@ -2544,6 +2547,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { ...@@ -2544,6 +2547,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) {
DisallowHeapAllocation no_allocation;
// Externalizing twice leaks the external resource, so it's // Externalizing twice leaks the external resource, so it's
// prohibited by the API. // prohibited by the API.
DCHECK(!this->IsExternalString()); DCHECK(!this->IsExternalString());
...@@ -2571,6 +2575,10 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { ...@@ -2571,6 +2575,10 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) {
bool is_internalized = this->IsInternalizedString(); bool is_internalized = this->IsInternalizedString();
bool has_pointers = StringShape(this).IsIndirect(); bool has_pointers = StringShape(this).IsIndirect();
if (has_pointers) {
heap->NotifyObjectLayoutChange(this, no_allocation);
}
// Morph the string to an external string by replacing the map and // Morph the string to an external string by replacing the map and
// reinitializing the fields. This won't work if the space the existing // reinitializing the fields. This won't work if the space the existing
// string occupies is too small for a regular external string. // string occupies is too small for a regular external string.
...@@ -17457,6 +17465,7 @@ void MakeStringThin(String* string, String* internalized, Isolate* isolate) { ...@@ -17457,6 +17465,7 @@ void MakeStringThin(String* string, String* internalized, Isolate* isolate) {
if (!string->IsInternalizedString()) { if (!string->IsInternalizedString()) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
isolate->heap()->NotifyObjectLayoutChange(string, no_gc);
bool one_byte = internalized->IsOneByteRepresentation(); bool one_byte = internalized->IsOneByteRepresentation();
Handle<Map> map = one_byte ? isolate->factory()->thin_one_byte_string_map() Handle<Map> map = one_byte ? isolate->factory()->thin_one_byte_string_map()
: isolate->factory()->thin_string_map(); : isolate->factory()->thin_string_map();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment