Index: src/objects-debug.cc |
diff --git a/src/objects-debug.cc b/src/objects-debug.cc |
index 13342ca36a405b187a862c54dca11a972d085f9e..fff4f3091c05f9e9c05af37a0abbd1d4d79aa0c7 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,166 @@ 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 { |
+ |
+void VerifyFastProperties(JSReceiver* receiver) { |
+ // TODO(cbruni): JSProxy support for slow properties |
+ if (!receiver->IsJSObject()) return; |
+ Isolate* isolate = receiver->GetIsolate(); |
+ Map* map = receiver->map(); |
+ CHECK(!map->is_dictionary_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))); |
} |
} |
+} |
+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 VerifySlowProperties(JSReceiver* obj) { |
+ Map* map = obj->map(); |
+ CHECK(map->is_dictionary_map()); |
+ CHECK_EQ(0, map->NumberOfOwnDescriptors()); |
+ CHECK_EQ(0, map->unused_property_fields()); |
+ CHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength()); |
+ CHECK(obj->properties()->IsDictionary()); |
+ // Kept in-object properties for sow-mode object need to be zapped: |
+ int nof_in_object_properties = map->GetInObjectProperties(); |
+ if (nof_in_object_properties > 0) { |
+ JSObject* js_object = JSObject::cast(obj); |
+ Smi* zap_value = Smi::FromInt(0); |
+ for (int i = 0; i < nof_in_object_properties; i++) { |
+ FieldIndex index = FieldIndex::ForLoadByFieldIndex(map, i); |
+ Object* field = js_object->RawFastPropertyAt(index); |
+ CHECK_EQ(field, zap_value); |
+ } |
+ } |
+ if (obj->IsJSGlobalObject()) { |
+ VerifyDictionary(JSObject::cast(obj)->global_dictionary()); |
+ } else { |
+ VerifyDictionary(obj->property_dictionary()); |
+ } |
+} |
+ |
+void VerifyProperties(JSReceiver* obj) { |
+ obj->VerifyHeapPointer(obj->properties()); |
+ if (obj->HasFastProperties()) { |
+ VerifyFastProperties(obj); |
+ } else { |
+ VerifySlowProperties(obj); |
+ } |
+} |
+ |
+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_EQ(map->has_fast_object_elements(), obj->HasFastObjectElements()); |
+ |
+ 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)); |
+ 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_LE(FIRST_TYPE, instance_type()); |
+ CHECK_LE(instance_type(), LAST_TYPE); |
CHECK(instance_size() == kVariableSizeSentinel || |
(kPointerSize <= instance_size() && |
instance_size() < heap->Capacity())); |
@@ -357,6 +449,7 @@ void Map::DictionaryMapVerify() { |
CHECK(instance_descriptors()->IsEmpty()); |
CHECK_EQ(0, unused_property_fields()); |
CHECK_EQ(Heap::GetStaticVisitorIdForMap(this), visitor_id()); |
+ CHECK_EQ(kInvalidEnumCacheSentinel, EnumLength()); |
} |
@@ -426,6 +519,59 @@ void JSGeneratorObject::JSGeneratorObjectVerify() { |
VerifyObjectField(kReceiverOffset); |
VerifyObjectField(kOperandStackOffset); |
VerifyObjectField(kContinuationOffset); |
+ JSObjectVerify(); |
+} |
+ |
+namespace { |
+ |
+void VerifyArgumentsParameterMap(FixedArray* elements) { |
+ Isolate* isolate = elements->GetIsolate(); |
+ CHECK(elements->IsFixedArray()); |
+ CHECK_GE(elements->length(), 2); |
+ HeapObject* context = HeapObject::cast(elements->get(0)); |
+ HeapObject* arguments = HeapObject::cast(elements->get(1)); |
+ // TODO(cbruni): fix arguments creation to be atomic. |
+ CHECK_IMPLIES(context->IsUndefined(isolate), arguments->IsUndefined(isolate)); |
+ CHECK(context->IsUndefined(isolate) || context->IsContext()); |
+ CHECK(arguments->IsUndefined(isolate) || arguments->IsFixedArray()); |
+} |
+ |
+} // namespace |
+ |
+void JSArgumentsObject::JSArgumentsObjectVerify() { |
+ VerifyObjectField(kLengthOffset); |
+ JSObjectVerify(); |
+ Isolate* isolate = GetIsolate(); |
+ |
+ if (isolate->bootstrapper()->IsActive()) return; |
+ Context* context = isolate->context(); |
+ Object* constructor = map()->GetConstructor(); |
+ if (constructor->IsJSFunction()) { |
+ context = JSFunction::cast(constructor)->context(); |
+ } |
+ // TODO(cbruni): replace with NULL check once all hydrogren stubs manage to |
+ // set up the contexts properly. |
+ if (isolate->context()->IsSmi()) return; |
+ Context* native_context = context->native_context(); |
+ if (map() == native_context->sloppy_arguments_map()) { |
+ // Sloppy arguments without aliased arguments has a normal elements backing |
+ // store which is already verified by JSObjectVerify() above. |
+ } else if (map() == native_context->fast_aliased_arguments_map()) { |
+ CHECK(HasFastArgumentsElements()); |
+ FixedArray* elements = FixedArray::cast(this->elements()); |
+ VerifyArgumentsParameterMap(FixedArray::cast(elements)); |
+ CHECK_EQ(elements->map(), isolate->heap()->sloppy_arguments_elements_map()); |
+ } else if (map() == native_context->slow_aliased_arguments_map()) { |
+ CHECK(HasSlowArgumentsElements()); |
+ FixedArray* elements = FixedArray::cast(this->elements()); |
+ VerifyArgumentsParameterMap(elements); |
+ VerifyDictionary(SeededNumberDictionary::cast(elements->get(1))); |
+ CHECK_EQ(elements->map(), isolate->heap()->sloppy_arguments_elements_map()); |
+ } else if (map() == native_context->strict_arguments_map()) { |
+ CHECK(HasFastElements()); |
+ } else { |
+ // TODO(cbruni): follow up on normalized argument maps. |
+ } |
} |
@@ -434,10 +580,12 @@ void JSValue::JSValueVerify() { |
if (v->IsHeapObject()) { |
VerifyHeapPointer(v); |
} |
+ JSObjectVerify(); |
} |
void JSDate::JSDateVerify() { |
+ JSObjectVerify(); |
if (value()->IsHeapObject()) { |
VerifyHeapPointer(value()); |
} |
@@ -487,6 +635,7 @@ void JSDate::JSDateVerify() { |
void JSMessageObject::JSMessageObjectVerify() { |
+ JSObjectVerify(); |
CHECK(IsJSMessageObject()); |
VerifyObjectField(kStartPositionOffset); |
VerifyObjectField(kEndPositionOffset); |
@@ -545,6 +694,7 @@ void JSBoundFunction::JSBoundFunctionVerify() { |
void JSFunction::JSFunctionVerify() { |
CHECK(IsJSFunction()); |
+ JSObjectVerify(); |
VerifyObjectField(kPrototypeOrInitialMapOffset); |
VerifyObjectField(kNextFunctionLinkOffset); |
CHECK(code()->IsCode()); |
@@ -591,6 +741,7 @@ void JSGlobalObject::JSGlobalObjectVerify() { |
elements()->length() == 0) { |
return; |
} |
+ CHECK(!HasFastProperties()); |
JSObjectVerify(); |
} |
@@ -694,7 +845,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 +859,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 +869,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 +1452,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 +1461,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(); |
} |