Chromium Code Reviews| Index: src/objects-debug.cc |
| diff --git a/src/objects-debug.cc b/src/objects-debug.cc |
| index 13342ca36a405b187a862c54dca11a972d085f9e..615c8b65e05546dec063a8c56e4c2d9066684a21 100644 |
| --- a/src/objects-debug.cc |
| +++ b/src/objects-debug.cc |
| @@ -100,13 +100,15 @@ void HeapObject::HeapObjectVerify() { |
| break; |
| case JS_OBJECT_TYPE: |
| case JS_ERROR_TYPE: |
| - case JS_ARGUMENTS_TYPE: |
| case JS_API_OBJECT_TYPE: |
| case JS_SPECIAL_API_OBJECT_TYPE: |
| case JS_CONTEXT_EXTENSION_OBJECT_TYPE: |
| case JS_PROMISE_TYPE: |
| JSObject::cast(this)->JSObjectVerify(); |
| break; |
| + case JS_ARGUMENTS_TYPE: |
| + JSArgumentsObject::cast(this)->JSArgumentsObjectVerify(); |
| + break; |
| case JS_GENERATOR_OBJECT_TYPE: |
| JSGeneratorObject::cast(this)->JSGeneratorObjectVerify(); |
| break; |
| @@ -246,9 +248,9 @@ void FreeSpace::FreeSpaceVerify() { |
| template <class Traits> |
| void FixedTypedArray<Traits>::FixedTypedArrayVerify() { |
| - CHECK(IsHeapObject() && |
| - HeapObject::cast(this)->map()->instance_type() == |
| - Traits::kInstanceType); |
| + CHECK(IsHeapObject()); |
| + CHECK(HeapObject::cast(this)->map()->instance_type() == |
| + Traits::kInstanceType); |
| if (base_pointer() == this) { |
| CHECK(external_pointer() == |
| ExternalReference::fixed_typed_array_base_data_offset().address()); |
| @@ -265,76 +267,145 @@ bool JSObject::ElementsAreSafeToExamine() { |
| GetHeap()->one_pointer_filler_map(); |
| } |
| - |
| -void JSObject::JSObjectVerify() { |
| - VerifyHeapPointer(properties()); |
| - VerifyHeapPointer(elements()); |
| - |
| - if (HasSloppyArgumentsElements()) { |
| - CHECK(this->elements()->IsFixedArray()); |
| - CHECK_GE(this->elements()->length(), 2); |
| +namespace { |
|
Igor Sheludko
2016/07/08 08:38:22
Add a blank line after this line.
Camillo Bruni
2016/07/11 11:46:38
done
|
| +void VerifyFastProperties(JSReceiver* receiver) { |
| + // TODO(cbruni): JSProxy support for slow properties |
| + if (!receiver->IsJSObject()) return; |
| + Isolate* isolate = receiver->GetIsolate(); |
| + Map* map = receiver->map(); |
| + JSObject* obj = JSObject::cast(receiver); |
| + int actual_unused_property_fields = map->GetInObjectProperties() + |
| + obj->properties()->length() - |
| + map->NextFreePropertyIndex(); |
| + if (map->unused_property_fields() != actual_unused_property_fields) { |
| + // This could actually happen in the middle of StoreTransitionStub |
| + // when the new extended backing store is already set into the object and |
| + // the allocation of the MutableHeapNumber triggers GC (in this case map |
| + // is not updated yet). |
| + CHECK_EQ(map->unused_property_fields(), |
| + actual_unused_property_fields - JSObject::kFieldsAdded); |
| } |
| - |
| - if (HasFastProperties()) { |
| - int actual_unused_property_fields = map()->GetInObjectProperties() + |
| - properties()->length() - |
| - map()->NextFreePropertyIndex(); |
| - if (map()->unused_property_fields() != actual_unused_property_fields) { |
| - // This could actually happen in the middle of StoreTransitionStub |
| - // when the new extended backing store is already set into the object and |
| - // the allocation of the MutableHeapNumber triggers GC (in this case map |
| - // is not updated yet). |
| - CHECK_EQ(map()->unused_property_fields(), |
| - actual_unused_property_fields - JSObject::kFieldsAdded); |
| + DescriptorArray* descriptors = map->instance_descriptors(); |
| + for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) { |
| + if (descriptors->GetDetails(i).type() != DATA) continue; |
| + Representation r = descriptors->GetDetails(i).representation(); |
| + FieldIndex index = FieldIndex::ForDescriptor(map, i); |
| + if (obj->IsUnboxedDoubleField(index)) { |
| + DCHECK(r.IsDouble()); |
| + continue; |
| } |
| - DescriptorArray* descriptors = map()->instance_descriptors(); |
| - Isolate* isolate = GetIsolate(); |
| - for (int i = 0; i < map()->NumberOfOwnDescriptors(); i++) { |
| - if (descriptors->GetDetails(i).type() == DATA) { |
| - Representation r = descriptors->GetDetails(i).representation(); |
| - FieldIndex index = FieldIndex::ForDescriptor(map(), i); |
| - if (IsUnboxedDoubleField(index)) { |
| - DCHECK(r.IsDouble()); |
| - continue; |
| - } |
| - Object* value = RawFastPropertyAt(index); |
| - if (r.IsDouble()) DCHECK(value->IsMutableHeapNumber()); |
| - if (value->IsUninitialized(isolate)) continue; |
| - if (r.IsSmi()) DCHECK(value->IsSmi()); |
| - if (r.IsHeapObject()) DCHECK(value->IsHeapObject()); |
| - FieldType* field_type = descriptors->GetFieldType(i); |
| - bool type_is_none = field_type->IsNone(); |
| - bool type_is_any = field_type->IsAny(); |
| - if (r.IsNone()) { |
| - CHECK(type_is_none); |
| - } else if (!type_is_any && !(type_is_none && r.IsHeapObject())) { |
| - // If allocation folding is off then GC could happen during inner |
| - // object literal creation and we will end up having and undefined |
| - // value that does not match the field type. |
| - CHECK(!field_type->NowStable() || field_type->NowContains(value) || |
| - (!FLAG_use_allocation_folding && value->IsUndefined(isolate))); |
| - } |
| - } |
| + Object* value = obj->RawFastPropertyAt(index); |
| + if (r.IsDouble()) DCHECK(value->IsMutableHeapNumber()); |
| + if (value->IsUninitialized(isolate)) continue; |
| + if (r.IsSmi()) DCHECK(value->IsSmi()); |
| + if (r.IsHeapObject()) DCHECK(value->IsHeapObject()); |
| + FieldType* field_type = descriptors->GetFieldType(i); |
| + bool type_is_none = field_type->IsNone(); |
| + bool type_is_any = field_type->IsAny(); |
| + if (r.IsNone()) { |
| + CHECK(type_is_none); |
| + } else if (!type_is_any && !(type_is_none && r.IsHeapObject())) { |
| + // If allocation folding is off then GC could happen during inner |
| + // object literal creation and we will end up having and undefined |
| + // value that does not match the field type. |
| + CHECK(!field_type->NowStable() || field_type->NowContains(value) || |
| + (!FLAG_use_allocation_folding && value->IsUndefined(isolate))); |
| } |
| } |
| +} |
| +void VerifySlowProperties(JSReceiver* obj) { |
| + CHECK(obj->properties()->IsDictionary()); |
| +} |
| + |
| +void VerifyProperties(JSReceiver* obj) { |
| + obj->VerifyHeapPointer(obj->properties()); |
| + if (obj->HasFastProperties()) { |
| + VerifyFastProperties(obj); |
| + } else { |
|
Igor Sheludko
2016/07/08 08:38:22
Instead of VerifySlowProperties:
} else if (obj->
Camillo Bruni
2016/07/11 11:46:39
Kept the separate method to do further checks but
|
| + VerifySlowProperties(obj); |
| + } |
| +} |
| + |
| +template <typename T> |
| +void VerifyDictionary(T* dict) { |
| + CHECK(dict->IsFixedArray()); |
| + CHECK(dict->IsDictionary()); |
| + int capacity = dict->Capacity(); |
| + int nof = dict->NumberOfElements(); |
| + int nod = dict->NumberOfDeletedElements(); |
| + CHECK_LE(capacity, dict->length()); |
| + CHECK_LE(nof, capacity); |
| + CHECK_LE(0, nof + nod); |
| + CHECK_LE(nof + nod, capacity); |
| +} |
| + |
| +void VerifyElements(JSObject* obj) { |
| + obj->VerifyHeapPointer(obj->elements()); |
| // If a GC was caused while constructing this object, the elements |
| // pointer may point to a one pointer filler map. |
| - if (ElementsAreSafeToExamine()) { |
| - CHECK_EQ((map()->has_fast_smi_or_object_elements() || |
| - (elements() == GetHeap()->empty_fixed_array()) || |
| - HasFastStringWrapperElements()), |
| - (elements()->map() == GetHeap()->fixed_array_map() || |
| - elements()->map() == GetHeap()->fixed_cow_array_map())); |
| - CHECK(map()->has_fast_object_elements() == HasFastObjectElements()); |
| + if (!obj->ElementsAreSafeToExamine()) return; |
| + Map* map = obj->map(); |
| + Heap* heap = obj->GetHeap(); |
| + FixedArrayBase* elements = obj->elements(); |
| + CHECK_EQ((map->has_fast_smi_or_object_elements() || |
| + (elements == heap->empty_fixed_array()) || |
| + obj->HasFastStringWrapperElements()), |
| + (elements->map() == heap->fixed_array_map() || |
| + elements->map() == heap->fixed_cow_array_map())); |
| + CHECK(map->has_fast_object_elements() == obj->HasFastObjectElements()); |
|
Igor Sheludko
2016/07/08 08:38:22
CHECK_EQ?
Camillo Bruni
2016/07/11 11:46:39
copy pasta
|
| + |
| + // If a GC was caused while constructing this object, the elements |
|
Igor Sheludko
2016/07/08 08:38:22
I think this comment belongs only to ElementAreSaf
Camillo Bruni
2016/07/11 11:46:39
right.
|
| + // pointer may point to a one pointer filler map. |
| + ElementsKind kind = obj->GetElementsKind(); |
| + if (IsFastSmiOrObjectElementsKind(kind)) { |
| + CHECK(elements->map() == heap->fixed_array_map() || |
| + elements->map() == heap->fixed_cow_array_map()); |
| + } else if (IsFastDoubleElementsKind(kind)) { |
| + CHECK(elements->IsFixedDoubleArray() || |
| + elements == heap->empty_fixed_array()); |
| + } else if (kind == DICTIONARY_ELEMENTS) { |
| + CHECK(elements->IsSeededNumberDictionary()); |
| + VerifyDictionary(SeededNumberDictionary::cast(elements)); |
|
Igor Sheludko
2016/07/08 08:38:22
I think this check also applies to SLOW_SLOPPY_ARG
Camillo Bruni
2016/07/11 11:46:39
eventually I will have to put a switch statement h
|
| + return; |
| + } else { |
| + CHECK(kind > DICTIONARY_ELEMENTS); |
| + } |
| + CHECK(!IsSloppyArgumentsElements(kind) || |
| + (elements->IsFixedArray() && elements->length() >= 2)); |
| + |
| + if (IsFastPackedElementsKind(kind)) { |
| + uint32_t length = elements->length(); |
| + if (obj->IsJSArray()) { |
| + Object* number = JSArray::cast(obj)->length(); |
| + if (number->IsSmi()) { |
| + length = Min(length, static_cast<uint32_t>(Smi::cast(number)->value())); |
| + } |
| + } |
| + if (kind == FAST_DOUBLE_ELEMENTS) { |
| + for (uint32_t i = 0; i < length; i++) { |
| + CHECK(!FixedDoubleArray::cast(elements)->is_the_hole(i)); |
| + } |
| + } else { |
| + for (uint32_t i = 0; i < length; i++) { |
| + CHECK_NE(FixedArray::cast(elements)->get(i), heap->the_hole_value()); |
| + } |
| + } |
| } |
| } |
| +} // namespace |
| + |
| +void JSObject::JSObjectVerify() { |
| + VerifyProperties(this); |
| + VerifyElements(this); |
| +} |
| void Map::MapVerify() { |
| Heap* heap = GetHeap(); |
| CHECK(!heap->InNewSpace(this)); |
| - CHECK(FIRST_TYPE <= instance_type() && instance_type() <= LAST_TYPE); |
| + CHECK(FIRST_TYPE <= instance_type()); |
|
Igor Sheludko
2016/07/08 08:38:22
CHECK_LE?
Camillo Bruni
2016/07/11 11:46:39
done.
|
| + CHECK(instance_type() <= LAST_TYPE); |
| CHECK(instance_size() == kVariableSizeSentinel || |
| (kPointerSize <= instance_size() && |
| instance_size() < heap->Capacity())); |
| @@ -426,6 +497,37 @@ void JSGeneratorObject::JSGeneratorObjectVerify() { |
| VerifyObjectField(kReceiverOffset); |
| VerifyObjectField(kOperandStackOffset); |
| VerifyObjectField(kContinuationOffset); |
| + JSObjectVerify(); |
| +} |
| + |
| +void JSArgumentsObject::JSArgumentsObjectVerify() { |
| + VerifyObjectField(kLengthOffset); |
| + JSObjectVerify(); |
| + Isolate* isolate = GetIsolate(); |
| + |
| + // TODO(cbruni): replace with NULL check once all hydrogren stubs manage to |
| + // set up the contexts properly |
| + if (isolate->context()->IsSmi()) return; |
| + if (isolate->bootstrapper()->IsActive()) return; |
| + if (map() == *isolate->sloppy_arguments_map()) { |
| + // TODO(cbruni): normal array-like size verification |
| + } else if (map() == *isolate->fast_aliased_arguments_map()) { |
| + CHECK(HasFastArgumentsElements()); |
| + CHECK(this->elements()->IsFixedArray()); |
| + CHECK_GE(this->elements()->length(), 2); |
| + } else if (map() == *isolate->slow_aliased_arguments_map()) { |
| + CHECK(HasSlowArgumentsElements()); |
| + CHECK(this->elements()->IsFixedArray()); |
| + CHECK_GE(this->elements()->length(), 2); |
| + } else if (map() == *isolate->strict_arguments_map()) { |
| + Object* length = *Object::GetProperty(handle(this, isolate), |
|
Igor Sheludko
2016/07/08 08:38:22
This is scary. I guess heap verification should no
Camillo Bruni
2016/07/11 11:46:39
right, also I just noticed that this can be easily
|
| + isolate->factory()->length_string()) |
| + .ToHandleChecked(); |
| + CHECK_EQ(Smi::cast(length)->value(), elements()->length()); |
| + CHECK(HasFastElements()); |
| + } else { |
| + PrintF("."); |
|
Igor Sheludko
2016/07/08 08:38:22
Surprise! :)
Camillo Bruni
2016/07/11 11:46:39
... as good as unreachable.
|
| + } |
| } |
| @@ -434,6 +536,7 @@ void JSValue::JSValueVerify() { |
| if (v->IsHeapObject()) { |
| VerifyHeapPointer(v); |
| } |
| + JSObjectVerify(); |
| } |
| @@ -545,6 +648,7 @@ void JSBoundFunction::JSBoundFunctionVerify() { |
| void JSFunction::JSFunctionVerify() { |
| CHECK(IsJSFunction()); |
| + JSObjectVerify(); |
| VerifyObjectField(kPrototypeOrInitialMapOffset); |
| VerifyObjectField(kNextFunctionLinkOffset); |
| CHECK(code()->IsCode()); |
| @@ -591,6 +695,7 @@ void JSGlobalObject::JSGlobalObjectVerify() { |
| elements()->length() == 0) { |
| return; |
| } |
| + CHECK(!HasFastProperties()); |
| JSObjectVerify(); |
| } |
| @@ -694,7 +799,7 @@ void Code::VerifyEmbeddedObjectsDependency() { |
| } else if (obj->IsJSObject()) { |
| if (isolate->heap()->InNewSpace(obj)) { |
| ArrayList* list = |
| - GetIsolate()->heap()->weak_new_space_object_to_code_list(); |
| + isolate->heap()->weak_new_space_object_to_code_list(); |
| bool found = false; |
| for (int i = 0; i < list->Length(); i += 2) { |
| WeakCell* obj_cell = WeakCell::cast(list->Get(i)); |
| @@ -708,7 +813,7 @@ void Code::VerifyEmbeddedObjectsDependency() { |
| } else { |
| Handle<HeapObject> key_obj(HeapObject::cast(obj), isolate); |
| DependentCode* dep = |
| - GetIsolate()->heap()->LookupWeakObjectToCodeDependency(key_obj); |
| + isolate->heap()->LookupWeakObjectToCodeDependency(key_obj); |
| dep->Contains(DependentCode::kWeakCodeGroup, cell); |
| } |
| } |
| @@ -718,18 +823,23 @@ void Code::VerifyEmbeddedObjectsDependency() { |
| void JSArray::JSArrayVerify() { |
| + CHECK(IsJSArray()); |
| JSObjectVerify(); |
| Isolate* isolate = GetIsolate(); |
| - CHECK(length()->IsNumber() || length()->IsUndefined(isolate)); |
| + // Allow undefined for not fully initialized JSArrays |
| + if (length()->IsUndefined(isolate)) { |
| + CHECK_EQ(FixedArray::cast(elements()), |
| + isolate->heap()->empty_fixed_array()); |
| + return; |
| + } |
| + CHECK(length()->IsNumber()); |
| // If a GC was caused while constructing this array, the elements |
| // pointer may point to a one pointer filler map. |
| if (ElementsAreSafeToExamine()) { |
| - CHECK(elements()->IsUndefined(isolate) || elements()->IsFixedArray() || |
| - elements()->IsFixedDoubleArray()); |
| + CHECK(elements()->IsFixedArray() || elements()->IsFixedDoubleArray()); |
| } |
| } |
| - |
| void JSSet::JSSetVerify() { |
| CHECK(IsJSSet()); |
| JSObjectVerify(); |
| @@ -1296,7 +1406,8 @@ void Code::VerifyRecompiledCode(Code* old_code, Code* new_code) { |
| while (!old_it.done()) { |
| RelocInfo* rinfo = old_it.rinfo(); |
| Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); |
| - CHECK(!target->is_handler() && !target->is_inline_cache_stub()); |
| + CHECK(!target->is_handler()); |
| + CHECK(!target->is_inline_cache_stub()); |
| if (target == stack_check) break; |
| old_it.next(); |
| } |
| @@ -1304,7 +1415,8 @@ void Code::VerifyRecompiledCode(Code* old_code, Code* new_code) { |
| while (!new_it.done()) { |
| RelocInfo* rinfo = new_it.rinfo(); |
| Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); |
| - CHECK(!target->is_handler() && !target->is_inline_cache_stub()); |
| + CHECK(!target->is_handler()); |
| + CHECK(!target->is_inline_cache_stub()); |
| if (target == stack_check) break; |
| new_it.next(); |
| } |