blob: 142dbf66110d0f8d9fdf88c33325f48e44015ba5 [file] [log] [blame]
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/objects.h"
#include "src/assembler-inl.h"
#include "src/bootstrapper.h"
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/elements.h"
#include "src/field-type.h"
#include "src/layout-descriptor.h"
#include "src/macro-assembler.h"
#include "src/objects-inl.h"
#include "src/objects/bigint.h"
#include "src/objects/data-handler-inl.h"
#include "src/objects/debug-objects-inl.h"
#include "src/objects/literal-objects.h"
#include "src/objects/module.h"
#include "src/ostreams.h"
#include "src/regexp/jsregexp.h"
#include "src/transitions.h"
namespace v8 {
namespace internal {
#ifdef VERIFY_HEAP
void Object::ObjectVerify() {
if (IsSmi()) {
Smi::cast(this)->SmiVerify();
} else {
HeapObject::cast(this)->HeapObjectVerify();
}
CHECK(!IsConstructor() || IsCallable());
}
void Object::VerifyPointer(Object* p) {
if (p->IsHeapObject()) {
HeapObject::VerifyHeapPointer(p);
} else {
CHECK(p->IsSmi());
}
}
namespace {
void VerifyForeignPointer(HeapObject* host, Object* foreign) {
host->VerifyPointer(foreign);
CHECK(foreign->IsUndefined(host->GetIsolate()) ||
Foreign::IsNormalized(foreign));
}
} // namespace
void Smi::SmiVerify() {
CHECK(IsSmi());
CHECK(!IsCallable());
CHECK(!IsConstructor());
}
void HeapObject::HeapObjectVerify() {
VerifyHeapPointer(map());
CHECK(map()->IsMap());
InstanceType instance_type = map()->instance_type();
if (instance_type < FIRST_NONSTRING_TYPE) {
String::cast(this)->StringVerify();
return;
}
switch (instance_type) {
case SYMBOL_TYPE:
Symbol::cast(this)->SymbolVerify();
break;
case MAP_TYPE:
Map::cast(this)->MapVerify();
break;
case HEAP_NUMBER_TYPE:
case MUTABLE_HEAP_NUMBER_TYPE:
HeapNumber::cast(this)->HeapNumberVerify();
break;
case BIGINT_TYPE:
BigInt::cast(this)->BigIntVerify();
break;
case HASH_TABLE_TYPE:
case FIXED_ARRAY_TYPE:
FixedArray::cast(this)->FixedArrayVerify();
break;
case FIXED_DOUBLE_ARRAY_TYPE:
FixedDoubleArray::cast(this)->FixedDoubleArrayVerify();
break;
case BYTE_ARRAY_TYPE:
ByteArray::cast(this)->ByteArrayVerify();
break;
case BYTECODE_ARRAY_TYPE:
BytecodeArray::cast(this)->BytecodeArrayVerify();
break;
case DESCRIPTOR_ARRAY_TYPE:
DescriptorArray::cast(this)->DescriptorArrayVerify();
break;
case TRANSITION_ARRAY_TYPE:
TransitionArray::cast(this)->TransitionArrayVerify();
break;
case PROPERTY_ARRAY_TYPE:
PropertyArray::cast(this)->PropertyArrayVerify();
break;
case FREE_SPACE_TYPE:
FreeSpace::cast(this)->FreeSpaceVerify();
break;
case FEEDBACK_VECTOR_TYPE:
FeedbackVector::cast(this)->FeedbackVectorVerify();
break;
#define VERIFY_TYPED_ARRAY(Type, type, TYPE, ctype, size) \
case FIXED_##TYPE##_ARRAY_TYPE: \
Fixed##Type##Array::cast(this)->FixedTypedArrayVerify(); \
break;
TYPED_ARRAYS(VERIFY_TYPED_ARRAY)
#undef VERIFY_TYPED_ARRAY
case CODE_TYPE:
Code::cast(this)->CodeVerify();
break;
case ODDBALL_TYPE:
Oddball::cast(this)->OddballVerify();
break;
case JS_OBJECT_TYPE:
case JS_ERROR_TYPE:
case JS_API_OBJECT_TYPE:
case JS_SPECIAL_API_OBJECT_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case WASM_INSTANCE_TYPE:
case WASM_MEMORY_TYPE:
case WASM_MODULE_TYPE:
case WASM_TABLE_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;
case JS_ASYNC_GENERATOR_OBJECT_TYPE:
JSAsyncGeneratorObject::cast(this)->JSAsyncGeneratorObjectVerify();
break;
case JS_VALUE_TYPE:
JSValue::cast(this)->JSValueVerify();
break;
case JS_DATE_TYPE:
JSDate::cast(this)->JSDateVerify();
break;
case JS_BOUND_FUNCTION_TYPE:
JSBoundFunction::cast(this)->JSBoundFunctionVerify();
break;
case JS_FUNCTION_TYPE:
JSFunction::cast(this)->JSFunctionVerify();
break;
case JS_GLOBAL_PROXY_TYPE:
JSGlobalProxy::cast(this)->JSGlobalProxyVerify();
break;
case JS_GLOBAL_OBJECT_TYPE:
JSGlobalObject::cast(this)->JSGlobalObjectVerify();
break;
case CELL_TYPE:
Cell::cast(this)->CellVerify();
break;
case PROPERTY_CELL_TYPE:
PropertyCell::cast(this)->PropertyCellVerify();
break;
case WEAK_CELL_TYPE:
WeakCell::cast(this)->WeakCellVerify();
break;
case JS_ARRAY_TYPE:
JSArray::cast(this)->JSArrayVerify();
break;
case JS_MODULE_NAMESPACE_TYPE:
JSModuleNamespace::cast(this)->JSModuleNamespaceVerify();
break;
case JS_SET_TYPE:
JSSet::cast(this)->JSSetVerify();
break;
case JS_MAP_TYPE:
JSMap::cast(this)->JSMapVerify();
break;
case JS_SET_KEY_VALUE_ITERATOR_TYPE:
case JS_SET_VALUE_ITERATOR_TYPE:
JSSetIterator::cast(this)->JSSetIteratorVerify();
break;
case JS_MAP_KEY_ITERATOR_TYPE:
case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
case JS_MAP_VALUE_ITERATOR_TYPE:
JSMapIterator::cast(this)->JSMapIteratorVerify();
break;
#define ARRAY_ITERATOR_CASE(type) case type:
ARRAY_ITERATOR_TYPE_LIST(ARRAY_ITERATOR_CASE)
#undef ARRAY_ITERATOR_CASE
JSArrayIterator::cast(this)->JSArrayIteratorVerify();
break;
case JS_STRING_ITERATOR_TYPE:
JSStringIterator::cast(this)->JSStringIteratorVerify();
break;
case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
JSAsyncFromSyncIterator::cast(this)->JSAsyncFromSyncIteratorVerify();
break;
case JS_WEAK_MAP_TYPE:
JSWeakMap::cast(this)->JSWeakMapVerify();
break;
case JS_WEAK_SET_TYPE:
JSWeakSet::cast(this)->JSWeakSetVerify();
break;
case JS_PROMISE_TYPE:
JSPromise::cast(this)->JSPromiseVerify();
break;
case JS_REGEXP_TYPE:
JSRegExp::cast(this)->JSRegExpVerify();
break;
case FILLER_TYPE:
break;
case JS_PROXY_TYPE:
JSProxy::cast(this)->JSProxyVerify();
break;
case FOREIGN_TYPE:
Foreign::cast(this)->ForeignVerify();
break;
case SHARED_FUNCTION_INFO_TYPE:
SharedFunctionInfo::cast(this)->SharedFunctionInfoVerify();
break;
case JS_MESSAGE_OBJECT_TYPE:
JSMessageObject::cast(this)->JSMessageObjectVerify();
break;
case JS_ARRAY_BUFFER_TYPE:
JSArrayBuffer::cast(this)->JSArrayBufferVerify();
break;
case JS_TYPED_ARRAY_TYPE:
JSTypedArray::cast(this)->JSTypedArrayVerify();
break;
case JS_DATA_VIEW_TYPE:
JSDataView::cast(this)->JSDataViewVerify();
break;
case SMALL_ORDERED_HASH_SET_TYPE:
SmallOrderedHashSet::cast(this)->SmallOrderedHashTableVerify();
break;
case SMALL_ORDERED_HASH_MAP_TYPE:
SmallOrderedHashMap::cast(this)->SmallOrderedHashTableVerify();
break;
case CODE_DATA_CONTAINER_TYPE:
CodeDataContainer::cast(this)->CodeDataContainerVerify();
break;
#define MAKE_STRUCT_CASE(NAME, Name, name) \
case NAME##_TYPE: \
Name::cast(this)->Name##Verify(); \
break;
STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE
case LOAD_HANDLER_TYPE:
LoadHandler::cast(this)->LoadHandlerVerify();
break;
case STORE_HANDLER_TYPE:
StoreHandler::cast(this)->StoreHandlerVerify();
break;
default:
UNREACHABLE();
break;
}
}
void HeapObject::VerifyHeapPointer(Object* p) {
CHECK(p->IsHeapObject());
HeapObject* ho = HeapObject::cast(p);
CHECK(ho->GetHeap()->Contains(ho));
}
void Symbol::SymbolVerify() {
CHECK(IsSymbol());
CHECK(HasHashCode());
CHECK_GT(Hash(), 0);
CHECK(name()->IsUndefined(GetIsolate()) || name()->IsString());
}
void HeapNumber::HeapNumberVerify() {
CHECK(IsHeapNumber() || IsMutableHeapNumber());
}
void ByteArray::ByteArrayVerify() {
CHECK(IsByteArray());
}
void BytecodeArray::BytecodeArrayVerify() {
// TODO(oth): Walk bytecodes and immediate values to validate sanity.
// - All bytecodes are known and well formed.
// - Jumps must go to new instructions starts.
// - No Illegal bytecodes.
// - No consecutive sequences of prefix Wide / ExtraWide.
CHECK(IsBytecodeArray());
CHECK(constant_pool()->IsFixedArray());
VerifyHeapPointer(constant_pool());
}
void FreeSpace::FreeSpaceVerify() {
CHECK(IsFreeSpace());
}
void FeedbackVector::FeedbackVectorVerify() { CHECK(IsFeedbackVector()); }
template <class Traits>
void FixedTypedArray<Traits>::FixedTypedArrayVerify() {
CHECK(IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
Traits::kInstanceType);
if (base_pointer() == this) {
CHECK(external_pointer() ==
ExternalReference::fixed_typed_array_base_data_offset().address());
} else {
CHECK_NULL(base_pointer());
}
}
bool JSObject::ElementsAreSafeToExamine() {
// If a GC was caused while constructing this object, the elements
// pointer may point to a one pointer filler map.
return reinterpret_cast<Map*>(elements()) !=
GetHeap()->one_pointer_filler_map();
}
void JSObject::JSObjectVerify() {
VerifyPointer(raw_properties_or_hash());
VerifyHeapPointer(elements());
CHECK_IMPLIES(HasSloppyArgumentsElements(), IsJSArgumentsObject());
if (HasFastProperties()) {
int actual_unused_property_fields = map()->GetInObjectProperties() +
property_array()->length() -
map()->NextFreePropertyIndex();
if (map()->UnusedPropertyFields() != actual_unused_property_fields) {
// There are two reasons why this can 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 while the map isn't updated yet.
// - deletion of the last property can leave additional backing store
// capacity behind.
CHECK_GT(actual_unused_property_fields, map()->UnusedPropertyFields());
int delta = actual_unused_property_fields - map()->UnusedPropertyFields();
CHECK_EQ(0, delta % JSObject::kFieldsAdded);
}
DescriptorArray* descriptors = map()->instance_descriptors();
Isolate* isolate = GetIsolate();
bool is_transitionable_fast_elements_kind =
IsTransitionableFastElementsKind(map()->elements_kind());
for (int i = 0; i < map()->NumberOfOwnDescriptors(); i++) {
PropertyDetails details = descriptors->GetDetails(i);
if (details.location() == kField) {
DCHECK_EQ(kData, details.kind());
Representation r = details.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())) {
CHECK(!field_type->NowStable() || field_type->NowContains(value));
}
CHECK_IMPLIES(is_transitionable_fast_elements_kind,
!Map::IsInplaceGeneralizableField(details.constness(), r,
field_type));
}
}
if (map()->EnumLength() != kInvalidEnumCacheSentinel) {
EnumCache* enum_cache = descriptors->GetEnumCache();
FixedArray* keys = enum_cache->keys();
FixedArray* indices = enum_cache->indices();
CHECK_LE(map()->EnumLength(), keys->length());
CHECK_IMPLIES(indices != isolate->heap()->empty_fixed_array(),
keys->length() == indices->length());
}
}
// 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() == HasObjectElements());
}
}
void Map::MapVerify() {
Heap* heap = GetHeap();
CHECK(!heap->InNewSpace(this));
CHECK(FIRST_TYPE <= instance_type() && instance_type() <= LAST_TYPE);
CHECK(instance_size() == kVariableSizeSentinel ||
(kPointerSize <= instance_size() &&
static_cast<size_t>(instance_size()) < heap->Capacity()));
CHECK(GetBackPointer()->IsUndefined(heap->isolate()) ||
!Map::cast(GetBackPointer())->is_stable());
VerifyHeapPointer(prototype());
VerifyHeapPointer(instance_descriptors());
SLOW_DCHECK(instance_descriptors()->IsSortedNoDuplicates());
DisallowHeapAllocation no_gc;
SLOW_DCHECK(TransitionsAccessor(this, &no_gc).IsSortedNoDuplicates());
SLOW_DCHECK(TransitionsAccessor(this, &no_gc).IsConsistentWithBackPointers());
SLOW_DCHECK(!FLAG_unbox_double_fields ||
layout_descriptor()->IsConsistentWithMap(this));
if (!may_have_interesting_symbols()) {
CHECK(!has_named_interceptor());
CHECK(!is_dictionary_map());
CHECK(!is_access_check_needed());
DescriptorArray* const descriptors = instance_descriptors();
for (int i = 0; i < NumberOfOwnDescriptors(); ++i) {
CHECK(!descriptors->GetKey(i)->IsInterestingSymbol());
}
}
CHECK_IMPLIES(has_named_interceptor(), may_have_interesting_symbols());
CHECK_IMPLIES(is_dictionary_map(), may_have_interesting_symbols());
CHECK_IMPLIES(is_access_check_needed(), may_have_interesting_symbols());
CHECK_IMPLIES(IsJSObjectMap() && !CanHaveFastTransitionableElementsKind(),
IsDictionaryElementsKind(elements_kind()) ||
IsTerminalElementsKind(elements_kind()));
if (is_prototype_map()) {
DCHECK(prototype_info() == Smi::kZero ||
prototype_info()->IsPrototypeInfo());
}
}
void Map::DictionaryMapVerify() {
MapVerify();
CHECK(is_dictionary_map());
CHECK_EQ(kInvalidEnumCacheSentinel, EnumLength());
CHECK_EQ(GetHeap()->empty_descriptor_array(), instance_descriptors());
CHECK_EQ(0, UnusedPropertyFields());
CHECK_EQ(Map::GetVisitorId(this), visitor_id());
}
void AliasedArgumentsEntry::AliasedArgumentsEntryVerify() {
VerifySmiField(kAliasedContextSlot);
}
void FixedArray::FixedArrayVerify() {
for (int i = 0; i < length(); i++) {
Object* e = get(i);
VerifyPointer(e);
}
}
void PropertyArray::PropertyArrayVerify() {
if (length() == 0) {
CHECK_EQ(this, this->GetHeap()->empty_property_array());
return;
}
// There are no empty PropertyArrays.
CHECK_LT(0, length());
for (int i = 0; i < length(); i++) {
Object* e = get(i);
VerifyPointer(e);
}
}
void FixedDoubleArray::FixedDoubleArrayVerify() {
for (int i = 0; i < length(); i++) {
if (!is_the_hole(i)) {
uint64_t value = get_representation(i);
uint64_t unexpected =
bit_cast<uint64_t>(std::numeric_limits<double>::quiet_NaN()) &
uint64_t{0x7FF8000000000000};
// Create implementation specific sNaN by inverting relevant bit.
unexpected ^= uint64_t{0x0008000000000000};
CHECK((value & uint64_t{0x7FF8000000000000}) != unexpected ||
(value & uint64_t{0x0007FFFFFFFFFFFF}) == uint64_t{0});
}
}
}
void DescriptorArray::DescriptorArrayVerify() {
FixedArrayVerify();
if (number_of_descriptors_storage() == 0) {
Heap* heap = GetHeap();
CHECK_EQ(heap->empty_descriptor_array(), this);
CHECK_EQ(2, length());
CHECK_EQ(0, number_of_descriptors());
CHECK_EQ(heap->empty_enum_cache(), GetEnumCache());
} else {
CHECK_LT(2, length());
CHECK_LE(LengthFor(number_of_descriptors()), length());
}
}
void TransitionArray::TransitionArrayVerify() {
FixedArrayVerify();
CHECK_LE(LengthFor(number_of_transitions()), length());
}
void JSArgumentsObject::JSArgumentsObjectVerify() {
if (IsSloppyArgumentsElementsKind(GetElementsKind())) {
SloppyArgumentsElements::cast(elements())
->SloppyArgumentsElementsVerify(this);
}
Isolate* isolate = GetIsolate();
if (isolate->IsInAnyContext(map(), Context::SLOPPY_ARGUMENTS_MAP_INDEX) ||
isolate->IsInAnyContext(map(),
Context::SLOW_ALIASED_ARGUMENTS_MAP_INDEX) ||
isolate->IsInAnyContext(map(),
Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX)) {
VerifyObjectField(JSSloppyArgumentsObject::kLengthOffset);
VerifyObjectField(JSSloppyArgumentsObject::kCalleeOffset);
} else if (isolate->IsInAnyContext(map(),
Context::STRICT_ARGUMENTS_MAP_INDEX)) {
VerifyObjectField(JSStrictArgumentsObject::kLengthOffset);
}
JSObjectVerify();
}
void SloppyArgumentsElements::SloppyArgumentsElementsVerify(JSObject* holder) {
Isolate* isolate = GetIsolate();
FixedArrayVerify();
// Abort verification if only partially initialized (can't use arguments()
// getter because it does FixedArray::cast()).
if (get(kArgumentsIndex)->IsUndefined(isolate)) return;
ElementsKind kind = holder->GetElementsKind();
bool is_fast = kind == FAST_SLOPPY_ARGUMENTS_ELEMENTS;
CHECK(IsFixedArray());
CHECK_GE(length(), 2);
CHECK_EQ(map(), isolate->heap()->sloppy_arguments_elements_map());
Context* context_object = Context::cast(context());
FixedArray* arg_elements = FixedArray::cast(arguments());
if (arg_elements->length() == 0) {
CHECK(arg_elements == isolate->heap()->empty_fixed_array());
return;
}
ElementsAccessor* accessor;
if (is_fast) {
accessor = ElementsAccessor::ForKind(HOLEY_ELEMENTS);
} else {
accessor = ElementsAccessor::ForKind(DICTIONARY_ELEMENTS);
}
int nofMappedParameters = 0;
int maxMappedIndex = 0;
for (int i = 0; i < nofMappedParameters; i++) {
// Verify that each context-mapped argument is either the hole or a valid
// Smi within context length range.
Object* mapped = get_mapped_entry(i);
if (mapped->IsTheHole(isolate)) {
// Slow sloppy arguments can be holey.
if (!is_fast) continue;
// Fast sloppy arguments elements are never holey. Either the element is
// context-mapped or present in the arguments elements.
CHECK(accessor->HasElement(holder, i, arg_elements));
continue;
}
int mappedIndex = Smi::ToInt(mapped);
nofMappedParameters++;
CHECK_LE(maxMappedIndex, mappedIndex);
maxMappedIndex = mappedIndex;
Object* value = context_object->get(mappedIndex);
CHECK(value->IsObject());
// None of the context-mapped entries should exist in the arguments
// elements.
CHECK(!accessor->HasElement(holder, i, arg_elements));
}
CHECK_LE(nofMappedParameters, context_object->length());
CHECK_LE(nofMappedParameters, arg_elements->length());
CHECK_LE(maxMappedIndex, context_object->length());
CHECK_LE(maxMappedIndex, arg_elements->length());
}
void JSGeneratorObject::JSGeneratorObjectVerify() {
// In an expression like "new g()", there can be a point where a generator
// object is allocated but its fields are all undefined, as it hasn't yet been
// initialized by the generator. Hence these weak checks.
VerifyObjectField(kFunctionOffset);
VerifyObjectField(kContextOffset);
VerifyObjectField(kReceiverOffset);
VerifyObjectField(kRegisterFileOffset);
VerifyObjectField(kContinuationOffset);
}
void JSAsyncGeneratorObject::JSAsyncGeneratorObjectVerify() {
// Check inherited fields
JSGeneratorObjectVerify();
VerifyObjectField(kQueueOffset);
queue()->HeapObjectVerify();
}
void JSValue::JSValueVerify() {
Object* v = value();
if (v->IsHeapObject()) {
VerifyHeapPointer(v);
}
}
void JSDate::JSDateVerify() {
if (value()->IsHeapObject()) {
VerifyHeapPointer(value());
}
Isolate* isolate = GetIsolate();
CHECK(value()->IsUndefined(isolate) || value()->IsSmi() ||
value()->IsHeapNumber());
CHECK(year()->IsUndefined(isolate) || year()->IsSmi() || year()->IsNaN());
CHECK(month()->IsUndefined(isolate) || month()->IsSmi() || month()->IsNaN());
CHECK(day()->IsUndefined(isolate) || day()->IsSmi() || day()->IsNaN());
CHECK(weekday()->IsUndefined(isolate) || weekday()->IsSmi() ||
weekday()->IsNaN());
CHECK(hour()->IsUndefined(isolate) || hour()->IsSmi() || hour()->IsNaN());
CHECK(min()->IsUndefined(isolate) || min()->IsSmi() || min()->IsNaN());
CHECK(sec()->IsUndefined(isolate) || sec()->IsSmi() || sec()->IsNaN());
CHECK(cache_stamp()->IsUndefined(isolate) || cache_stamp()->IsSmi() ||
cache_stamp()->IsNaN());
if (month()->IsSmi()) {
int month = Smi::ToInt(this->month());
CHECK(0 <= month && month <= 11);
}
if (day()->IsSmi()) {
int day = Smi::ToInt(this->day());
CHECK(1 <= day && day <= 31);
}
if (hour()->IsSmi()) {
int hour = Smi::ToInt(this->hour());
CHECK(0 <= hour && hour <= 23);
}
if (min()->IsSmi()) {
int min = Smi::ToInt(this->min());
CHECK(0 <= min && min <= 59);
}
if (sec()->IsSmi()) {
int sec = Smi::ToInt(this->sec());
CHECK(0 <= sec && sec <= 59);
}
if (weekday()->IsSmi()) {
int weekday = Smi::ToInt(this->weekday());
CHECK(0 <= weekday && weekday <= 6);
}
if (cache_stamp()->IsSmi()) {
CHECK(Smi::ToInt(cache_stamp()) <=
Smi::ToInt(isolate->date_cache()->stamp()));
}
}
void JSMessageObject::JSMessageObjectVerify() {
CHECK(IsJSMessageObject());
VerifyObjectField(kStartPositionOffset);
VerifyObjectField(kEndPositionOffset);
VerifyObjectField(kArgumentsOffset);
VerifyObjectField(kScriptOffset);
VerifyObjectField(kStackFramesOffset);
}
void String::StringVerify() {
CHECK(IsString());
CHECK(length() >= 0 && length() <= Smi::kMaxValue);
CHECK_IMPLIES(length() == 0, this == GetHeap()->empty_string());
if (IsInternalizedString()) {
CHECK(!GetHeap()->InNewSpace(this));
}
if (IsConsString()) {
ConsString::cast(this)->ConsStringVerify();
} else if (IsSlicedString()) {
SlicedString::cast(this)->SlicedStringVerify();
} else if (IsThinString()) {
ThinString::cast(this)->ThinStringVerify();
}
}
void ConsString::ConsStringVerify() {
CHECK(this->first()->IsString());
CHECK(this->second() == GetHeap()->empty_string() ||
this->second()->IsString());
CHECK_GE(this->length(), ConsString::kMinLength);
CHECK(this->length() == this->first()->length() + this->second()->length());
if (this->IsFlat()) {
// A flat cons can only be created by String::SlowFlatten.
// Afterwards, the first part may be externalized or internalized.
CHECK(this->first()->IsSeqString() || this->first()->IsExternalString() ||
this->first()->IsThinString());
}
}
void ThinString::ThinStringVerify() {
CHECK(this->actual()->IsInternalizedString());
CHECK(this->actual()->IsSeqString() || this->actual()->IsExternalString());
}
void SlicedString::SlicedStringVerify() {
CHECK(!this->parent()->IsConsString());
CHECK(!this->parent()->IsSlicedString());
CHECK_GE(this->length(), SlicedString::kMinLength);
}
void JSBoundFunction::JSBoundFunctionVerify() {
CHECK(IsJSBoundFunction());
JSObjectVerify();
VerifyObjectField(kBoundThisOffset);
VerifyObjectField(kBoundTargetFunctionOffset);
VerifyObjectField(kBoundArgumentsOffset);
CHECK(IsCallable());
Isolate* const isolate = GetIsolate();
if (!raw_bound_target_function()->IsUndefined(isolate)) {
CHECK(bound_target_function()->IsCallable());
CHECK_EQ(IsConstructor(), bound_target_function()->IsConstructor());
}
}
void JSFunction::JSFunctionVerify() {
CHECK(IsJSFunction());
CHECK(code()->IsCode());
CHECK(map()->is_callable());
if (has_prototype_slot()) {
VerifyObjectField(kPrototypeOrInitialMapOffset);
}
}
void SharedFunctionInfo::SharedFunctionInfoVerify() {
CHECK(IsSharedFunctionInfo());
VerifyObjectField(kCodeOffset);
VerifyObjectField(kDebugInfoOffset);
VerifyObjectField(kFeedbackMetadataOffset);
VerifyObjectField(kFunctionDataOffset);
VerifyObjectField(kFunctionIdentifierOffset);
VerifyObjectField(kInstanceClassNameOffset);
VerifyObjectField(kNameOffset);
VerifyObjectField(kOuterScopeInfoOffset);
VerifyObjectField(kScopeInfoOffset);
VerifyObjectField(kScriptOffset);
CHECK(raw_name() == kNoSharedNameSentinel || raw_name()->IsString());
Isolate* isolate = GetIsolate();
CHECK(function_data()->IsUndefined(isolate) || IsApiFunction() ||
HasBytecodeArray() || HasAsmWasmData() ||
HasLazyDeserializationBuiltinId() || HasPreParsedScopeData());
CHECK(function_identifier()->IsUndefined(isolate) || HasBuiltinFunctionId() ||
HasInferredName());
int expected_map_index = Context::FunctionMapIndex(
language_mode(), kind(), true, has_shared_name(), needs_home_object());
CHECK_EQ(expected_map_index, function_map_index());
if (scope_info()->length() > 0) {
CHECK(kind() == scope_info()->function_kind());
CHECK_EQ(kind() == kModule, scope_info()->scope_type() == MODULE_SCOPE);
}
}
void JSGlobalProxy::JSGlobalProxyVerify() {
CHECK(IsJSGlobalProxy());
JSObjectVerify();
VerifyObjectField(JSGlobalProxy::kNativeContextOffset);
// Make sure that this object has no properties, elements.
CHECK_EQ(0, FixedArray::cast(elements())->length());
}
void JSGlobalObject::JSGlobalObjectVerify() {
CHECK(IsJSGlobalObject());
// Do not check the dummy global object for the builtins.
if (global_dictionary()->NumberOfElements() == 0 &&
elements()->length() == 0) {
return;
}
JSObjectVerify();
}
void Oddball::OddballVerify() {
CHECK(IsOddball());
Heap* heap = GetHeap();
VerifyHeapPointer(to_string());
Object* number = to_number();
if (number->IsHeapObject()) {
CHECK(number == heap->nan_value() ||
number == heap->hole_nan_value());
} else {
CHECK(number->IsSmi());
int value = Smi::ToInt(number);
// Hidden oddballs have negative smis.
const int kLeastHiddenOddballNumber = -7;
CHECK_LE(value, 1);
CHECK_GE(value, kLeastHiddenOddballNumber);
}
if (map() == heap->undefined_map()) {
CHECK(this == heap->undefined_value());
} else if (map() == heap->the_hole_map()) {
CHECK(this == heap->the_hole_value());
} else if (map() == heap->null_map()) {
CHECK(this == heap->null_value());
} else if (map() == heap->boolean_map()) {
CHECK(this == heap->true_value() ||
this == heap->false_value());
} else if (map() == heap->uninitialized_map()) {
CHECK(this == heap->uninitialized_value());
} else if (map() == heap->arguments_marker_map()) {
CHECK(this == heap->arguments_marker());
} else if (map() == heap->termination_exception_map()) {
CHECK(this == heap->termination_exception());
} else if (map() == heap->exception_map()) {
CHECK(this == heap->exception());
} else if (map() == heap->optimized_out_map()) {
CHECK(this == heap->optimized_out());
} else if (map() == heap->stale_register_map()) {
CHECK(this == heap->stale_register());
} else {
UNREACHABLE();
}
}
void Cell::CellVerify() {
CHECK(IsCell());
VerifyObjectField(kValueOffset);
}
void PropertyCell::PropertyCellVerify() {
CHECK(IsPropertyCell());
VerifyObjectField(kValueOffset);
}
void WeakCell::WeakCellVerify() {
CHECK(IsWeakCell());
VerifyObjectField(kValueOffset);
}
void CodeDataContainer::CodeDataContainerVerify() {
CHECK(IsCodeDataContainer());
VerifyObjectField(kNextCodeLinkOffset);
CHECK(next_code_link()->IsCode() ||
next_code_link()->IsUndefined(GetIsolate()));
}
void Code::CodeVerify() {
CHECK(IsAligned(reinterpret_cast<intptr_t>(instruction_start()),
kCodeAlignment));
relocation_info()->ObjectVerify();
Address last_gc_pc = nullptr;
Isolate* isolate = GetIsolate();
for (RelocIterator it(this); !it.done(); it.next()) {
it.rinfo()->Verify(isolate);
// Ensure that GC will not iterate twice over the same pointer.
if (RelocInfo::IsGCRelocMode(it.rinfo()->rmode())) {
CHECK(it.rinfo()->pc() != last_gc_pc);
last_gc_pc = it.rinfo()->pc();
}
}
}
void Code::VerifyEmbeddedObjectsDependency() {
if (!CanContainWeakObjects()) return;
WeakCell* cell = CachedWeakCell();
DisallowHeapAllocation no_gc;
Isolate* isolate = GetIsolate();
HandleScope scope(isolate);
int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
Object* obj = it.rinfo()->target_object();
if (IsWeakObject(obj)) {
if (obj->IsMap()) {
Map* map = Map::cast(obj);
CHECK(map->dependent_code()->Contains(DependentCode::kWeakCodeGroup,
cell));
} else if (obj->IsJSObject()) {
if (isolate->heap()->InNewSpace(obj)) {
ArrayList* list =
GetIsolate()->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));
if (!obj_cell->cleared() && obj_cell->value() == obj &&
WeakCell::cast(list->Get(i + 1)) == cell) {
found = true;
break;
}
}
CHECK(found);
} else {
Handle<HeapObject> key_obj(HeapObject::cast(obj), isolate);
DependentCode* dep =
GetIsolate()->heap()->LookupWeakObjectToCodeDependency(key_obj);
dep->Contains(DependentCode::kWeakCodeGroup, cell);
}
}
}
}
}
void JSArray::JSArrayVerify() {
JSObjectVerify();
Isolate* isolate = GetIsolate();
CHECK(length()->IsNumber() || length()->IsUndefined(isolate));
// If a GC was caused while constructing this array, the elements
// pointer may point to a one pointer filler map.
if (!ElementsAreSafeToExamine()) return;
if (elements()->IsUndefined(isolate)) return;
CHECK(elements()->IsFixedArray() || elements()->IsFixedDoubleArray());
if (!length()->IsNumber()) return;
// Verify that the length and the elements backing store are in sync.
if (length()->IsSmi() && HasFastElements()) {
int size = Smi::ToInt(length());
// Holey / Packed backing stores might have slack or might have not been
// properly initialized yet.
CHECK(size <= elements()->length() ||
elements() == isolate->heap()->empty_fixed_array());
} else {
CHECK(HasDictionaryElements());
uint32_t array_length;
CHECK(length()->ToArrayLength(&array_length));
if (array_length == 0xFFFFFFFF) {
CHECK(length()->ToArrayLength(&array_length));
}
if (array_length != 0) {
NumberDictionary* dict = NumberDictionary::cast(elements());
// The dictionary can never have more elements than the array length + 1.
// If the backing store grows the verification might be triggered with
// the old length in place.
uint32_t nof_elements = static_cast<uint32_t>(dict->NumberOfElements());
if (nof_elements != 0) nof_elements--;
CHECK_LE(nof_elements, array_length);
}
}
}
void JSSet::JSSetVerify() {
CHECK(IsJSSet());
JSObjectVerify();
VerifyHeapPointer(table());
CHECK(table()->IsOrderedHashSet() || table()->IsUndefined(GetIsolate()));
// TODO(arv): Verify OrderedHashTable too.
}
void JSMap::JSMapVerify() {
CHECK(IsJSMap());
JSObjectVerify();
VerifyHeapPointer(table());
CHECK(table()->IsOrderedHashMap() || table()->IsUndefined(GetIsolate()));
// TODO(arv): Verify OrderedHashTable too.
}
void JSSetIterator::JSSetIteratorVerify() {
CHECK(IsJSSetIterator());
JSObjectVerify();
VerifyHeapPointer(table());
CHECK(table()->IsOrderedHashSet());
CHECK(index()->IsSmi());
}
void JSMapIterator::JSMapIteratorVerify() {
CHECK(IsJSMapIterator());
JSObjectVerify();
VerifyHeapPointer(table());
CHECK(table()->IsOrderedHashMap());
CHECK(index()->IsSmi());
}
void JSWeakMap::JSWeakMapVerify() {
CHECK(IsJSWeakMap());
JSObjectVerify();
VerifyHeapPointer(table());
CHECK(table()->IsHashTable() || table()->IsUndefined(GetIsolate()));
}
void JSArrayIterator::JSArrayIteratorVerify() {
CHECK(IsJSArrayIterator());
JSObjectVerify();
CHECK(object()->IsJSReceiver() || object()->IsUndefined(GetIsolate()));
CHECK_GE(index()->Number(), 0);
CHECK_LE(index()->Number(), kMaxSafeInteger);
CHECK(object_map()->IsMap() || object_map()->IsUndefined(GetIsolate()));
}
void JSStringIterator::JSStringIteratorVerify() {
CHECK(IsJSStringIterator());
JSObjectVerify();
CHECK(string()->IsString());
CHECK_GE(index(), 0);
CHECK_LE(index(), String::kMaxLength);
}
void JSAsyncFromSyncIterator::JSAsyncFromSyncIteratorVerify() {
CHECK(IsJSAsyncFromSyncIterator());
JSObjectVerify();
VerifyHeapPointer(sync_iterator());
}
void JSWeakSet::JSWeakSetVerify() {
CHECK(IsJSWeakSet());
JSObjectVerify();
VerifyHeapPointer(table());
CHECK(table()->IsHashTable() || table()->IsUndefined(GetIsolate()));
}
void PromiseCapability::PromiseCapabilityVerify() {
CHECK(IsPromiseCapability());
VerifyPointer(promise());
VerifyPointer(resolve());
VerifyPointer(reject());
}
void JSPromise::JSPromiseVerify() {
CHECK(IsJSPromise());
JSObjectVerify();
Isolate* isolate = GetIsolate();
CHECK(result()->IsUndefined(isolate) || result()->IsObject());
CHECK(deferred_promise()->IsUndefined(isolate) ||
deferred_promise()->IsJSReceiver() ||
deferred_promise()->IsFixedArray());
CHECK(deferred_on_resolve()->IsUndefined(isolate) ||
deferred_on_resolve()->IsCallable() ||
deferred_on_resolve()->IsFixedArray());
CHECK(deferred_on_reject()->IsUndefined(isolate) ||
deferred_on_reject()->IsCallable() ||
deferred_on_reject()->IsFixedArray());
CHECK(fulfill_reactions()->IsUndefined(isolate) ||
fulfill_reactions()->IsCallable() || fulfill_reactions()->IsSymbol() ||
fulfill_reactions()->IsFixedArray());
CHECK(reject_reactions()->IsUndefined(isolate) ||
reject_reactions()->IsSymbol() || reject_reactions()->IsCallable() ||
reject_reactions()->IsFixedArray());
}
template <typename Derived>
void SmallOrderedHashTable<Derived>::SmallOrderedHashTableVerify() {
CHECK(IsSmallOrderedHashTable());
Isolate* isolate = GetIsolate();
for (int entry = 0; entry < NumberOfBuckets(); entry++) {
int bucket = GetFirstEntry(entry);
if (bucket == kNotFound) continue;
for (int offset = 0; offset < Derived::kEntrySize; offset++) {
Object* val = GetDataEntry(bucket, offset);
CHECK(!val->IsTheHole(isolate));
}
}
for (int entry = 0; entry < NumberOfElements(); entry++) {
int chain = GetNextEntry(entry);
if (chain == kNotFound) continue;
for (int offset = 0; offset < Derived::kEntrySize; offset++) {
Object* val = GetDataEntry(chain, offset);
CHECK(!val->IsTheHole(isolate));
}
}
for (int entry = 0; entry < NumberOfElements(); entry++) {
for (int offset = 0; offset < Derived::kEntrySize; offset++) {
Object* val = GetDataEntry(entry, offset);
VerifyPointer(val);
}
}
for (int entry = NumberOfElements(); entry < Capacity(); entry++) {
for (int offset = 0; offset < Derived::kEntrySize; offset++) {
Object* val = GetDataEntry(entry, offset);
CHECK(val->IsTheHole(isolate));
}
}
}
template void
SmallOrderedHashTable<SmallOrderedHashMap>::SmallOrderedHashTableVerify();
template void
SmallOrderedHashTable<SmallOrderedHashSet>::SmallOrderedHashTableVerify();
void JSRegExp::JSRegExpVerify() {
JSObjectVerify();
Isolate* isolate = GetIsolate();
CHECK(data()->IsUndefined(isolate) || data()->IsFixedArray());
switch (TypeTag()) {
case JSRegExp::ATOM: {
FixedArray* arr = FixedArray::cast(data());
CHECK(arr->get(JSRegExp::kAtomPatternIndex)->IsString());
break;
}
case JSRegExp::IRREGEXP: {
bool is_native = RegExpImpl::UsesNativeRegExp();
FixedArray* arr = FixedArray::cast(data());
Object* one_byte_data = arr->get(JSRegExp::kIrregexpLatin1CodeIndex);
// Smi : Not compiled yet (-1).
// Code/ByteArray: Compiled code.
CHECK(
(one_byte_data->IsSmi() &&
Smi::ToInt(one_byte_data) == JSRegExp::kUninitializedValue) ||
(is_native ? one_byte_data->IsCode() : one_byte_data->IsByteArray()));
Object* uc16_data = arr->get(JSRegExp::kIrregexpUC16CodeIndex);
CHECK((uc16_data->IsSmi() &&
Smi::ToInt(uc16_data) == JSRegExp::kUninitializedValue) ||
(is_native ? uc16_data->IsCode() : uc16_data->IsByteArray()));
CHECK(arr->get(JSRegExp::kIrregexpCaptureCountIndex)->IsSmi());
CHECK(arr->get(JSRegExp::kIrregexpMaxRegisterCountIndex)->IsSmi());
break;
}
default:
CHECK_EQ(JSRegExp::NOT_COMPILED, TypeTag());
CHECK(data()->IsUndefined(isolate));
break;
}
}
void JSProxy::JSProxyVerify() {
CHECK(IsJSProxy());
VerifyPointer(target());
VerifyPointer(handler());
Isolate* isolate = GetIsolate();
if (!IsRevoked()) {
CHECK_EQ(target()->IsCallable(), map()->is_callable());
CHECK_EQ(target()->IsConstructor(), map()->is_constructor());
}
CHECK(map()->prototype()->IsNull(isolate));
// There should be no properties on a Proxy.
CHECK_EQ(0, map()->NumberOfOwnDescriptors());
}
void JSArrayBuffer::JSArrayBufferVerify() {
CHECK(IsJSArrayBuffer());
JSObjectVerify();
VerifyPointer(byte_length());
CHECK(byte_length()->IsSmi() || byte_length()->IsHeapNumber() ||
byte_length()->IsUndefined(GetIsolate()));
}
void JSArrayBufferView::JSArrayBufferViewVerify() {
CHECK(IsJSArrayBufferView());
JSObjectVerify();
VerifyPointer(buffer());
Isolate* isolate = GetIsolate();
CHECK(buffer()->IsJSArrayBuffer() || buffer()->IsUndefined(isolate) ||
buffer() == Smi::kZero);
VerifyPointer(raw_byte_offset());
CHECK(raw_byte_offset()->IsSmi() || raw_byte_offset()->IsHeapNumber() ||
raw_byte_offset()->IsUndefined(isolate));
VerifyPointer(raw_byte_length());
CHECK(raw_byte_length()->IsSmi() || raw_byte_length()->IsHeapNumber() ||
raw_byte_length()->IsUndefined(isolate));
}
void JSTypedArray::JSTypedArrayVerify() {
CHECK(IsJSTypedArray());
JSArrayBufferViewVerify();
VerifyPointer(raw_length());
CHECK(raw_length()->IsSmi() || raw_length()->IsUndefined(GetIsolate()));
VerifyPointer(elements());
}
void JSDataView::JSDataViewVerify() {
CHECK(IsJSDataView());
JSArrayBufferViewVerify();
}
void Foreign::ForeignVerify() {
CHECK(IsForeign());
}
void PromiseResolveThenableJobInfo::PromiseResolveThenableJobInfoVerify() {
CHECK(IsPromiseResolveThenableJobInfo());
CHECK(thenable()->IsJSReceiver());
CHECK(then()->IsJSReceiver());
CHECK(resolve()->IsJSFunction());
CHECK(reject()->IsJSFunction());
CHECK(context()->IsContext());
}
void PromiseReactionJobInfo::PromiseReactionJobInfoVerify() {
Isolate* isolate = GetIsolate();
CHECK(IsPromiseReactionJobInfo());
CHECK(value()->IsObject());
CHECK(tasks()->IsFixedArray() || tasks()->IsCallable() ||
tasks()->IsSymbol());
CHECK(deferred_promise()->IsUndefined(isolate) ||
deferred_promise()->IsJSReceiver() ||
deferred_promise()->IsFixedArray());
CHECK(deferred_on_resolve()->IsUndefined(isolate) ||
deferred_on_resolve()->IsCallable() ||
deferred_on_resolve()->IsFixedArray());
CHECK(deferred_on_reject()->IsUndefined(isolate) ||
deferred_on_reject()->IsCallable() ||
deferred_on_reject()->IsFixedArray());
CHECK(context()->IsContext());
}
void AsyncGeneratorRequest::AsyncGeneratorRequestVerify() {
CHECK(IsAsyncGeneratorRequest());
VerifySmiField(kResumeModeOffset);
CHECK_GE(resume_mode(), JSGeneratorObject::kNext);
CHECK_LE(resume_mode(), JSGeneratorObject::kThrow);
CHECK(promise()->IsJSPromise());
VerifyPointer(value());
VerifyPointer(next());
next()->ObjectVerify();
}
void BigInt::BigIntVerify() {
CHECK(IsBigInt());
CHECK_GE(length(), 0);
CHECK_IMPLIES(is_zero(), !sign()); // There is no -0n.
// TODO(neis): Somewhere check that MSD is non-zero. Doesn't hold during some
// operations that allocate which is why we can't test it here.
}
void JSModuleNamespace::JSModuleNamespaceVerify() {
CHECK(IsJSModuleNamespace());
VerifyPointer(module());
}
void ModuleInfoEntry::ModuleInfoEntryVerify() {
Isolate* isolate = GetIsolate();
CHECK(IsModuleInfoEntry());
CHECK(export_name()->IsUndefined(isolate) || export_name()->IsString());
CHECK(local_name()->IsUndefined(isolate) || local_name()->IsString());
CHECK(import_name()->IsUndefined(isolate) || import_name()->IsString());
VerifySmiField(kModuleRequestOffset);
VerifySmiField(kCellIndexOffset);
VerifySmiField(kBegPosOffset);
VerifySmiField(kEndPosOffset);
CHECK_IMPLIES(import_name()->IsString(), module_request() >= 0);
CHECK_IMPLIES(export_name()->IsString() && import_name()->IsString(),
local_name()->IsUndefined(isolate));
}
void Module::ModuleVerify() {
CHECK(IsModule());
VerifyPointer(code());
VerifyPointer(exports());
VerifyPointer(module_namespace());
VerifyPointer(requested_modules());
VerifyPointer(script());
VerifyPointer(import_meta());
VerifyPointer(exception());
VerifySmiField(kHashOffset);
VerifySmiField(kStatusOffset);
CHECK((status() >= kEvaluating && code()->IsModuleInfo()) ||
(status() == kInstantiated && code()->IsJSGeneratorObject()) ||
(status() >= kInstantiating && code()->IsJSFunction()) ||
(code()->IsSharedFunctionInfo()));
CHECK_EQ(status() == kErrored, !exception()->IsTheHole(GetIsolate()));
CHECK(module_namespace()->IsUndefined(GetIsolate()) ||
module_namespace()->IsJSModuleNamespace());
if (module_namespace()->IsJSModuleNamespace()) {
CHECK_LE(kInstantiating, status());
CHECK_EQ(JSModuleNamespace::cast(module_namespace())->module(), this);
}
CHECK_EQ(requested_modules()->length(), info()->module_requests()->length());
CHECK(import_meta()->IsTheHole(GetIsolate()) || import_meta()->IsJSObject());
CHECK_NE(hash(), 0);
}
void PrototypeInfo::PrototypeInfoVerify() {
CHECK(IsPrototypeInfo());
CHECK(weak_cell()->IsWeakCell() || weak_cell()->IsUndefined(GetIsolate()));
if (prototype_users()->IsWeakFixedArray()) {
WeakFixedArray::cast(prototype_users())->FixedArrayVerify();
} else {
CHECK(prototype_users()->IsSmi());
}
CHECK(validity_cell()->IsSmi() || validity_cell()->IsCell());
}
void Tuple2::Tuple2Verify() {
CHECK(IsTuple2());
Heap* heap = GetHeap();
if (this == heap->empty_enum_cache()) {
CHECK_EQ(heap->empty_fixed_array(), EnumCache::cast(this)->keys());
CHECK_EQ(heap->empty_fixed_array(), EnumCache::cast(this)->indices());
} else {
VerifyObjectField(kValue1Offset);
VerifyObjectField(kValue2Offset);
}
}
void Tuple3::Tuple3Verify() {
CHECK(IsTuple3());
VerifyObjectField(kValue1Offset);
VerifyObjectField(kValue2Offset);
VerifyObjectField(kValue3Offset);
}
void DataHandler::DataHandlerVerify() {
CHECK(IsDataHandler());
CHECK_IMPLIES(!smi_handler()->IsSmi(),
smi_handler()->IsCode() && IsStoreHandler());
CHECK(validity_cell()->IsSmi() || validity_cell()->IsCell());
int data_count = data_field_count();
if (data_count >= 1) {
VerifyObjectField(kData1Offset);
}
if (data_count >= 2) {
VerifyObjectField(kData2Offset);
}
if (data_count >= 3) {
VerifyObjectField(kData3Offset);
}
}
void LoadHandler::LoadHandlerVerify() {
DataHandler::DataHandlerVerify();
// TODO(ishell): check handler integrity
}
void StoreHandler::StoreHandlerVerify() {
DataHandler::DataHandlerVerify();
// TODO(ishell): check handler integrity
}
void ContextExtension::ContextExtensionVerify() {
CHECK(IsContextExtension());
VerifyObjectField(kScopeInfoOffset);
VerifyObjectField(kExtensionOffset);
}
void AccessorInfo::AccessorInfoVerify() {
CHECK(IsAccessorInfo());
VerifyPointer(name());
VerifyPointer(expected_receiver_type());
VerifyForeignPointer(this, getter());
VerifyForeignPointer(this, setter());
VerifyForeignPointer(this, js_getter());
VerifyPointer(data());
}
void AccessorPair::AccessorPairVerify() {
CHECK(IsAccessorPair());
VerifyPointer(getter());
VerifyPointer(setter());
}
void AccessCheckInfo::AccessCheckInfoVerify() {
CHECK(IsAccessCheckInfo());
VerifyPointer(callback());
VerifyPointer(named_interceptor());
VerifyPointer(indexed_interceptor());
VerifyPointer(data());
}
void InterceptorInfo::InterceptorInfoVerify() {
CHECK(IsInterceptorInfo());
VerifyForeignPointer(this, getter());
VerifyForeignPointer(this, setter());
VerifyForeignPointer(this, query());
VerifyForeignPointer(this, deleter());
VerifyForeignPointer(this, enumerator());
VerifyPointer(data());
VerifySmiField(kFlagsOffset);
}
void TemplateInfo::TemplateInfoVerify() {
VerifyPointer(tag());
VerifyPointer(property_list());
VerifyPointer(property_accessors());
}
void FunctionTemplateInfo::FunctionTemplateInfoVerify() {
CHECK(IsFunctionTemplateInfo());
TemplateInfoVerify();
VerifyPointer(serial_number());
VerifyPointer(call_code());
VerifyPointer(prototype_template());
VerifyPointer(parent_template());
VerifyPointer(named_property_handler());
VerifyPointer(indexed_property_handler());
VerifyPointer(instance_template());
VerifyPointer(signature());
VerifyPointer(access_check_info());
VerifyPointer(cached_property_name());
}
void ObjectTemplateInfo::ObjectTemplateInfoVerify() {
CHECK(IsObjectTemplateInfo());
TemplateInfoVerify();
VerifyPointer(constructor());
VerifyPointer(data());
}
void AllocationSite::AllocationSiteVerify() {
CHECK(IsAllocationSite());
}
void AllocationMemento::AllocationMementoVerify() {
CHECK(IsAllocationMemento());
VerifyHeapPointer(allocation_site());
CHECK(!IsValid() || GetAllocationSite()->IsAllocationSite());
}
void Script::ScriptVerify() {
CHECK(IsScript());
VerifyPointer(source());
VerifyPointer(name());
VerifyPointer(wrapper());
VerifyPointer(line_ends());
}
void NormalizedMapCache::NormalizedMapCacheVerify() {
FixedArray::cast(this)->FixedArrayVerify();
if (FLAG_enable_slow_asserts) {
Isolate* isolate = GetIsolate();
for (int i = 0; i < length(); i++) {
Object* e = FixedArray::get(i);
if (e->IsWeakCell()) {
if (!WeakCell::cast(e)->cleared()) {
Map::cast(WeakCell::cast(e)->value())->DictionaryMapVerify();
}
} else {
CHECK(e->IsUndefined(isolate));
}
}
}
}
void DebugInfo::DebugInfoVerify() {
CHECK(IsDebugInfo());
VerifyPointer(shared());
VerifyPointer(debug_bytecode_array());
VerifyPointer(break_points());
}
void StackFrameInfo::StackFrameInfoVerify() {
CHECK(IsStackFrameInfo());
VerifyPointer(script_name());
VerifyPointer(script_name_or_source_url());
VerifyPointer(function_name());
}
void PreParsedScopeData::PreParsedScopeDataVerify() {
CHECK(IsPreParsedScopeData());
CHECK(scope_data()->IsByteArray());
CHECK(child_data()->IsFixedArray());
}
#endif // VERIFY_HEAP
#ifdef DEBUG
void JSObject::IncrementSpillStatistics(SpillInformation* info) {
info->number_of_objects_++;
// Named properties
if (HasFastProperties()) {
info->number_of_objects_with_fast_properties_++;
info->number_of_fast_used_fields_ += map()->NextFreePropertyIndex();
info->number_of_fast_unused_fields_ += map()->UnusedPropertyFields();
} else if (IsJSGlobalObject()) {
GlobalDictionary* dict = JSGlobalObject::cast(this)->global_dictionary();
info->number_of_slow_used_properties_ += dict->NumberOfElements();
info->number_of_slow_unused_properties_ +=
dict->Capacity() - dict->NumberOfElements();
} else {
NameDictionary* dict = property_dictionary();
info->number_of_slow_used_properties_ += dict->NumberOfElements();
info->number_of_slow_unused_properties_ +=
dict->Capacity() - dict->NumberOfElements();
}
// Indexed properties
switch (GetElementsKind()) {
case HOLEY_SMI_ELEMENTS:
case PACKED_SMI_ELEMENTS:
case HOLEY_DOUBLE_ELEMENTS:
case PACKED_DOUBLE_ELEMENTS:
case HOLEY_ELEMENTS:
case PACKED_ELEMENTS:
case FAST_STRING_WRAPPER_ELEMENTS: {
info->number_of_objects_with_fast_elements_++;
int holes = 0;
FixedArray* e = FixedArray::cast(elements());
int len = e->length();
Isolate* isolate = GetIsolate();
for (int i = 0; i < len; i++) {
if (e->get(i)->IsTheHole(isolate)) holes++;
}
info->number_of_fast_used_elements_ += len - holes;
info->number_of_fast_unused_elements_ += holes;
break;
}
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
{ info->number_of_objects_with_fast_elements_++;
FixedArrayBase* e = FixedArrayBase::cast(elements());
info->number_of_fast_used_elements_ += e->length();
break;
}
case DICTIONARY_ELEMENTS:
case SLOW_STRING_WRAPPER_ELEMENTS: {
NumberDictionary* dict = element_dictionary();
info->number_of_slow_used_elements_ += dict->NumberOfElements();
info->number_of_slow_unused_elements_ +=
dict->Capacity() - dict->NumberOfElements();
break;
}
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
case NO_ELEMENTS:
break;
}
}
void JSObject::SpillInformation::Clear() {
number_of_objects_ = 0;
number_of_objects_with_fast_properties_ = 0;
number_of_objects_with_fast_elements_ = 0;
number_of_fast_used_fields_ = 0;
number_of_fast_unused_fields_ = 0;
number_of_slow_used_properties_ = 0;
number_of_slow_unused_properties_ = 0;
number_of_fast_used_elements_ = 0;
number_of_fast_unused_elements_ = 0;
number_of_slow_used_elements_ = 0;
number_of_slow_unused_elements_ = 0;
}
void JSObject::SpillInformation::Print() {
PrintF("\n JSObject Spill Statistics (#%d):\n", number_of_objects_);
PrintF(" - fast properties (#%d): %d (used) %d (unused)\n",
number_of_objects_with_fast_properties_,
number_of_fast_used_fields_, number_of_fast_unused_fields_);
PrintF(" - slow properties (#%d): %d (used) %d (unused)\n",
number_of_objects_ - number_of_objects_with_fast_properties_,
number_of_slow_used_properties_, number_of_slow_unused_properties_);
PrintF(" - fast elements (#%d): %d (used) %d (unused)\n",
number_of_objects_with_fast_elements_,
number_of_fast_used_elements_, number_of_fast_unused_elements_);
PrintF(" - slow elements (#%d): %d (used) %d (unused)\n",
number_of_objects_ - number_of_objects_with_fast_elements_,
number_of_slow_used_elements_, number_of_slow_unused_elements_);
PrintF("\n");
}
bool DescriptorArray::IsSortedNoDuplicates(int valid_entries) {
if (valid_entries == -1) valid_entries = number_of_descriptors();
Name* current_key = nullptr;
uint32_t current = 0;
for (int i = 0; i < number_of_descriptors(); i++) {
Name* key = GetSortedKey(i);
if (key == current_key) {
Print();
return false;
}
current_key = key;
uint32_t hash = GetSortedKey(i)->Hash();
if (hash < current) {
Print();
return false;
}
current = hash;
}
return true;
}
bool TransitionArray::IsSortedNoDuplicates(int valid_entries) {
DCHECK_EQ(valid_entries, -1);
Name* prev_key = nullptr;
PropertyKind prev_kind = kData;
PropertyAttributes prev_attributes = NONE;
uint32_t prev_hash = 0;
for (int i = 0; i < number_of_transitions(); i++) {
Name* key = GetSortedKey(i);
uint32_t hash = key->Hash();
PropertyKind kind = kData;
PropertyAttributes attributes = NONE;
if (!TransitionsAccessor::IsSpecialTransition(key)) {
Map* target = GetTarget(i);
PropertyDetails details =
TransitionsAccessor::GetTargetDetails(key, target);
kind = details.kind();
attributes = details.attributes();
} else {
// Duplicate entries are not allowed for non-property transitions.
DCHECK_NE(prev_key, key);
}
int cmp = CompareKeys(prev_key, prev_hash, prev_kind, prev_attributes, key,
hash, kind, attributes);
if (cmp >= 0) {
Print();
return false;
}
prev_key = key;
prev_hash = hash;
prev_attributes = attributes;
prev_kind = kind;
}
return true;
}
bool TransitionsAccessor::IsSortedNoDuplicates() {
// Simple and non-existent transitions are always sorted.
if (encoding() != kFullTransitionArray) return true;
return transitions()->IsSortedNoDuplicates();
}
static bool CheckOneBackPointer(Map* current_map, Object* target) {
return !target->IsMap() || Map::cast(target)->GetBackPointer() == current_map;
}
bool TransitionsAccessor::IsConsistentWithBackPointers() {
int num_transitions = NumberOfTransitions();
for (int i = 0; i < num_transitions; i++) {
Map* target = GetTarget(i);
if (!CheckOneBackPointer(map_, target)) return false;
}
return true;
}
// Estimates if there is a path from the object to a context.
// This function is not precise, and can return false even if
// there is a path to a context.
bool CanLeak(Object* obj, Heap* heap, bool skip_weak_cell) {
if (!obj->IsHeapObject()) return false;
if (obj->IsWeakCell()) {
if (skip_weak_cell) return false;
return CanLeak(WeakCell::cast(obj)->value(), heap, skip_weak_cell);
}
if (obj->IsCell()) {
return CanLeak(Cell::cast(obj)->value(), heap, skip_weak_cell);
}
if (obj->IsPropertyCell()) {
return CanLeak(PropertyCell::cast(obj)->value(), heap, skip_weak_cell);
}
if (obj->IsContext()) return true;
if (obj->IsMap()) {
Map* map = Map::cast(obj);
for (int i = 0; i < Heap::kStrongRootListLength; i++) {
Heap::RootListIndex root_index = static_cast<Heap::RootListIndex>(i);
if (map == heap->root(root_index)) return false;
}
return true;
}
return CanLeak(HeapObject::cast(obj)->map(), heap, skip_weak_cell);
}
void Code::VerifyEmbeddedObjects(VerifyMode mode) {
if (kind() == OPTIMIZED_FUNCTION) return;
Heap* heap = GetIsolate()->heap();
int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
bool skip_weak_cell = (mode == kNoContextSpecificPointers) ? false : true;
for (RelocIterator it(this, mask); !it.done(); it.next()) {
Object* target = it.rinfo()->target_object();
DCHECK(!CanLeak(target, heap, skip_weak_cell));
}
}
#endif // DEBUG
} // namespace internal
} // namespace v8