// 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 <iomanip>
#include <memory>
#include <optional>
#include "include/v8-internal.h"
#include "src/api/api-arguments.h"
#include "src/base/logging.h"
#include "src/common/assert-scope.h"
#include "src/common/globals.h"
#include "src/diagnostics/disasm.h"
#include "src/diagnostics/disassembler.h"
#include "src/execution/frames-inl.h"
#include "src/execution/isolate-utils-inl.h"
#include "src/heap/heap-inl.h" // For InOldSpace.
#include "src/heap/heap-layout-inl.h"
#include "src/heap/heap-write-barrier-inl.h" // For GetIsolateFromWritableObj.
#include "src/heap/marking-inl.h"
#include "src/ic/handler-configuration-inl.h"
#include "src/init/bootstrapper.h"
#include "src/interpreter/bytecodes.h"
#include "src/objects/all-objects-inl.h"
#include "src/objects/allocation-site.h"
#include "src/objects/code-kind.h"
#include "src/objects/contexts.h"
#include "src/objects/cpp-heap-object-wrapper-inl.h"
#include "src/objects/instance-type.h"
#include "src/objects/js-function-inl.h"
#include "src/objects/js-objects.h"
#include "src/objects/js-shared-array.h"
#include "src/objects/shared-function-info-inl.h"
#include "src/objects/struct.h"
#include "src/regexp/regexp.h"
#include "src/sandbox/isolate.h"
#include "src/sandbox/js-dispatch-table.h"
#include "src/snapshot/embedded/embedded-data.h"
#include "src/strings/string-stream.h"
#include "src/utils/ostreams.h"
#include "third_party/fp16/src/include/fp16.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/debug/debug-wasm-objects-inl.h"
#include "src/wasm/canonical-types.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-code-pointer-table-inl.h"
#include "src/wasm/wasm-engine-globals.h"
#include "src/wasm/wasm-objects-inl.h"
#endif // V8_ENABLE_WEBASSEMBLY
namespace v8::internal {
namespace {
constexpr char kUnavailableString[] = "unavailable";
void PrintDouble(std::ostream& os, double val) {
if (i::IsMinusZero(val)) {
os << "-0.0";
} else if (val == DoubleToInteger(val) && val >= kMinSafeInteger &&
val <= kMaxSafeInteger) {
// Print integer HeapNumbers in safe integer range with max precision: as
// 9007199254740991.0 instead of 9.0072e+15
int64_t i = static_cast<int64_t>(val);
os << i << ".0";
} else if (std::isnan(val)) {
os << val << " (0x" << std::hex << base::double_to_uint64(val) << std::dec
<< ")";
} else {
os << val;
}
}
} // namespace
#ifdef OBJECT_PRINT
const char* JSIteratorHelperStateToString(JSIteratorHelperState state) {
switch (state) {
case JSIteratorHelperState::kSuspendedStart:
return "SUSPENDED_START";
case JSIteratorHelperState::kSuspendedYield:
return "SUSPENDED_YIELD";
case JSIteratorHelperState::kExecuting:
return "EXECUTING";
case JSIteratorHelperState::kCompleted:
return "COMPLETED";
}
UNREACHABLE();
}
std::ostream& operator<<(std::ostream& os, JSIteratorHelperState state) {
return os << JSIteratorHelperStateToString(state);
}
const char* JSIteratorZipHelperModeToString(JSIteratorZipHelperMode mode) {
switch (mode) {
case JSIteratorZipHelperMode::kShortest:
return "SHORTEST";
case JSIteratorZipHelperMode::kLongest:
return "LONGEST";
case JSIteratorZipHelperMode::kStrict:
return "STRICT";
}
UNREACHABLE();
}
std::ostream& operator<<(std::ostream& os, JSIteratorZipHelperMode mode) {
return os << JSIteratorZipHelperModeToString(mode);
}
void Print(Tagged<Object> obj) {
// Output into debugger's command window if a debugger is attached.
DbgStdoutStream dbg_os;
Print(obj, dbg_os);
dbg_os << std::flush;
StdoutStream os;
Print(obj, os);
os << std::flush;
}
void Print(Tagged<Object> obj, std::ostream& os) {
if (IsSmi(obj)) {
os << "Smi: " << std::hex << "0x" << Smi::ToInt(obj);
os << std::dec << " (" << Smi::ToInt(obj) << ")\n";
} else {
Cast<HeapObject>(obj)->HeapObjectPrint(os);
}
}
namespace {
#define AS_PTR(x) reinterpret_cast<void*>(x)
#define AS_OBJ(x) Brief(Tagged<Object>(x))
void PrintFunctionCallbackInfo(Address* values, std::ostream& os) {
using FCA = FunctionCallbackArguments;
static_assert(FCA::kApiArgsLength == 4);
uint32_t argc = static_cast<uint32_t>(values[FCA::kArgcIndex]);
bool is_construct = values[FCA::kFrameTypeIndex] ==
Smi::FromEnum(StackFrame::API_CONSTRUCT_EXIT).ptr();
os << "FunctionCallbackInfo: "
<< (is_construct ? "[[Construct]]" : "[[Call]]")
<< "\n - isolate: " << AS_PTR(values[FCA::kIsolateIndex])
<< "\n - return_value: " << AS_OBJ(values[FCA::kReturnValueIndex])
<< "\n - target: " << AS_OBJ(values[FCA::kTargetIndex]);
if (is_construct) {
os << "\n - new_target: " << AS_OBJ(values[FCA::kNewTargetIndex]);
}
os << "\n - argc: " << argc
<< "\n - receiver: " << AS_OBJ(values[FCA::kReceiverIndex]);
constexpr uint32_t kMaxArgs = 4;
Address* js_args = &values[FCA::kFirstJSArgumentIndex];
for (uint32_t i = 0; i < std::min(argc, kMaxArgs); i++) {
os << "\n - arg[" << i << "]: " << AS_OBJ(js_args[i]);
}
os << "\n";
}
void PrintPropertyCallbackInfo(Address* args, std::ostream& os) {
using PCA = internal::PropertyCallbackArguments;
static_assert(PCA::kFullArgsLength == 11 || PCA::kFullArgsLength == 12);
bool is_named = args[PCA::kFrameTypeIndex] ==
Smi::FromEnum(StackFrame::API_NAMED_ACCESSOR_EXIT).ptr();
os << "PropertyCallbackInfo: " << (is_named ? "(named)" : "(indexed)")
<< "\n - isolate: " << AS_PTR(args[PCA::kIsolateIndex])
<< "\n - return_value: " << AS_OBJ(args[PCA::kReturnValueIndex])
<< "\n - holder: " << AS_OBJ(args[PCA::kHolderIndex])
<< "\n - callback_info: " << AS_OBJ(args[PCA::kCallbackInfoIndex]);
if (is_named) {
os << "\n - property_name: " << AS_OBJ(args[PCA::kPropertyKeyIndex]);
} else {
os << "\n - property_index: " << args[PCA::kPropertyKeyIndex];
}
// In case it's a setter/definer/deleter call there will be additional
// |should_throw_on_error| and |value| parameters, print them as a raw
// pointer to avoid crashing. |value| parameter is initialized only for
// calls through builtin.
os << "\n - should_throw?: " << AS_PTR(args[PCA::kShouldThrowOnErrorIndex])
<< "\n - value?: " << AS_PTR(args[PCA::kValueIndex]);
os << "\n";
}
#undef AS_PTR
#undef AS_OBJ
} // namespace
void PrintFunctionCallbackInfo(void* function_callback_info) {
using FCI = v8::FunctionCallbackInfo<v8::Value>;
FCI& info = *reinterpret_cast<FCI*>(function_callback_info);
// Output into debugger's command window if a debugger is attached.
DbgStdoutStream dbg_os;
PrintFunctionCallbackInfo(info.values_, dbg_os);
dbg_os << std::flush;
StdoutStream os;
PrintFunctionCallbackInfo(info.values_, os);
os << std::flush;
}
void PrintPropertyCallbackInfo(void* property_callback_info) {
using PCI = v8::PropertyCallbackInfo<v8::Value>;
PCI& info = *reinterpret_cast<PCI*>(property_callback_info);
// Output into debugger's command window if a debugger is attached.
DbgStdoutStream dbg_os;
PrintPropertyCallbackInfo(info.args_, dbg_os);
dbg_os << std::flush;
StdoutStream os;
PrintPropertyCallbackInfo(info.args_, os);
os << std::flush;
}
namespace {
void PrintHeapObjectHeaderWithoutMap(Tagged<HeapObject> object,
std::ostream& os, const char* id) {
os << reinterpret_cast<void*>(object.ptr()) << ": [";
if (id != nullptr) {
os << id;
} else {
os << object->map()->instance_type();
}
os << "]";
if (ReadOnlyHeap::Contains(object)) {
os << " in ReadOnlySpace";
} else if (Isolate::Current()->heap()->InOldSpace(object)) {
os << " in OldSpace";
} else if (HeapLayout::InAnySharedSpace(object)) {
os << " in SharedSpace";
}
}
template <typename T>
void PrintDictionaryContents(std::ostream& os, Tagged<T> dict) {
DisallowGarbageCollection no_gc;
ReadOnlyRoots roots = GetReadOnlyRoots();
if (dict->Capacity() == 0) return;
#ifdef V8_ENABLE_SWISS_NAME_DICTIONARY
Isolate* isolate = Isolate::Current();
// IterateEntries for SwissNameDictionary needs to create a handle.
HandleScope scope(isolate);
#endif
for (InternalIndex i : dict->IterateEntries()) {
Tagged<Object> k;
if (!dict->ToKey(roots, i, &k)) continue;
os << "\n ";
if (IsString(k)) {
Cast<String>(k)->PrintUC16(os);
} else {
os << Brief(k);
}
os << ": " << Brief(dict->ValueAt(i)) << " ";
dict->DetailsAt(i).PrintAsSlowTo(os, !T::kIsOrderedDictionaryType);
}
}
} // namespace
void HeapObject::PrintHeader(std::ostream& os, const char* id) {
PrintHeapObjectHeaderWithoutMap(this, os, id);
if (!IsMetaMap(this)) {
os << "\n - map: " << Brief(map());
}
}
void HeapObject::HeapObjectPrint(std::ostream& os) {
#if V8_ENABLE_WEBASSEMBLY
if (IsWasmNull(Tagged<HeapObject>(this))) {
os << "WasmNull";
return;
}
#endif // V8_ENABLE_WEBASSEMBLY
InstanceType instance_type = map()->instance_type();
if (instance_type < FIRST_NONSTRING_TYPE) {
Cast<String>(this)->StringPrint(os);
os << "\n";
return;
}
// Skip invalid trusted objects. Technically it'd be fine to still handle
// them below since we only print the objects, but such an object will
// quickly lead to out-of-sandbox segfaults and so fuzzers will complain.
if (InstanceTypeChecker::IsTrustedObject(instance_type) &&
!OutsideSandboxOrInReadonlySpace(this)) {
os << "<Invalid TrustedObject (outside trusted space)>\n";
return;
}
switch (instance_type) {
case AWAIT_CONTEXT_TYPE:
case BLOCK_CONTEXT_TYPE:
case CATCH_CONTEXT_TYPE:
case DEBUG_EVALUATE_CONTEXT_TYPE:
case EVAL_CONTEXT_TYPE:
case FUNCTION_CONTEXT_TYPE:
case MODULE_CONTEXT_TYPE:
case SCRIPT_CONTEXT_TYPE:
case WITH_CONTEXT_TYPE:
Cast<Context>(this)->ContextPrint(os);
break;
case NATIVE_CONTEXT_TYPE:
Cast<NativeContext>(this)->NativeContextPrint(os);
break;
case HASH_TABLE_TYPE:
Cast<ObjectHashTable>(this)->ObjectHashTablePrint(os);
break;
case DOUBLE_STRING_CACHE_TYPE:
Cast<DoubleStringCache>(this)->DoubleStringCachePrint(os);
break;
case HASH_SEED_WRAPPER_TYPE:
Cast<HashSeedWrapper>(this)->HashSeedWrapperPrint(os);
break;
case NAME_TO_INDEX_HASH_TABLE_TYPE:
Cast<NameToIndexHashTable>(this)->NameToIndexHashTablePrint(os);
break;
case REGISTERED_SYMBOL_TABLE_TYPE:
Cast<RegisteredSymbolTable>(this)->RegisteredSymbolTablePrint(os);
break;
case ORDERED_HASH_MAP_TYPE:
Cast<OrderedHashMap>(this)->OrderedHashMapPrint(os);
break;
case ORDERED_HASH_SET_TYPE:
Cast<OrderedHashSet>(this)->OrderedHashSetPrint(os);
break;
case ORDERED_NAME_DICTIONARY_TYPE:
Cast<OrderedNameDictionary>(this)->OrderedNameDictionaryPrint(os);
break;
case NAME_DICTIONARY_TYPE:
Cast<NameDictionary>(this)->NameDictionaryPrint(os);
break;
case GLOBAL_DICTIONARY_TYPE:
Cast<GlobalDictionary>(this)->GlobalDictionaryPrint(os);
break;
case SIMPLE_NAME_DICTIONARY_TYPE:
Cast<FixedArray>(this)->FixedArrayPrint(os);
break;
case SIMPLE_NUMBER_DICTIONARY_TYPE:
Cast<FixedArray>(this)->FixedArrayPrint(os);
break;
case NUMBER_DICTIONARY_TYPE:
Cast<NumberDictionary>(this)->NumberDictionaryPrint(os);
break;
case EPHEMERON_HASH_TABLE_TYPE:
Cast<EphemeronHashTable>(this)->EphemeronHashTablePrint(os);
break;
case TRANSITION_ARRAY_TYPE:
Cast<TransitionArray>(this)->TransitionArrayPrint(os);
break;
case FILLER_TYPE:
os << "filler";
break;
case JS_API_OBJECT_TYPE:
case JS_ARRAY_ITERATOR_PROTOTYPE_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_ERROR_TYPE:
case JS_ITERATOR_PROTOTYPE_TYPE:
case JS_MAP_ITERATOR_PROTOTYPE_TYPE:
case JS_OBJECT_PROTOTYPE_TYPE:
case JS_PROMISE_PROTOTYPE_TYPE:
case JS_REG_EXP_PROTOTYPE_TYPE:
case JS_SET_ITERATOR_PROTOTYPE_TYPE:
case JS_SET_PROTOTYPE_TYPE:
case JS_SPECIAL_API_OBJECT_TYPE:
case JS_STRING_ITERATOR_PROTOTYPE_TYPE:
case JS_TYPED_ARRAY_PROTOTYPE_TYPE:
Cast<JSObject>(this)->JSObjectPrint(os);
break;
#if V8_ENABLE_WEBASSEMBLY
case WASM_TRUSTED_INSTANCE_DATA_TYPE:
TrustedCast<WasmTrustedInstanceData>(this)->WasmTrustedInstanceDataPrint(
os);
break;
case WASM_DISPATCH_TABLE_TYPE:
TrustedCast<WasmDispatchTable>(this)->WasmDispatchTablePrint(os);
break;
case WASM_DISPATCH_TABLE_FOR_IMPORTS_TYPE:
TrustedCast<WasmDispatchTableForImports>(this)
->WasmDispatchTableForImportsPrint(os);
break;
case WASM_VALUE_OBJECT_TYPE:
Cast<WasmValueObject>(this)->WasmValueObjectPrint(os);
break;
case WASM_EXCEPTION_PACKAGE_TYPE:
Cast<WasmExceptionPackage>(this)->WasmExceptionPackagePrint(os);
break;
case WASM_NULL_TYPE:
// Handled before the switch.
UNREACHABLE();
#endif // V8_ENABLE_WEBASSEMBLY
case INSTRUCTION_STREAM_TYPE:
TrustedCast<InstructionStream>(this)->InstructionStreamPrint(os);
break;
case CODE_TYPE:
TrustedCast<Code>(this)->CodePrint(os);
break;
case CODE_WRAPPER_TYPE:
Cast<CodeWrapper>(this)->CodeWrapperPrint(os);
break;
case JS_SET_KEY_VALUE_ITERATOR_TYPE:
case JS_SET_VALUE_ITERATOR_TYPE:
Cast<JSSetIterator>(this)->JSSetIteratorPrint(os);
break;
case JS_MAP_KEY_ITERATOR_TYPE:
case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
case JS_MAP_VALUE_ITERATOR_TYPE:
Cast<JSMapIterator>(this)->JSMapIteratorPrint(os);
break;
#define MAKE_PRINT_CASE(Name, TYPE) \
case TYPE: \
TrustedCast<Name>(this)->Name##Print(os); \
break;
HEAP_OBJECT_DIAGNOSTIC_DISPATCH_LIST(MAKE_PRINT_CASE)
#undef MAKE_PRINT_CASE
case HOLE_TYPE:
Cast<Hole>(this)->HolePrint(os);
break;
case TUPLE2_TYPE:
Cast<Tuple2>(this)->Tuple2Print(os);
break;
case CLASS_POSITIONS_TYPE:
Cast<ClassPositions>(this)->ClassPositionsPrint(os);
break;
case ACCESSOR_PAIR_TYPE:
Cast<AccessorPair>(this)->AccessorPairPrint(os);
break;
case ALLOCATION_SITE_TYPE:
Cast<AllocationSite>(this)->AllocationSitePrint(os);
break;
case LOAD_HANDLER_TYPE:
Cast<LoadHandler>(this)->LoadHandlerPrint(os);
break;
case STORE_HANDLER_TYPE:
Cast<StoreHandler>(this)->StoreHandlerPrint(os);
break;
case BIG_INT_BASE_TYPE:
Cast<BigIntBase>(this)->BigIntBasePrint(os);
break;
case FREE_SPACE_TYPE:
Cast<FreeSpace>(this)->FreeSpacePrint(os);
break;
case JS_CLASS_CONSTRUCTOR_TYPE:
case JS_PROMISE_CONSTRUCTOR_TYPE:
case JS_REG_EXP_CONSTRUCTOR_TYPE:
case JS_ARRAY_CONSTRUCTOR_TYPE:
#define TYPED_ARRAY_CONSTRUCTORS_SWITCH(Type, type, TYPE, Ctype) \
case TYPE##_TYPED_ARRAY_CONSTRUCTOR_TYPE:
TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTORS_SWITCH)
#undef TYPED_ARRAY_CONSTRUCTORS_SWITCH
Cast<JSFunction>(this)->JSFunctionPrint(os);
break;
case INTERNALIZED_TWO_BYTE_STRING_TYPE:
case EXTERNAL_INTERNALIZED_TWO_BYTE_STRING_TYPE:
case INTERNALIZED_ONE_BYTE_STRING_TYPE:
case EXTERNAL_INTERNALIZED_ONE_BYTE_STRING_TYPE:
case UNCACHED_EXTERNAL_INTERNALIZED_TWO_BYTE_STRING_TYPE:
case UNCACHED_EXTERNAL_INTERNALIZED_ONE_BYTE_STRING_TYPE:
case SEQ_TWO_BYTE_STRING_TYPE:
case CONS_TWO_BYTE_STRING_TYPE:
case EXTERNAL_TWO_BYTE_STRING_TYPE:
case SLICED_TWO_BYTE_STRING_TYPE:
case THIN_TWO_BYTE_STRING_TYPE:
case SEQ_ONE_BYTE_STRING_TYPE:
case CONS_ONE_BYTE_STRING_TYPE:
case EXTERNAL_ONE_BYTE_STRING_TYPE:
case SLICED_ONE_BYTE_STRING_TYPE:
case THIN_ONE_BYTE_STRING_TYPE:
case UNCACHED_EXTERNAL_TWO_BYTE_STRING_TYPE:
case UNCACHED_EXTERNAL_ONE_BYTE_STRING_TYPE:
case SHARED_SEQ_TWO_BYTE_STRING_TYPE:
case SHARED_SEQ_ONE_BYTE_STRING_TYPE:
case SHARED_EXTERNAL_TWO_BYTE_STRING_TYPE:
case SHARED_EXTERNAL_ONE_BYTE_STRING_TYPE:
case SHARED_UNCACHED_EXTERNAL_TWO_BYTE_STRING_TYPE:
case SHARED_UNCACHED_EXTERNAL_ONE_BYTE_STRING_TYPE:
case JS_LAST_DUMMY_API_OBJECT_TYPE:
// TODO(all): Handle these types too.
os << "UNKNOWN TYPE " << map()->instance_type();
UNREACHABLE();
}
}
template <typename T>
void PrintByteArrayElements(std::ostream& os, const T* array) {
uint32_t length = array->ulength().value();
uint32_t i = 0;
while (i < length) {
os << " 0x" << std::setfill('0') << std::setw(4) << std::hex << i << ":";
uint32_t line_end = std::min(i + 16, length);
for (; i < line_end; ++i) {
os << " " << std::setfill('0') << std::setw(2) << std::hex
<< static_cast<int>(array->get(i));
}
os << "\n";
}
}
void ByteArray::ByteArrayPrint(std::ostream& os) {
PrintHeader(os, "ByteArray");
os << "\n - length: " << length()
<< "\n - begin: " << static_cast<void*>(begin()) << "\n";
PrintByteArrayElements(os, this);
}
void TrustedByteArray::TrustedByteArrayPrint(std::ostream& os) {
PrintHeader(os, "TrustedByteArray");
os << "\n - length: " << length()
<< "\n - begin: " << static_cast<void*>(begin()) << "\n";
PrintByteArrayElements(os, this);
}
void BytecodeArray::BytecodeArrayPrint(std::ostream& os) {
PrintHeader(os, "BytecodeArray");
os << "\n";
Disassemble(os);
}
void BytecodeWrapper::BytecodeWrapperPrint(std::ostream& os) {
PrintHeader(os, "BytecodeWrapper");
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
os << "\n bytecode: " << Brief(bytecode(isolate));
}
void FreeSpace::FreeSpacePrint(std::ostream& os) {
os << "free space, size " << Size() << "\n";
}
bool JSObject::PrintProperties(std::ostream& os) {
if (HasFastProperties()) {
Tagged<DescriptorArray> descs = map()->instance_descriptors();
for (InternalIndex i : map()->IterateOwnDescriptors()) {
os << "\n ";
descs->GetKey(i)->NamePrint(os);
os << ": ";
PropertyDetails details = descs->GetDetails(i);
switch (details.location()) {
case PropertyLocation::kField: {
FieldIndex field_index = FieldIndex::ForDetails(map(), details);
os << Brief(RawFastPropertyAt(field_index));
break;
}
case PropertyLocation::kDescriptor:
os << Brief(descs->GetStrongValue(i));
break;
}
os << " ";
details.PrintAsFastTo(os, PropertyDetails::kForProperties);
}
return map()->NumberOfOwnDescriptors() > 0;
} else if (Is<JSGlobalObject>(this)) {
PrintDictionaryContents(
os, Cast<JSGlobalObject>(this)->global_dictionary(kAcquireLoad));
} else if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
PrintDictionaryContents(os, property_dictionary_swiss());
} else {
PrintDictionaryContents(os, property_dictionary());
}
return true;
}
namespace {
template <class T>
bool IsTheHoleAt(Tagged<T> array, int index) {
return false;
}
template <>
bool IsTheHoleAt(Tagged<FixedDoubleArray> array, int index) {
return array->is_the_hole(index);
}
template <class T>
bool IsUndefinedAt(Tagged<T> array, int index) {
return false;
}
template <>
bool IsUndefinedAt(Tagged<FixedDoubleArray> array, int index) {
#ifdef V8_ENABLE_UNDEFINED_DOUBLE
return array->is_undefined(index);
#else
return false;
#endif // V8_ENABLE_UNDEFINED_DOUBLE
}
template <class T>
double GetScalarElement(Tagged<T> array, int index) {
if (IsTheHoleAt(array, index)) {
return std::numeric_limits<double>::quiet_NaN();
}
return array->get_scalar(index);
}
template <class T>
void DoPrintElements(std::ostream& os, Tagged<Object> object, uint32_t length) {
const bool print_nans = std::is_same_v<T, FixedDoubleArray>;
Tagged<T> array = Cast<T>(object);
if (length == 0) return;
uint32_t previous_index = 0;
uint64_t previous_representation = array->get_representation(0);
uint64_t representation = 0;
uint32_t i;
for (i = 1; i <= length; i++) {
if (i < length) {
representation = array->get_representation(i);
if (previous_representation == representation) continue;
}
os << "\n";
std::stringstream ss;
ss << previous_index;
if (previous_index != i - 1) {
ss << '-' << (i - 1);
}
os << std::setw(12) << ss.str() << ": ";
if (print_nans && IsTheHoleAt(array, i - 1)) {
os << "<the_hole>";
} else if (print_nans && IsUndefinedAt(array, i - 1)) {
os << "undefined";
} else {
auto value = GetScalarElement(array, i - 1);
os << value;
if constexpr (print_nans) {
os << " (0x" << std::hex << base::double_to_uint64(value) << std::dec
<< ")";
}
}
previous_index = i;
previous_representation = representation;
}
}
struct Fp16Printer {
uint16_t val;
explicit Fp16Printer(float f) : val(fp16_ieee_from_fp32_value(f)) {}
operator float() const { return fp16_ieee_to_fp32_value(val); }
};
template <typename ElementType>
void PrintTypedArrayElements(std::ostream& os, const ElementType* data_ptr,
size_t length, bool is_on_heap) {
if (length == 0) return;
size_t previous_index = 0;
if (i::v8_flags.mock_arraybuffer_allocator && !is_on_heap) {
// Don't try to print data that's not actually allocated.
os << "\n 0-" << length << ": <mocked array buffer bytes>";
return;
}
ElementType previous_value = data_ptr[0];
ElementType value{0};
for (size_t i = 1; i <= length; i++) {
if (i < length) value = data_ptr[i];
if (i != length && previous_value == value) {
continue;
}
os << "\n";
std::stringstream ss;
ss << previous_index;
if (previous_index != i - 1) {
ss << '-' << (i - 1);
}
if constexpr (std::is_floating_point_v<ElementType>) {
if (std::isnan(previous_value)) {
os << std::setw(12) << ss.str() << ": " << +previous_value << " (0x"
<< std::hex;
if constexpr (std::is_same_v<ElementType, float>) {
os << base::bit_cast<uint32_t>(previous_value);
} else {
os << base::bit_cast<uint64_t>(previous_value);
}
os << std::dec << ")";
previous_index = i;
previous_value = value;
continue;
}
}
os << std::setw(12) << ss.str() << ": " << +previous_value;
previous_index = i;
previous_value = value;
}
}
template <typename T>
void PrintFixedArrayElements(std::ostream& os, Tagged<T> array,
uint32_t capacity,
Tagged<Object> (*get)(Tagged<T>, uint32_t)) {
// Print in array notation for non-sparse arrays.
if (capacity == 0) return;
Tagged<Object> previous_value = get(array, 0);
Tagged<Object> value;
uint32_t previous_index = 0;
uint32_t i;
for (i = 1; i <= capacity; i++) {
if (i < capacity) value = get(array, i);
if (previous_value == value && i != capacity) {
continue;
}
os << "\n";
std::stringstream ss;
ss << previous_index;
if (previous_index != i - 1) {
ss << '-' << (i - 1);
}
os << std::setw(12) << ss.str() << ": " << Brief(previous_value);
previous_index = i;
previous_value = value;
}
}
template <typename T>
void PrintFixedArrayElements(std::ostream& os, Tagged<T> array,
uint32_t array_len) {
DCHECK_EQ(array->length().value(), array_len);
PrintFixedArrayElements<T>(
os, array, array_len,
[](Tagged<T> xs, uint32_t i) { return Cast<Object>(xs->get(i)); });
}
void PrintNumberDictionaryFlags(std::ostream& os,
Tagged<NumberDictionary> dict) {
if (dict->requires_slow_elements()) {
os << "\n - requires_slow_elements";
} else {
os << "\n - max_number_key: " << dict->max_number_key();
}
}
void PrintSloppyArgumentElements(std::ostream& os, ElementsKind kind,
Tagged<SloppyArgumentsElements> elements) {
Tagged<FixedArray> arguments_store = elements->arguments();
os << "\n 0: context: " << Brief(elements->context())
<< "\n 1: arguments_store: " << Brief(arguments_store)
<< "\n parameter to context slot map:";
uint32_t elements_len = elements->ulength().value();
for (uint32_t i = 0; i < elements_len; i++) {
Tagged<Object> mapped_entry = elements->mapped_entries(i, kRelaxedLoad);
os << "\n " << i << ": param(" << i << "): " << Brief(mapped_entry);
if (IsTheHole(mapped_entry)) {
os << " in the arguments_store[" << i << "]";
} else {
os << " in the context";
}
}
uint32_t arg_elements_len = arguments_store->ulength().value();
if (arg_elements_len == 0) return;
os << "\n }"
<< "\n - arguments_store: " << Brief(arguments_store) << " "
<< ElementsKindToString(arguments_store->map()->elements_kind()) << " {";
if (kind == FAST_SLOPPY_ARGUMENTS_ELEMENTS) {
PrintFixedArrayElements(os, arguments_store, arg_elements_len);
} else {
DCHECK_EQ(kind, SLOW_SLOPPY_ARGUMENTS_ELEMENTS);
PrintDictionaryContents(os, Cast<NumberDictionary>(arguments_store));
}
}
void PrintEmbedderData(IsolateForPointerCompression isolate, std::ostream& os,
EmbedderDataSlot slot) {
DisallowGarbageCollection no_gc;
Tagged<Object> value = slot.load_tagged();
os << Brief(value);
void* raw_pointer;
if (slot.ToGenericAlignedPointer(isolate, &raw_pointer)) {
os << ", aligned pointer: " << raw_pointer;
}
}
} // namespace
void JSObject::PrintElements(std::ostream& os) {
// Don't call GetElementsKind, its validation code can cause the printer to
// fail when debugging.
os << " - elements: " << Brief(elements()) << " {";
// We sometimes print out objects in an inconsistent state e.g., when first
// changing the map and then updating the elements. The code below tries to
// look at what elements() is, as opposed to what it should be according to
// the map(), as much as possible.
if (IsFixedArrayExact(elements())) {
auto array = Cast<FixedArray>(elements());
PrintFixedArrayElements(os, array, array->ulength().value());
} else if (IsFixedDoubleArray(elements())) {
DoPrintElements<FixedDoubleArray>(os, elements(),
elements()->ulength().value());
} else if (IsTypedArrayOrRabGsabTypedArrayElementsKind(
map()->elements_kind())) {
// elements() is a ByteArray, and we need to look at the map to figure out
// the ElementsKind. These kind of objects shouldn't be involved in
// transitions where we might print inconsistent objects.
switch (map()->elements_kind()) {
#define PRINT_ELEMENTS(Type, type, TYPE, elementType) \
case TYPE##_ELEMENTS: { \
size_t length = Cast<JSTypedArray>(this)->GetLength(); \
bool is_on_heap = Cast<JSTypedArray>(this)->is_on_heap(); \
const elementType* data_ptr = \
static_cast<const elementType*>(Cast<JSTypedArray>(this)->DataPtr()); \
PrintTypedArrayElements<elementType>(os, data_ptr, length, is_on_heap); \
break; \
}
TYPED_ARRAYS(PRINT_ELEMENTS)
RAB_GSAB_TYPED_ARRAYS(PRINT_ELEMENTS)
#undef PRINT_ELEMENTS
default:
UNREACHABLE();
}
} else if (IsNumberDictionary(elements())) {
PrintDictionaryContents(os, Cast<NumberDictionary>(elements()));
} else if (IsSloppyArgumentsElements(elements())) {
PrintSloppyArgumentElements(os, map()->elements_kind(),
Cast<SloppyArgumentsElements>(elements()));
} else {
os << " Unexpected elements backing store\n";
}
os << "\n }\n";
}
namespace {
void JSObjectPrintHeader(std::ostream& os, Tagged<JSObject> obj,
const char* id) {
Isolate* isolate = Isolate::Current();
obj->PrintHeader(os, id);
// Don't call GetElementsKind, its validation code can cause the printer to
// fail when debugging.
os << " [";
if (obj->HasFastProperties()) {
os << "FastProperties";
} else {
os << "DictionaryProperties";
}
PrototypeIterator iter(isolate, obj);
os << "]\n - prototype: " << Brief(iter.GetCurrent());
os << "\n - elements: " << Brief(obj->elements()) << " ["
<< ElementsKindToString(obj->map()->elements_kind());
if (obj->elements()->IsCowArray()) os << " (COW)";
os << "]";
Tagged<Object> hash = Object::GetHash(obj);
if (IsSmi(hash)) {
os << "\n - hash: " << Brief(hash);
}
if (obj->GetEmbedderFieldCount() > 0) {
os << "\n - embedder fields: " << obj->GetEmbedderFieldCount();
}
}
void JSAPIObjectWithEmbedderSlotsPrintHeader(std::ostream& os,
Tagged<JSObject> obj,
const char* id = nullptr) {
JSObjectPrintHeader(os, obj, id);
os << "\n - cpp_heap_wrappable: "
<< obj->ReadField<uint32_t>(
offsetof(JSAPIObjectWithEmbedderSlots, cpp_heap_wrappable_));
}
void JSObjectPrintBody(std::ostream& os, Tagged<JSObject> obj,
bool print_elements = true) {
os << "\n - properties: ";
Tagged<JSReceiver::PropertiesOrHash> properties_or_hash =
obj->raw_properties_or_hash(kRelaxedLoad);
if (!IsSmi(properties_or_hash)) {
os << Brief(properties_or_hash);
}
os << "\n - All own properties (excluding elements): {";
if (obj->PrintProperties(os)) os << "\n ";
os << "}\n";
if (print_elements) {
size_t length = IsJSTypedArray(obj) ? Cast<JSTypedArray>(obj)->GetLength()
: obj->elements()->ulength().value();
if (length > 0) obj->PrintElements(os);
}
int embedder_fields = obj->GetEmbedderFieldCount();
if (embedder_fields > 0) {
os << " - embedder fields = {";
for (int i = 0; i < embedder_fields; i++) {
os << "\n ";
PrintEmbedderData(Isolate::Current(), os, EmbedderDataSlot(obj, i));
}
os << "\n }\n";
}
}
// Pointer-to-JSObject overloads. Let layout-based subclass printers
// pass their `this` directly. Can't use `Tagged<JSObject>` here:
// normalize_type maps that back to Tagged<JSObject>, which makes the
// overload match legacy callers too and creates ambiguity. A pointer
// parameter is unambiguous because legacy JSObject doesn't inherit from
// JSObject.
void JSObjectPrintHeader(std::ostream& os, const JSObject* obj,
const char* id) {
JSObjectPrintHeader(os, Cast<JSObject>(obj), id);
}
void JSObjectPrintBody(std::ostream& os, const JSObject* obj,
bool print_elements = true) {
JSObjectPrintBody(os, Cast<JSObject>(obj), print_elements);
}
} // namespace
void JSObject::JSObjectPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, nullptr);
JSObjectPrintBody(os, this);
}
void JSExternalObject::JSExternalObjectPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, nullptr);
os << "\n - external value: " << value(kExternalObjectValueTagRange);
JSObjectPrintBody(os, this);
}
void CppHeapExternalObject::CppHeapExternalObjectPrint(std::ostream& os) {
if (i::Isolate* isolate = i::Isolate::TryGetCurrent()) {
os << "\n - cpp_heap_wrappable: "
<< CppHeapObjectWrapper(this).GetCppHeapWrappable(isolate,
kAnyCppHeapPointer);
} else {
os << "\n - cpp_heap_wrappable: <object>";
}
}
void JSGeneratorObject::JSGeneratorObjectPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSGeneratorObject");
os << "\n - function: " << Brief(function());
os << "\n - context: " << Brief(context());
os << "\n - receiver: " << Brief(receiver());
if (is_executing() || is_closed()) {
os << "\n - input: " << Brief(input_or_debug_pos());
} else {
DCHECK(is_suspended());
os << "\n - debug pos: " << Brief(input_or_debug_pos());
}
const char* mode = "(invalid)";
switch (resume_mode()) {
case kNext:
mode = ".next()";
break;
case kReturn:
mode = ".return()";
break;
case kThrow:
mode = ".throw()";
break;
}
os << "\n - resume mode: " << mode;
os << "\n - continuation: " << continuation();
if (is_closed()) os << " (closed)";
if (is_executing()) os << " (executing)";
if (is_suspended()) os << " (suspended)";
if (is_suspended()) {
DisallowGarbageCollection no_gc;
Tagged<SharedFunctionInfo> fun_info = function()->shared();
if (fun_info->HasSourceCode()) {
Tagged<Script> script = Cast<Script>(fun_info->script());
Tagged<String> script_name = IsString(script->name())
? Cast<String>(script->name())
: GetReadOnlyRoots().empty_string();
os << "\n - source position: ";
// Can't collect source positions here if not available as that would
// allocate memory.
Isolate* isolate = Isolate::Current();
if (fun_info->HasBytecodeArray() &&
fun_info->GetBytecodeArray(isolate)->HasSourcePositionTable()) {
os << source_position();
os << " (";
script_name->PrintUC16(os);
Script::PositionInfo info;
script->GetPositionInfo(source_position(), &info);
os << ", line " << info.line + 1;
os << ", column " << info.column + 1;
} else {
os << kUnavailableString;
}
os << ")";
}
}
os << "\n - register file: " << Brief(parameters_and_registers());
JSObjectPrintBody(os, this);
}
void JSArray::JSArrayPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSArray");
os << "\n - length: " << Brief(this->length());
JSObjectPrintBody(os, this);
}
void JSProxy::JSProxyPrint(std::ostream& os) {
PrintHeader(os, "JSProxy");
os << "\n - target: " << Brief(target());
os << "\n - handler: " << Brief(handler());
os << '\n';
}
void JSObjectWithEmbedderSlots::JSObjectWithEmbedderSlotsPrint(
std::ostream& os) {
JSObjectPrintHeader(os, this, "JSObjectWithEmbedderSlots");
JSObjectPrintBody(os, this);
}
void JSPromise::JSPromisePrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSPromise");
os << "\n - status: " << JSPromise::Status(status());
if (status() == Promise::kPending) {
os << "\n - reactions: " << Brief(reactions());
} else {
os << "\n - result: " << Brief(result());
}
os << "\n - has_handler: " << has_handler();
os << "\n - is_silent: " << is_silent();
JSObjectPrintBody(os, this);
}
void JSRegExp::JSRegExpPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSRegExp");
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
os << "\n - data: " << Brief(data(isolate));
FlagsBuffer buffer;
os << "\n - flags: " << JSRegExp::FlagsToString(flags(), &buffer);
JSObjectPrintBody(os, this);
}
void RegExpData::RegExpDataPrint(std::ostream& os) {
switch (type_tag()) {
case RegExpData::Type::ATOM:
PrintHeader(os, "AtomRegExpData");
break;
case RegExpData::Type::IRREGEXP:
PrintHeader(os, "IrRegExpData");
break;
case RegExpData::Type::EXPERIMENTAL:
PrintHeader(os, "IrRegExpData");
break;
default:
UNREACHABLE();
}
os << "\n - original_source: " << original_source();
os << "\n - escaped_source: " << escaped_source();
JSRegExp::FlagsBuffer buffer;
os << "\n - flags: " << JSRegExp::FlagsToString(flags(), &buffer);
}
void AtomRegExpData::AtomRegExpDataPrint(std::ostream& os) {
RegExpDataPrint(os);
os << "\n - pattern: " << pattern();
os << "\n";
}
void IrRegExpData::IrRegExpDataPrint(std::ostream& os) {
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
RegExpDataPrint(os);
if (has_latin1_bytecode()) {
os << "\n - latin1_bytecode: " << Brief(latin1_bytecode());
}
if (has_uc16_bytecode()) {
os << "\n - uc16_bytecode: " << Brief(uc16_bytecode());
}
if (has_latin1_code()) {
os << "\n - latin1_code: " << Brief(latin1_code(isolate));
}
if (has_uc16_code()) {
os << "\n - uc16_code: " << Brief(uc16_code(isolate));
}
os << "\n - capture_name_map: " << Brief(capture_name_map());
os << "\n - max_register_count: " << max_register_count();
os << "\n - capture_count: " << capture_count();
os << "\n - ticks_until_tier_up: " << ticks_until_tier_up();
os << "\n - backtrack_limit: " << backtrack_limit();
if (can_be_zero_length()) os << "\n - can_be_zero_length";
if (is_linear_executable()) os << "\n - linear_executable";
os << "\n";
}
void RegExpDataWrapper::RegExpDataWrapperPrint(std::ostream& os) {
PrintHeader(os, "RegExpDataWrapper");
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
os << "\n data: " << Brief(data(isolate));
os << "\n";
}
void JSRegExpStringIterator::JSRegExpStringIteratorPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSRegExpStringIterator");
os << "\n - regex: " << Brief(iterating_reg_exp());
os << "\n - string: " << Brief(iterated_string());
os << "\n - done: " << done();
os << "\n - global: " << global();
os << "\n - unicode: " << unicode();
JSObjectPrintBody(os, this);
}
void Symbol::SymbolPrint(std::ostream& os) {
PrintHeader(os, "Symbol");
os << "\n - hash: " << hash();
os << "\n - description: " << Brief(description());
if (IsUndefined(description())) {
os << " (" << PrivateSymbolToName() << ")";
}
os << "\n - private_symbol_kind: " << private_symbol_kind();
os << "\n - is_interesting_symbol: " << is_interesting_symbol();
os << "\n - is_well_known_symbol: " << is_well_known_symbol();
os << "\n";
}
void EnumCache::EnumCachePrint(std::ostream& os) {
PrintHeader(os, "EnumCache");
os << "\n - keys: " << Brief(keys());
os << "\n - indices: " << Brief(indices());
os << "\n";
}
void DescriptorArray::DescriptorArrayPrint(std::ostream& os) {
PrintHeader(os, "DescriptorArray");
os << "\n - enum_cache: ";
uint32_t keys_len = enum_cache()->keys()->ulength().value();
if (keys_len == 0) {
os << "empty";
} else {
os << keys_len;
os << "\n - keys: " << Brief(enum_cache()->keys());
os << "\n - indices: " << Brief(enum_cache()->indices());
}
os << "\n - nof slack descriptors: " << number_of_slack_descriptors();
os << "\n - nof descriptors: " << number_of_descriptors();
os << "\n - fast iterable state: ";
switch (fast_iterable()) {
case FastIterableState::kJsonFast:
os << "JSON fast";
break;
case FastIterableState::kJsonSlow:
os << "JSON slow";
break;
case FastIterableState::kUnknown:
os << "unknown";
break;
}
PrintDescriptors(os);
}
namespace {
template <typename T>
void PrintFixedArrayWithHeader(std::ostream& os, T* array, const char* type) {
array->PrintHeader(os, type);
uint32_t length = array->ulength().value();
os << "\n - length: " << length;
PrintFixedArrayElements(os, Tagged(array), length);
os << "\n";
}
template <typename T>
void PrintWeakArrayElements(std::ostream& os, T* array, uint32_t array_len) {
// Print in array notation for non-sparse arrays.
DCHECK_EQ(array->ulength().value(), array_len);
Tagged<MaybeObject> previous_value =
array_len > 0 ? array->get(0) : Tagged<MaybeObject>(kNullAddress);
Tagged<MaybeObject> value;
uint32_t previous_index = 0;
uint32_t i;
for (i = 1; i <= array_len; i++) {
if (i < array_len) value = array->get(i);
if (previous_value == value && i != array_len) {
continue;
}
os << "\n";
std::stringstream ss;
ss << previous_index;
if (previous_index != i - 1) {
ss << '-' << (i - 1);
}
os << std::setw(12) << ss.str() << ": " << Brief(previous_value);
previous_index = i;
previous_value = value;
}
}
} // namespace
void PropertyDescriptorObject::PropertyDescriptorObjectPrint(std::ostream& os) {
PrintHeader(os, "PropertyDescriptorObject");
os << "\n - flags: " << flags();
os << "\n - value: " << Brief(value());
os << "\n - get: " << Brief(get());
os << "\n - set: " << Brief(set());
os << "\n";
}
void TemplateObjectDescription::TemplateObjectDescriptionPrint(
std::ostream& os) {
PrintHeader(os, "TemplateObjectDescription");
os << "\n - raw_strings: " << Brief(raw_strings());
os << "\n - cooked_strings: " << Brief(cooked_strings());
os << "\n";
}
void ObjectBoilerplateDescription::ObjectBoilerplateDescriptionPrint(
std::ostream& os) {
PrintHeader(os, "ObjectBoilerplateDescription");
const uint32_t cap = capacity().value();
os << "\n - capacity: " << cap;
os << "\n - backing_store_size: " << backing_store_size();
os << "\n - flags: " << flags();
os << "\n - elements:";
PrintFixedArrayElements<ObjectBoilerplateDescription>(
os, this, cap, [](Tagged<ObjectBoilerplateDescription> xs, uint32_t i) {
return xs->get(i);
});
os << "\n";
}
void ClassBoilerplate::ClassBoilerplatePrint(std::ostream& os) {
PrintHeader(os, "ClassBoilerplate");
os << "\n - arguments_count: " << arguments_count();
os << "\n - static_properties_template: " << static_properties_template();
os << "\n - static_elements_template: " << static_elements_template();
os << "\n - static_computed_properties: " << static_computed_properties();
os << "\n - instance_properties_template: " << instance_properties_template();
os << "\n - instance_elements_template: " << instance_elements_template();
os << "\n - instance_computed_properties: " << instance_computed_properties();
os << "\n";
}
void RegExpBoilerplateDescription::RegExpBoilerplateDescriptionPrint(
std::ostream& os) {
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
PrintHeader(os, "RegExpBoilerplate");
os << "\n - data: " << Brief(data(isolate));
os << "\n - flags: " << flags();
os << "\n";
}
void EmbedderDataArray::EmbedderDataArrayPrint(std::ostream& os) {
PrintHeader(os, "EmbedderDataArray");
os << "\n - length: " << length();
EmbedderDataSlot start(this, 0);
EmbedderDataSlot end(this, length());
for (EmbedderDataSlot slot = start; slot < end; ++slot) {
os << "\n ";
PrintEmbedderData(Isolate::Current(), os, slot);
}
os << "\n";
}
void FixedArray::FixedArrayPrint(std::ostream& os) {
PrintFixedArrayWithHeader(os, this, "FixedArray");
}
void TrustedFixedArray::TrustedFixedArrayPrint(std::ostream& os) {
PrintFixedArrayWithHeader(os, this, "TrustedFixedArray");
}
void ProtectedFixedArray::ProtectedFixedArrayPrint(std::ostream& os) {
PrintFixedArrayWithHeader(os, this, "ProtectedFixedArray");
}
void ArrayList::ArrayListPrint(std::ostream& os) {
PrintHeader(os, "ArrayList");
const uint32_t len = ulength().value();
const uint32_t cap = capacity().value();
os << "\n - capacity: " << cap;
os << "\n - length: " << len;
os << "\n - elements:";
PrintFixedArrayElements<ArrayList>(
os, this, len,
[](Tagged<ArrayList> xs, uint32_t i) { return xs->get(i); });
os << "\n";
}
void ScriptContextTable::ScriptContextTablePrint(std::ostream& os) {
PrintHeader(os, "ScriptContextTable");
const uint32_t len = length(kAcquireLoad).value();
const uint32_t cap = capacity().value();
os << "\n - capacity: " << cap;
os << "\n - length: " << len;
os << "\n - names_to_context_index: " << names_to_context_index();
os << "\n - elements:";
PrintFixedArrayElements<ScriptContextTable>(
os, this, len, [](Tagged<ScriptContextTable> xs, uint32_t i) {
return Cast<Object>(xs->get(i));
});
os << "\n";
}
void RegExpMatchInfo::RegExpMatchInfoPrint(std::ostream& os) {
PrintHeader(os, "RegExpMatchInfo");
const uint32_t cap = capacity().value();
os << "\n - capacity: " << cap;
os << "\n - number_of_capture_registers: " << number_of_capture_registers();
os << "\n - last_subject: " << last_subject();
os << "\n - last_input: " << last_input();
os << "\n - captures:";
PrintFixedArrayElements<RegExpMatchInfo>(
os, this, cap, [](Tagged<RegExpMatchInfo> xs, uint32_t i) {
return Cast<Object>(xs->get(i));
});
os << "\n";
}
void SloppyArgumentsElements::SloppyArgumentsElementsPrint(std::ostream& os) {
PrintHeader(os, "SloppyArgumentsElements");
uint32_t len = ulength().value();
os << "\n - length: " << len;
os << "\n - context: " << Brief(context());
os << "\n - arguments: " << Brief(arguments());
os << "\n - mapped_entries:";
PrintFixedArrayElements<SloppyArgumentsElements>(
os, this, len, [](Tagged<SloppyArgumentsElements> xs, uint32_t i) {
return Cast<Object>(xs->mapped_entries(i, kRelaxedLoad));
});
os << '\n';
}
namespace {
const char* SideEffectType2String(SideEffectType type) {
switch (type) {
case SideEffectType::kHasSideEffect:
return "kHasSideEffect";
case SideEffectType::kHasNoSideEffect:
return "kHasNoSideEffect";
case SideEffectType::kHasSideEffectToReceiver:
return "kHasSideEffectToReceiver";
}
UNREACHABLE();
}
} // namespace
#define AS_PTR(x) reinterpret_cast<void*>(x)
void AccessorInfo::AccessorInfoPrint(std::ostream& os) {
PrintHeader(os, "AccessorInfo");
os << "\n - data: " << Brief(data());
os << "\n - name: " << Brief(name());
os << "\n - flags: " << flags();
os << "\n - is_sloppy: " << is_sloppy();
os << "\n - replace_on_access: " << replace_on_access();
os << "\n - getter_side_effect_type: "
<< SideEffectType2String(getter_side_effect_type());
os << "\n - setter_side_effect_type: "
<< SideEffectType2String(setter_side_effect_type());
os << "\n - initial_attributes: " << initial_property_attributes();
Isolate* isolate;
if (GetIsolateFromHeapObject(this, &isolate)) {
os << "\n - getter: " << AS_PTR(getter(isolate));
if (USE_SIMULATOR_BOOL) {
os << "\n - getter (redirected): " << AS_PTR(getter_raw(isolate));
}
os << "\n - setter: " << AS_PTR(setter(isolate));
} else {
os << "\n - getter: " << kUnavailableString;
if (USE_SIMULATOR_BOOL) {
os << "\n - getter (redirected): " << kUnavailableString;
}
os << "\n - setter: " << kUnavailableString;
}
os << '\n';
}
void AccessCheckInfo::AccessCheckInfoPrint(std::ostream& os) {
PrintHeader(os, "AccessCheckInfo");
os << "\n - callback: " << Brief(callback());
os << "\n - named_interceptor: " << Brief(named_interceptor());
os << "\n - indexed_interceptor: " << Brief(indexed_interceptor());
os << "\n - data: " << Brief(data());
os << "\n";
}
void InterceptorInfo::InterceptorInfoPrint(std::ostream& os) {
PrintHeader(os, "InterceptorInfo");
os << "\n - data: " << Brief(data());
os << "\n - flags: " << flags();
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
if (is_named()) {
os << " - getter: " << AS_PTR(named_getter(isolate));
if (USE_SIMULATOR_BOOL) {
os << "\n - getter (redirected): " << AS_PTR(named_getter_raw(isolate));
}
os << "\n - setter: " << AS_PTR(named_setter(isolate));
os << "\n - query: " << AS_PTR(named_query(isolate));
os << "\n - descriptor: " << AS_PTR(named_descriptor(isolate));
os << "\n - deleter: " << AS_PTR(named_deleter(isolate));
os << "\n - enumerator: " << AS_PTR(named_enumerator(isolate));
os << "\n - definer: " << AS_PTR(named_definer(isolate));
} else {
os << " - getter: " << AS_PTR(indexed_getter(isolate));
os << "\n - setter: " << AS_PTR(indexed_setter(isolate));
os << "\n - query: " << AS_PTR(indexed_query(isolate));
os << "\n - descriptor: " << AS_PTR(indexed_descriptor(isolate));
os << "\n - deleter: " << AS_PTR(indexed_deleter(isolate));
os << "\n - enumerator: " << AS_PTR(indexed_enumerator(isolate));
os << "\n - definer: " << AS_PTR(indexed_definer(isolate));
os << "\n - index_of: " << AS_PTR(indexed_index_of(isolate));
os << "\n - iterable_to_list: "
<< AS_PTR(indexed_iterable_to_list(isolate));
}
os << "\n --- flags: ";
if (can_intercept_symbols()) os << "\n - can_intercept_symbols";
if (non_masking()) os << "\n - non_masking";
if (is_named()) os << "\n - is_named";
if (has_no_side_effect()) os << "\n - has_no_side_effect";
os << '\n';
}
void FunctionTemplateInfo::FunctionTemplateInfoPrint(std::ostream& os) {
PrintHeader(os, "FunctionTemplateInfo");
os << "\n - is_cacheable: " << is_cacheable();
os << "\n - should_promote_to_read_only: " << should_promote_to_read_only();
os << "\n - serial_number: " << serial_number();
os << "\n - number_of_properties: " << number_of_properties();
os << "\n - property_list: " << Brief(property_list());
os << "\n - property_accessors: " << Brief(property_accessors());
os << "\n - class_name: " << Brief(class_name());
os << "\n - interface_name: " << Brief(interface_name());
os << "\n - signature: " << Brief(signature());
os << "\n - rare_data: " << Brief(rare_data(kAcquireLoad));
os << "\n - shared_function_info: " << Brief(shared_function_info());
os << "\n - cached_property_name: " << Brief(cached_property_name());
os << "\n - callback_data: " << Brief(callback_data(kAcquireLoad));
os << "\n - flag: " << flag();
os << "\n - length: " << length();
os << "\n - instance_type: " << instance_type();
os << "\n - exception_context: " << exception_context();
Isolate* isolate;
if (GetIsolateFromHeapObject(this, &isolate)) {
os << " - callback: " << AS_PTR(callback(isolate));
if (USE_SIMULATOR_BOOL) {
os << "\n - callback (redirected): "
<< AS_PTR(this->callback_.load_raw(isolate));
}
} else {
os << "\n - callback: " << kUnavailableString;
if (USE_SIMULATOR_BOOL) {
os << "\n - callback (redirected): " << kUnavailableString;
}
}
os << "\n - serial_number: ";
if (serial_number() == kUninitializedSerialNumber) {
os << "n/a";
} else {
os << serial_number();
}
os << "\n --- flags: ";
if (is_cacheable()) os << "\n - is_cacheable";
if (should_promote_to_read_only()) os << "\n - should_promote_to_read_only";
if (is_object_template_call_handler()) {
os << "\n - is_object_template_call_handler";
}
if (has_side_effects()) os << "\n - has_side_effects";
if (undetectable()) os << "\n - undetectable";
if (needs_access_check()) os << "\n - needs_access_check";
if (read_only_prototype()) os << "\n - read_only_prototype";
if (remove_prototype()) os << "\n - remove_prototype";
if (accept_any_receiver()) os << "\n - accept_any_receiver";
if (published()) os << "\n - published";
if (allowed_receiver_instance_type_range_start() ||
allowed_receiver_instance_type_range_end()) {
os << "\n - allowed_receiver_instance_type_range: ["
<< allowed_receiver_instance_type_range_start() << ", "
<< allowed_receiver_instance_type_range_end() << "]";
}
os << '\n';
}
void ObjectTemplateInfo::ObjectTemplateInfoPrint(std::ostream& os) {
PrintHeader(os, "ObjectTemplateInfo");
os << "\n - is_cacheable: " << is_cacheable();
os << "\n - should_promote_to_read_only: " << should_promote_to_read_only();
os << "\n - serial_number: " << serial_number();
os << "\n - number_of_properties: " << number_of_properties();
os << "\n - property_list: " << Brief(property_list());
os << "\n - property_accessors: " << Brief(property_accessors());
os << "\n - constructor: " << Brief(constructor());
os << "\n - data: " << data();
os << "\n";
}
void DictionaryTemplateInfo::DictionaryTemplateInfoPrint(std::ostream& os) {
PrintHeader(os, "DictionaryTemplateInfo");
os << "\n - is_cacheable: " << is_cacheable();
os << "\n - should_promote_to_read_only: " << should_promote_to_read_only();
os << "\n - serial_number: " << serial_number();
os << "\n - property_names: " << Brief(property_names());
os << "\n";
}
#undef AS_PTR
#if V8_ENABLE_WEBASSEMBLY
void WasmTableObject::WasmTableObjectPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "WasmTableObject");
os << "\n - entries: " << Brief(entries());
os << "\n - current_length: " << current_length();
os << "\n - maximum_length: " << Brief(maximum_length());
os << "\n - raw_type: " << raw_type();
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
if (has_trusted_dispatch_table()) {
os << "\n - trusted_dispatch_table: "
<< Brief(trusted_dispatch_table(isolate));
}
if (has_trusted_data()) {
os << "\n - trusted_data: " << Brief(trusted_data(isolate));
}
os << "\n - address_type: " << static_cast<int>(address_type());
JSObjectPrintBody(os, this);
}
void WasmMemoryObject::WasmMemoryObjectPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "WasmMemoryObject");
os << "\n - array_buffer: " << Brief(array_buffer());
os << "\n - managed_backing_store: " << Brief(managed_backing_store());
os << "\n - maximum_pages: " << maximum_pages();
os << "\n - instances: " << Brief(instances());
os << "\n - address_type: " << static_cast<int>(address_type());
JSObjectPrintBody(os, this);
}
void WasmTagObject::WasmTagObjectPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "WasmTagObject");
os << "\n - tag: " << Brief(tag());
os << "\n - canonical_type_index: " << canonical_type_index();
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
if (has_trusted_data()) {
os << "\n - trusted_data: " << Brief(trusted_data(isolate));
} else {
os << "\n - trusted_data: <empty>";
}
JSObjectPrintBody(os, this);
}
#endif // V8_ENABLE_WEBASSEMBLY
void Context::PrintContextWithHeader(std::ostream& os, const char* type) {
PrintHeader(os, type);
os << "\n - type: " << map()->instance_type();
os << "\n - scope_info: " << Brief(scope_info());
os << "\n - previous: " << Brief(unchecked_previous());
os << "\n - native_context: " << Brief(native_context());
if (scope_info()->HasContextExtensionSlot()) {
os << "\n - extension: " << extension();
}
os << "\n - length: " << length();
os << "\n - elements:";
PrintFixedArrayElements<Context>(
os, this, static_cast<uint32_t>(length()),
[](Tagged<Context> xs, uint32_t i) {
return Cast<Object>(xs->get(i, kRelaxedLoad));
});
os << "\n";
}
void Context::ContextPrint(std::ostream& os) {
PrintContextWithHeader(os, "Context");
}
void NativeContext::NativeContextPrint(std::ostream& os) {
PrintContextWithHeader(os, "NativeContext");
os << " - microtask_queue: " << microtask_queue() << "\n";
}
void ContextCell::ContextCellPrint(std::ostream& os) {
PrintHeader(os, "ContextCell");
os << "\n - state: " << state()
<< "\n - dependent_code: " << Brief(dependent_code());
switch (state()) {
case kConst:
os << "\n - tagged_value (const): " << Brief(tagged_value());
break;
case kSmi:
os << "\n - tagged_value (smi): " << Brief(tagged_value());
break;
case kInt32:
os << "\n - int32_value: " << int32_value();
break;
case kFloat64:
os << "\n - float64_value: " << float64_value();
break;
case kDetached:
os << "\n - detached";
break;
}
os << "\n";
}
namespace {
using DataPrinter = std::function<void(InternalIndex)>;
// Prints the data associated with each key (but no headers or other meta
// data) in a hash table. Works on different hash table types, like the
// subtypes of HashTable and OrderedHashTable. |print_data_at| is given an
// index into the table (where a valid key resides) and prints the data at
// that index, like just the value (in case of a hash map), or value and
// property details (in case of a property dictionary). No leading space
// required or trailing newline required. It can be null/non-callable
// std::function to indicate that there is no associated data to be printed
// (for example in case of a hash set).
template <typename T>
void PrintTableContentsGeneric(std::ostream& os, T* dict,
DataPrinter print_data_at) {
DisallowGarbageCollection no_gc;
ReadOnlyRoots roots = GetReadOnlyRoots();
for (InternalIndex i : dict->IterateEntries()) {
Tagged<Object> k;
if (!dict->ToKey(roots, i, &k)) continue;
os << "\n " << std::setw(12) << i.as_int() << ": ";
if (!IsAnyHole(k) && IsString(k)) {
Cast<String>(k)->PrintUC16(os);
} else {
os << Brief(k);
}
if (print_data_at) {
os << " -> ";
print_data_at(i);
}
}
}
void PrintNameDictionaryFlags(std::ostream& os, Tagged<NameDictionary> dict) {
if (dict->may_have_interesting_properties()) {
os << "\n - may_have_interesting_properties";
}
}
// Used for ordered and unordered dictionaries.
template <typename T>
void PrintDictionaryContentsFull(std::ostream& os, T* dict) {
os << "\n - elements: {";
auto print_value_and_property_details = [&](InternalIndex i) {
os << Brief(dict->ValueAt(i)) << " ";
dict->DetailsAt(i).PrintAsSlowTo(os, !T::kIsOrderedDictionaryType);
};
PrintTableContentsGeneric(os, dict, print_value_and_property_details);
os << "\n }\n";
}
// Used for ordered and unordered hash maps.
template <typename T>
void PrintHashMapContentsFull(std::ostream& os, T* dict) {
os << "\n - elements: {";
auto print_value = [&](InternalIndex i) { os << Brief(dict->ValueAt(i)); };
PrintTableContentsGeneric(os, dict, print_value);
os << "\n }\n";
}
// Used for ordered and unordered hash sets.
template <typename T>
void PrintHashSetContentsFull(std::ostream& os, T* dict) {
os << "\n - elements: {";
// Passing non-callable std::function as there are no values to print.
PrintTableContentsGeneric(os, dict, nullptr);
os << "\n }\n";
}
// Used for subtypes of OrderedHashTable.
template <typename T>
void PrintOrderedHashTableHeaderAndBuckets(std::ostream& os, T* table,
const char* type) {
DisallowGarbageCollection no_gc;
PrintHeapObjectHeaderWithoutMap(table, os, type);
os << "\n - FixedArray length: " << table->length();
os << "\n - elements: " << table->NumberOfElements();
os << "\n - deleted: " << table->NumberOfDeletedElements();
os << "\n - buckets: " << table->NumberOfBuckets();
os << "\n - capacity: " << table->Capacity();
os << "\n - buckets: {";
for (int bucket = 0; bucket < table->NumberOfBuckets(); bucket++) {
Tagged<Object> entry = table->get(T::HashTableStartIndex() + bucket);
DCHECK(IsSmi(entry));
os << "\n " << std::setw(12) << bucket << ": " << Brief(entry);
}
os << "\n }";
}
// Used for subtypes of HashTable.
template <typename T>
void PrintHashTableHeader(std::ostream& os, T* table, const char* type) {
PrintHeapObjectHeaderWithoutMap(table, os, type);
os << "\n - FixedArray length: " << table->length();
os << "\n - elements: " << table->NumberOfElements();
os << "\n - deleted: " << table->NumberOfDeletedElements();
os << "\n - capacity: " << table->Capacity();
}
} // namespace
void ObjectHashTable::ObjectHashTablePrint(std::ostream& os) {
PrintHashTableHeader(os, this, "ObjectHashTable");
PrintHashMapContentsFull(os, this);
}
void NameToIndexHashTable::NameToIndexHashTablePrint(std::ostream& os) {
PrintHashTableHeader(os, this, "NameToIndexHashTable");
PrintHashMapContentsFull(os, this);
}
void RegisteredSymbolTable::RegisteredSymbolTablePrint(std::ostream& os) {
PrintHashTableHeader(os, this, "RegisteredSymbolTable");
PrintHashMapContentsFull(os, this);
}
void NumberDictionary::NumberDictionaryPrint(std::ostream& os) {
PrintHashTableHeader(os, this, "NumberDictionary");
PrintNumberDictionaryFlags(os, this);
PrintDictionaryContentsFull(os, this);
}
void EphemeronHashTable::EphemeronHashTablePrint(std::ostream& os) {
PrintHashTableHeader(os, this, "EphemeronHashTable");
PrintHashMapContentsFull(os, this);
}
void NameDictionary::NameDictionaryPrint(std::ostream& os) {
PrintHashTableHeader(os, this, "NameDictionary");
PrintNameDictionaryFlags(os, this);
PrintDictionaryContentsFull(os, this);
}
void GlobalDictionary::GlobalDictionaryPrint(std::ostream& os) {
PrintHashTableHeader(os, this, "GlobalDictionary");
PrintDictionaryContentsFull(os, this);
}
void SmallOrderedHashSet::SmallOrderedHashSetPrint(std::ostream& os) {
PrintHeader(os, "SmallOrderedHashSet");
// TODO(turbofan): Print all fields.
}
void SmallOrderedHashMap::SmallOrderedHashMapPrint(std::ostream& os) {
PrintHeader(os, "SmallOrderedHashMap");
// TODO(turbofan): Print all fields.
}
void SmallOrderedNameDictionary::SmallOrderedNameDictionaryPrint(
std::ostream& os) {
PrintHeader(os, "SmallOrderedNameDictionary");
// TODO(turbofan): Print all fields.
}
void OrderedHashSet::OrderedHashSetPrint(std::ostream& os) {
PrintOrderedHashTableHeaderAndBuckets(os, this, "OrderedHashSet");
PrintHashSetContentsFull(os, this);
}
void OrderedHashMap::OrderedHashMapPrint(std::ostream& os) {
PrintOrderedHashTableHeaderAndBuckets(os, this, "OrderedHashMap");
PrintHashMapContentsFull(os, this);
}
void OrderedNameDictionary::OrderedNameDictionaryPrint(std::ostream& os) {
PrintOrderedHashTableHeaderAndBuckets(os, this, "OrderedNameDictionary");
PrintDictionaryContentsFull(os, this);
}
void print_hex_byte(std::ostream& os, int value) {
os << "0x" << std::setfill('0') << std::setw(2) << std::right << std::hex
<< (value & 0xff) << std::setfill(' ');
}
void SwissNameDictionary::SwissNameDictionaryPrint(std::ostream& os) {
this->PrintHeader(os, "SwissNameDictionary");
os << "\n - meta table ByteArray: "
<< reinterpret_cast<void*>(this->meta_table().ptr());
os << "\n - capacity: " << this->Capacity();
os << "\n - elements: " << this->NumberOfElements();
os << "\n - deleted: " << this->NumberOfDeletedElements();
std::ios_base::fmtflags sav_flags = os.flags();
os << "\n - ctrl table (omitting buckets where key is hole value): {";
for (int i = 0; i < this->Capacity() + kGroupWidth; i++) {
ctrl_t ctrl = CtrlTable()[i];
if (ctrl == Ctrl::kEmpty) continue;
os << "\n " << std::setw(12) << std::dec << i << ": ";
switch (ctrl) {
case Ctrl::kEmpty:
UNREACHABLE();
case Ctrl::kDeleted:
print_hex_byte(os, ctrl);
os << " (= kDeleted)";
break;
case Ctrl::kSentinel:
print_hex_byte(os, ctrl);
os << " (= kSentinel)";
break;
default:
print_hex_byte(os, ctrl);
os << " (= H2 of a key)";
break;
}
}
os << "\n }";
os << "\n - enumeration table: {";
for (int enum_index = 0; enum_index < this->UsedCapacity(); enum_index++) {
int entry = EntryForEnumerationIndex(enum_index);
os << "\n " << std::setw(12) << std::dec << enum_index << ": " << entry;
}
os << "\n }";
os << "\n - data table (omitting slots where key is the hole): {";
for (int bucket = 0; bucket < this->Capacity(); ++bucket) {
Tagged<Object> k;
if (!this->ToKey(GetReadOnlyRoots(), bucket, &k)) continue;
Tagged<Object> value = this->ValueAtRaw(bucket);
PropertyDetails details = this->DetailsAt(bucket);
os << "\n " << std::setw(12) << std::dec << bucket << ": ";
if (IsString(k)) {
Cast<String>(k)->PrintUC16(os);
} else {
os << Brief(k);
}
os << " -> " << Brief(value);
details.PrintAsSlowTo(os, false);
}
os << "\n }\n";
os.flags(sav_flags);
}
void PropertyArray::PropertyArrayPrint(std::ostream& os) {
PrintHeader(os, "PropertyArray");
uint32_t len = length().value();
os << "\n - length: " << len;
os << "\n - hash: " << Hash();
PrintFixedArrayElements<PropertyArray>(os, this, len);
os << "\n";
}
void FixedDoubleArray::FixedDoubleArrayPrint(std::ostream& os) {
PrintHeader(os, "FixedDoubleArray");
uint32_t len = ulength().value();
os << "\n - length: " << len;
DoPrintElements<FixedDoubleArray>(os, this, len);
os << "\n";
}
void WeakFixedArray::WeakFixedArrayPrint(std::ostream& os) {
PrintHeader(os, "WeakFixedArray");
uint32_t len = ulength().value();
os << "\n - length: " << len;
PrintWeakArrayElements(os, this, len);
os << "\n";
}
void WeakHomomorphicFixedArray::WeakHomomorphicFixedArrayPrint(
std::ostream& os) {
PrintHeader(os, "WeakHomomorphicFixedArray");
uint32_t len = ulength().value();
os << "\n - length: " << len;
PrintWeakArrayElements(os, this, len);
os << "\n";
}
void TrustedWeakFixedArray::TrustedWeakFixedArrayPrint(std::ostream& os) {
PrintHeader(os, "TrustedWeakFixedArray");
uint32_t len = ulength().value();
os << "\n - length: " << len;
PrintWeakArrayElements(os, this, len);
os << "\n";
}
void ProtectedWeakFixedArray::ProtectedWeakFixedArrayPrint(std::ostream& os) {
PrintHeader(os, "ProtectedWeakFixedArray");
uint32_t len = ulength().value();
os << "\n - length: " << len;
PrintWeakArrayElements(os, this, len);
os << "\n";
}
void WeakArrayList::WeakArrayListPrint(std::ostream& os) {
PrintHeader(os, "WeakArrayList");
uint32_t len = ulength().value();
os << "\n - capacity: " << capacity();
os << "\n - length: " << len;
PrintWeakArrayElements(os, this, len);
os << "\n";
}
void TransitionArray::TransitionArrayPrint(std::ostream& os) {
PrintHeader(os, "TransitionArray");
PrintInternal(os);
os << "\n";
}
void FeedbackCell::FeedbackCellPrint(std::ostream& os) {
PrintHeader(os, "FeedbackCell");
ReadOnlyRoots roots = GetReadOnlyRoots();
if (map() == roots.no_closures_cell_map()) {
os << "\n - no closures";
} else if (map() == roots.one_closure_cell_map()) {
os << "\n - one closure";
} else if (map() == roots.many_closures_cell_map()) {
os << "\n - many closures";
} else {
os << "\n - Invalid FeedbackCell map";
}
os << "\n - value: " << Brief(value());
os << "\n - interrupt_budget: " << interrupt_budget();
os << "\n - dispatch_handle: 0x" << std::hex << dispatch_handle() << std::dec;
JSDispatchTable& jdt = Isolate::Current()->js_dispatch_table();
if (dispatch_handle() != kNullJSDispatchHandle &&
jdt.IsTieringRequested(dispatch_handle())) {
os << "\n - tiering request ";
if (Tagged<FeedbackVector> fbv;
TryCast(value(), &fbv) && fbv->tiering_in_progress()) {
os << "in_progress ";
}
jdt.PrintCurrentTieringRequest(dispatch_handle(), Isolate::Current(), os);
}
os << "\n";
}
void DoubleStringCache::DoubleStringCachePrint(std::ostream& os) {
PrintHeader(os, "DoubleStringCache");
os << "\n - capacity: " << capacity();
for (InternalIndex entry_index : InternalIndex::Range(capacity())) {
auto& entry = entries()[entry_index.as_uint32()];
if (entry.value_.load() == kEmptySentinel) continue;
os << "\n [" << entry_index.as_uint32() << "]: ";
PrintDouble(os, entry.key_.value());
os << " - " << Brief(entry.value_.load());
}
os << "\n";
}
void FeedbackVectorSpec::Print() {
StdoutStream os;
FeedbackVectorSpecPrint(os);
os << std::flush;
}
void FeedbackVectorSpec::FeedbackVectorSpecPrint(std::ostream& os) {
os << " - slot_count: " << slot_count();
if (slot_count() == 0) {
os << " (empty)\n";
return;
}
for (int slot = 0; slot < slot_count();) {
FeedbackSlotKind kind = GetKind(FeedbackSlot(slot));
int entry_size = FeedbackMetadata::GetSlotSize(kind);
DCHECK_LT(0, entry_size);
os << "\n Slot #" << slot << " " << kind;
slot += entry_size;
}
os << "\n";
}
void FeedbackMetadata::FeedbackMetadataPrint(std::ostream& os) {
PrintHeader(os, "FeedbackMetadata");
os << "\n - slot_count: " << slot_count();
os << "\n - create_closure_slot_count: " << create_closure_slot_count();
DisallowGarbageCollection no_gc;
FeedbackMetadataIterator iter(Tagged<FeedbackMetadata>(this), no_gc);
while (iter.HasNext()) {
FeedbackSlot slot = iter.Next();
FeedbackSlotKind kind = iter.kind();
os << "\n Slot " << slot << " " << kind;
}
os << "\n";
}
void ClosureFeedbackCellArray::ClosureFeedbackCellArrayPrint(std::ostream& os) {
PrintHeader(os, "ClosureFeedbackCellArray");
uint32_t len = ulength().value();
os << "\n - length: " << len;
os << "\n - elements:";
PrintFixedArrayElements<ClosureFeedbackCellArray>(os, this, len);
os << "\n";
}
void FeedbackVector::FeedbackVectorPrint(std::ostream& os) {
PrintHeader(os, "FeedbackVector");
os << "\n - length: " << length();
if (length().value() == 0) {
os << " (empty)\n";
return;
}
os << "\n - shared function info: " << Brief(shared_function_info());
os << "\n - tiering_in_progress: " << tiering_in_progress();
os << "\n - osr_tiering_in_progress: " << osr_tiering_in_progress();
os << "\n - invocation count: " << invocation_count();
os << "\n - closure feedback cell array: ";
closure_feedback_cell_array()->ClosureFeedbackCellArrayPrint(os);
if (has_metadata()) {
DisallowGarbageCollection no_gc;
FeedbackMetadataIterator iter(metadata(), no_gc);
while (iter.HasNext()) {
FeedbackSlot slot = iter.Next();
FeedbackSlotKind kind = iter.kind();
os << "\n - slot " << slot << " " << kind << " ";
FeedbackSlotPrint(os, slot);
int entry_size = iter.entry_size();
if (entry_size > 0) os << " {";
for (int i = 0; i < entry_size; i++) {
FeedbackSlot slot_with_offset = slot.WithOffset(i);
os << "\n [" << slot_with_offset.ToInt()
<< "]: " << Brief(Get(slot_with_offset));
}
if (entry_size > 0) os << "\n }";
}
} else {
os << " - metadata: not available in SFI";
}
os << "\n";
}
void FeedbackVector::FeedbackSlotPrint(std::ostream& os, FeedbackSlot slot) {
FeedbackNexus nexus(Isolate::Current(), this, slot);
nexus.Print(os);
}
void FeedbackNexus::Print(std::ostream& os) {
auto slot_kind = kind();
switch (slot_kind) {
case FeedbackSlotKind::kCall:
case FeedbackSlotKind::kCloneObject:
case FeedbackSlotKind::kHasKeyed:
case FeedbackSlotKind::kInstanceOf:
case FeedbackSlotKind::kTypeOf:
case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral:
case FeedbackSlotKind::kStoreInArrayLiteral: {
os << InlineCacheState2String(ic_state());
break;
}
case FeedbackSlotKind::kLoadGlobalInsideTypeof:
case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
case FeedbackSlotKind::kStoreGlobalSloppy:
case FeedbackSlotKind::kStoreGlobalStrict: {
os << InlineCacheState2String(ic_state());
if (ic_state() == InlineCacheState::MONOMORPHIC) {
os << "\n ";
if (GetFeedback().IsCleared()) {
// Handler mode: feedback is the cleared value, extra is the handler.
if (IsLoadGlobalICKind(slot_kind)) {
LoadHandler::PrintHandler(GetFeedbackExtra().GetHeapObjectOrSmi(),
os);
} else {
StoreHandler::PrintHandler(GetFeedbackExtra().GetHeapObjectOrSmi(),
os);
}
} else if (IsPropertyCell(GetFeedback().GetHeapObjectOrSmi())) {
os << Brief(GetFeedback());
} else {
// Lexical variable mode: the variable location is encoded in the SMI.
int handler = GetFeedback().GetHeapObjectOrSmi().ToSmi().value();
os << (IsLoadGlobalICKind(slot_kind) ? "Load" : "Store");
os << "Handler(Lexical variable mode)(context ix = "
<< FeedbackNexus::ContextIndexBits::decode(handler)
<< ", slot ix = " << FeedbackNexus::SlotIndexBits::decode(handler)
<< ")";
}
}
break;
}
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kLoadProperty: {
os << InlineCacheState2String(ic_state());
if (ic_state() == InlineCacheState::MONOMORPHIC ||
ic_state() == InlineCacheState::HOMOMORPHIC) {
os << "\n " << Brief(GetFeedback()) << ": ";
if (GetFeedbackExtra().IsCleared()) {
os << " [cleared]\n";
break;
}
Tagged<Object> handler = GetFeedbackExtra().GetHeapObjectOrSmi();
if (IsWeakFixedArray(handler) &&
!Cast<WeakFixedArray>(handler)->get(0).IsCleared()) {
handler = Cast<WeakFixedArray>(handler)->get(0).GetHeapObjectOrSmi();
}
LoadHandler::PrintHandler(handler, os);
} else if (ic_state() == InlineCacheState::POLYMORPHIC) {
Tagged<HeapObject> feedback = GetFeedback().GetHeapObject();
Tagged<WeakFixedArray> array;
if (IsName(feedback)) {
os << " with name " << Brief(feedback);
array = Cast<WeakFixedArray>(GetFeedbackExtra().GetHeapObject());
} else {
array = Cast<WeakFixedArray>(feedback);
}
uint32_t array_len = array->ulength().value();
for (uint32_t i = 0; i < array_len; i += 2) {
os << "\n " << Brief(array->get(i)) << ": ";
if (!array->get(i + 1).IsCleared()) {
LoadHandler::PrintHandler(array->get(i + 1).GetHeapObjectOrSmi(),
os);
} else {
os << "[cleared]\n";
}
}
}
break;
}
case FeedbackSlotKind::kDefineNamedOwn:
case FeedbackSlotKind::kDefineKeyedOwn:
case FeedbackSlotKind::kSetNamedSloppy:
case FeedbackSlotKind::kSetNamedStrict:
case FeedbackSlotKind::kSetKeyedSloppy:
case FeedbackSlotKind::kSetKeyedStrict: {
os << InlineCacheState2String(ic_state());
if (GetFeedback().IsCleared()) {
os << "\n [cleared]";
break;
}
if (ic_state() == InlineCacheState::MONOMORPHIC) {
Tagged<HeapObject> feedback = GetFeedback().GetHeapObject();
if (GetFeedbackExtra().IsCleared()) {
os << " [cleared]\n";
break;
}
if (IsName(feedback)) {
os << " with name " << Brief(feedback);
Tagged<WeakFixedArray> array =
Cast<WeakFixedArray>(GetFeedbackExtra().GetHeapObject());
os << "\n " << Brief(array->get(0)) << ": ";
if (array->get(1).IsCleared()) {
os << "[cleared]\n";
} else {
Tagged<Object> handler = array->get(1).GetHeapObjectOrSmi();
StoreHandler::PrintHandler(handler, os);
}
} else {
os << "\n " << Brief(feedback) << ": ";
StoreHandler::PrintHandler(GetFeedbackExtra().GetHeapObjectOrSmi(),
os);
}
} else if (ic_state() == InlineCacheState::POLYMORPHIC) {
Tagged<HeapObject> feedback = GetFeedback().GetHeapObject();
Tagged<WeakFixedArray> array;
if (IsName(feedback)) {
os << " with name " << Brief(feedback);
array = Cast<WeakFixedArray>(GetFeedbackExtra().GetHeapObject());
} else {
array = Cast<WeakFixedArray>(feedback);
}
uint32_t array_len = array->ulength().value();
for (uint32_t i = 0; i < array_len; i += 2) {
os << "\n " << Brief(array->get(i)) << ": ";
if (!array->get(i + 1).IsCleared()) {
StoreHandler::PrintHandler(array->get(i + 1).GetHeapObjectOrSmi(),
os);
}
}
}
break;
}
case FeedbackSlotKind::kBinaryOp: {
os << "BinaryOp:" << GetBinaryOperationFeedback();
break;
}
case FeedbackSlotKind::kCompareOp: {
os << "CompareOp:" << GetCompareOperationFeedback();
break;
}
case FeedbackSlotKind::kForIn: {
os << "ForIn:" << GetForInFeedback();
break;
}
case FeedbackSlotKind::kLiteral:
break;
case FeedbackSlotKind::kJumpLoop:
os << "JumpLoop";
break;
case FeedbackSlotKind::kStringAddAndInternalize: {
os << "StringAddAndInternalize:" << GetBinaryOperationFeedback()
<< " with cache " << Brief(Cast<Object>(GetFeedbackExtra()));
break;
}
case FeedbackSlotKind::kInvalid:
UNREACHABLE();
}
}
void Oddball::OddballPrint(std::ostream& os) {
PrintHeapObjectHeaderWithoutMap(Tagged<HeapObject>(this), os, "Oddball");
os << ": ";
Tagged<String> s = to_string();
os << s->PrefixForDebugPrint();
s->PrintUC16(os);
os << s->SuffixForDebugPrint();
os << std::endl;
}
void Hole::HolePrint(std::ostream& os) {
PrintHeapObjectHeaderWithoutMap(this, os, "Hole");
ReadOnlyRoots roots = GetReadOnlyRoots();
#define PRINT_SPECIFIC_HOLE(type, name, CamelName) \
if (this == roots.name()) { \
os << "\n <" #name ">"; \
}
HOLE_LIST(PRINT_SPECIFIC_HOLE);
#undef PRINT_SPECIFIC_HOLE
os << std::endl;
}
void JSAsyncFunctionObject::JSAsyncFunctionObjectPrint(std::ostream& os) {
JSGeneratorObjectPrint(os);
}
void JSAsyncGeneratorObject::JSAsyncGeneratorObjectPrint(std::ostream& os) {
JSGeneratorObjectPrint(os);
}
void JSArgumentsObject::JSArgumentsObjectPrint(std::ostream& os) {
JSObjectPrint(os);
}
void JSStringIterator::JSStringIteratorPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSStringIterator");
os << "\n - string: " << Brief(string());
os << "\n - index: " << index();
JSObjectPrintBody(os, this);
}
void JSAsyncFromSyncIterator::JSAsyncFromSyncIteratorPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSAsyncFromSyncIterator");
os << "\n - sync_iterator: " << Brief(sync_iterator());
os << "\n - next: " << Brief(next());
JSObjectPrintBody(os, this);
}
void JSValidIteratorWrapper::JSValidIteratorWrapperPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSValidIteratorWrapper");
os << "\n - underlying.object: " << Brief(underlying_object());
os << "\n - underlying.next: " << Brief(underlying_next());
JSObjectPrintBody(os, this);
}
void JSPrimitiveWrapper::JSPrimitiveWrapperPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSPrimitiveWrapper");
os << "\n - value: " << Brief(value());
JSObjectPrintBody(os, this);
}
void JSMessageObject::JSMessageObjectPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSMessageObject");
os << "\n - type: " << static_cast<int>(type());
os << "\n - arguments: " << Brief(argument());
os << "\n - script: " << Brief(script());
os << "\n - stack_trace: " << Brief(stack_trace());
os << "\n - shared_info: " << Brief(shared_info());
if (shared_info() == Smi::zero()) {
os << " (cleared after calculating line ends)";
} else if (shared_info() == Smi::FromInt(-1)) {
os << "(no line ends needed)";
}
os << "\n - bytecode_offset: " << bytecode_offset();
os << "\n - start_position: " << start_position();
os << "\n - end_position: " << end_position();
os << "\n - error_level: " << error_level();
JSObjectPrintBody(os, this);
}
void String::StringPrint(std::ostream& os) {
PrintHeapObjectHeaderWithoutMap(this, os, "String");
os << ": ";
os << PrefixForDebugPrint();
PrintUC16(os, 0, length());
os << SuffixForDebugPrint();
}
void Name::NamePrint(std::ostream& os) {
if (Is<String>(this)) {
Cast<String>(this)->StringPrint(os);
} else {
os << Brief(this);
}
}
static const char* const weekdays[] = {"???", "Sun", "Mon", "Tue",
"Wed", "Thu", "Fri", "Sat"};
void JSDate::JSDatePrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSDate");
os << "\n - value: " << value();
os << "\n - cache_stamp: " << cache_stamp()
<< " (vs. Isolate::date_time_stamp: "
<< Brief(Isolate::Current()->date_cache_stamp()) << ")";
if (!IsSmi(year())) {
os << "\n - time = NaN\n";
} else {
// TODO(svenpanne) Add some basic formatting to our streams.
auto buf = base::OwnedVector<char>::NewForOverwrite(100);
SNPrintF(buf.as_vector(), "\n - time = %s %04d/%02d/%02d %02d:%02d:%02d\n",
weekdays[IsSmi(weekday()) ? Smi::ToInt(weekday()) + 1 : 0],
IsSmi(year()) ? Smi::ToInt(year()) : -1,
IsSmi(month()) ? Smi::ToInt(month()) : -1,
IsSmi(day()) ? Smi::ToInt(day()) : -1,
IsSmi(hour()) ? Smi::ToInt(hour()) : -1,
IsSmi(min()) ? Smi::ToInt(min()) : -1,
IsSmi(sec()) ? Smi::ToInt(sec()) : -1);
os << buf.begin();
}
JSObjectPrintBody(os, this);
}
void JSSet::JSSetPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSSet");
os << "\n - table: " << Brief(table());
JSObjectPrintBody(os, this);
}
void JSMap::JSMapPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSMap");
os << "\n - table: " << Brief(table());
JSObjectPrintBody(os, this);
}
void JSCollectionIterator::JSCollectionIteratorPrint(std::ostream& os,
const char* name) {
JSObjectPrintHeader(os, this, name);
os << "\n - table: " << Brief(table());
os << "\n - index: " << Brief(index());
JSObjectPrintBody(os, this);
}
void JSSetIterator::JSSetIteratorPrint(std::ostream& os) {
JSCollectionIteratorPrint(os, "JSSetIterator");
}
void JSMapIterator::JSMapIteratorPrint(std::ostream& os) {
JSCollectionIteratorPrint(os, "JSMapIterator");
}
void JSWeakRef::JSWeakRefPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSWeakRef");
os << "\n - target: " << Brief(target());
JSObjectPrintBody(os, this);
}
void JSShadowRealm::JSShadowRealmPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSShadowRealm");
os << "\n - native_context: " << Brief(native_context());
JSObjectPrintBody(os, this);
}
void JSWrappedFunction::JSWrappedFunctionPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSWrappedFunction");
os << "\n - wrapped_target_function: " << Brief(wrapped_target_function());
JSObjectPrintBody(os, this);
}
void JSFinalizationRegistry::JSFinalizationRegistryPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSFinalizationRegistry");
os << "\n - native_context: " << Brief(native_context());
os << "\n - cleanup: " << Brief(cleanup());
os << "\n - active_cells: " << Brief(active_cells());
Tagged<Object> active_cell = active_cells();
while (IsWeakCell(active_cell)) {
os << "\n - " << Brief(active_cell);
active_cell = Cast<WeakCell>(active_cell)->next();
}
os << "\n - cleared_cells: " << Brief(cleared_cells());
Tagged<Object> cleared_cell = cleared_cells();
while (IsWeakCell(cleared_cell)) {
os << "\n - " << Brief(cleared_cell);
cleared_cell = Cast<WeakCell>(cleared_cell)->next();
}
os << "\n - key_map: " << Brief(key_map());
JSObjectPrintBody(os, this);
}
void JSSharedArray::JSSharedArrayPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSSharedArray");
if (HeapLayout::InWritableSharedSpace(Tagged<HeapObject>(this))) {
os << " (shared)";
}
JSObjectPrintBody(os, this);
}
void JSSharedStruct::JSSharedStructPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSSharedStruct");
if (HeapLayout::InWritableSharedSpace(Tagged<HeapObject>(this))) {
os << " (shared)";
}
JSObjectPrintBody(os, this);
}
void JSAtomicsMutex::JSAtomicsMutexPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSAtomicsMutex");
if (HeapLayout::InWritableSharedSpace(Tagged<HeapObject>(this))) {
os << " (shared)";
}
os << "\n - state: " << this->state();
os << "\n - owner_thread_id: " << this->owner_thread_id();
JSObjectPrintBody(os, this);
}
void JSAtomicsCondition::JSAtomicsConditionPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSAtomicsCondition");
if (HeapLayout::InWritableSharedSpace(Tagged<HeapObject>(this))) {
os << " (shared)";
}
os << "\n - state: " << this->state();
JSObjectPrintBody(os, this);
}
std::ostream& operator<<(std::ostream& os, DisposableStackState state) {
switch (state) {
case DisposableStackState::kPending:
return os << "Pending";
case DisposableStackState::kDisposed:
return os << "Disposed";
}
UNREACHABLE();
}
void JSDisposableStackBase::JSDisposableStackBasePrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSDisposableStack");
os << "\n - stack: " << Brief(stack());
os << "\n - length: " << length();
os << "\n - state: " << state();
os << "\n - needs_await: " << needs_await();
os << "\n - has_awaited: " << has_awaited();
os << "\n - suppressed_error_created: " << suppressed_error_created();
os << "\n - error: " << error();
os << "\n - error_message: " << error_message();
JSObjectPrintBody(os, this);
}
void JSSyncDisposableStack::JSSyncDisposableStackPrint(std::ostream& os) {
JSDisposableStackBasePrint(os);
}
void JSAsyncDisposableStack::JSAsyncDisposableStackPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSAsyncDisposableStack");
os << "\n - stack: " << Brief(stack());
os << "\n - length: " << length();
os << "\n - state: " << state();
os << "\n - needs_await: " << needs_await();
os << "\n - has_awaited: " << has_awaited();
os << "\n - suppressed_error_created: " << suppressed_error_created();
os << "\n - error: " << error();
os << "\n - error_message: " << error_message();
JSObjectPrintBody(os, this);
}
void JSIteratorHelper::JSIteratorHelperPrintHeader(std::ostream& os,
const char* helper_name) {
JSObjectPrintHeader(os, this, helper_name);
os << "\n - state: " << state();
}
void JSIteratorHelperSimple::JSIteratorHelperSimplePrintHeader(
std::ostream& os, const char* helper_name) {
JSIteratorHelperPrintHeader(os, helper_name);
os << "\n - underlying.object: " << Brief(underlying_iterator_object());
os << "\n - underlying.next: " << Brief(underlying_iterator_next());
}
void JSIteratorMapHelper::JSIteratorMapHelperPrint(std::ostream& os) {
JSIteratorHelperSimplePrintHeader(os, "JSIteratorMapHelper");
os << "\n - mapper: " << Brief(mapper());
os << "\n - counter: " << counter();
JSObjectPrintBody(os, this);
}
void JSIteratorFilterHelper::JSIteratorFilterHelperPrint(std::ostream& os) {
JSIteratorHelperSimplePrintHeader(os, "JSIteratorFilterHelper");
os << "\n - predicate: " << Brief(predicate());
os << "\n - counter: " << counter();
JSObjectPrintBody(os, this);
}
void JSIteratorTakeHelper::JSIteratorTakeHelperPrint(std::ostream& os) {
JSIteratorHelperSimplePrintHeader(os, "JSIteratorTakeHelper");
os << "\n - remaining: " << remaining();
JSObjectPrintBody(os, this);
}
void JSIteratorDropHelper::JSIteratorDropHelperPrint(std::ostream& os) {
JSIteratorHelperSimplePrintHeader(os, "JSIteratorDropHelper");
os << "\n - remaining: " << remaining();
JSObjectPrintBody(os, this);
}
void JSIteratorFlatMapHelper::JSIteratorFlatMapHelperPrint(std::ostream& os) {
JSIteratorHelperSimplePrintHeader(os, "JSIteratorFlatMapHelper");
os << "\n - mapper: " << Brief(mapper());
os << "\n - counter: " << counter();
os << "\n - inner_iterator.object" << Brief(inner_iterator_object());
os << "\n - inner_iterator.next" << Brief(inner_iterator_next());
JSObjectPrintBody(os, this);
}
void JSIteratorConcatHelper::JSIteratorConcatHelperPrint(std::ostream& os) {
JSIteratorHelperSimplePrintHeader(os, "JSIteratorConcatHelper");
os << "\n - iterables: " << Brief(iterables()) << " {";
PrintFixedArrayElements(os, iterables(), iterables()->ulength().value());
os << "\n - current: " << current();
JSObjectPrintBody(os, this);
}
void JSIteratorZipHelper::JSIteratorZipHelperPrintHeader(
std::ostream& os, const char* helper_name) {
JSIteratorHelperPrintHeader(os, helper_name);
os << "\n - underlying_iterators: " << Brief(underlying_iterators());
os << "\n - mode: " << mode();
os << "\n - active_count: " << active_count();
os << "\n - padding: " << Brief(padding());
}
void JSIteratorZipHelper::JSIteratorZipHelperPrint(std::ostream& os) {
JSIteratorZipHelperPrintHeader(os, "JSIteratorZipHelper");
JSObjectPrintBody(os, this);
}
void JSIteratorZipKeyedHelper::JSIteratorZipKeyedHelperPrint(std::ostream& os) {
JSIteratorZipHelperPrintHeader(os, "JSIteratorZipKeyedHelper");
os << "\n - keys: " << Brief(keys());
JSObjectPrintBody(os, this);
}
void JSWeakMap::JSWeakMapPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSWeakMap");
os << "\n - table: " << Brief(table());
JSObjectPrintBody(os, this);
}
void JSWeakSet::JSWeakSetPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSWeakSet");
os << "\n - table: " << Brief(table());
JSObjectPrintBody(os, this);
}
void JSArrayBuffer::JSArrayBufferPrint(std::ostream& os) {
JSAPIObjectWithEmbedderSlotsPrintHeader(os, this, "JSArrayBuffer");
os << "\n - backing_store: " << backing_store();
os << "\n - byte_length: " << GetByteLength();
os << "\n - max_byte_length: " << max_byte_length();
if (has_detach_key()) {
os << "\n - detach key: " << Brief(detach_key()->value());
os << "\n - views: (many views)";
} else {
os << "\n - detach key: (undefined)";
if (views() == kNoView) {
os << "\n - views: (no views)";
} else if (views() == kManyViews) {
os << "\n - views: (many views)";
} else {
os << "\n - views: " << Brief(views());
}
}
if (is_external()) os << "\n - external";
if (is_detachable()) os << "\n - detachable";
if (was_detached()) os << "\n - detached";
if (is_shared()) os << "\n - shared";
if (is_resizable_by_js()) os << "\n - resizable_by_js";
JSObjectPrintBody(os, this, !was_detached());
}
void JSDetachedTypedArray::JSDetachedTypedArrayPrint(std::ostream& os) {
JSAPIObjectWithEmbedderSlotsPrintHeader(os, this, "JSDetachedTypedArray");
os << "\n - buffer: " << Brief(buffer());
os << "\n - byte_offset: " << byte_offset();
if (byte_length() != 0) {
os << "\n - invalid length";
}
if (!WasDetached()) {
os << "\n - invalid detached state!";
}
os << "\n - data_ptr: " << DataPtr();
Tagged_t base_ptr = static_cast<Tagged_t>(base_pointer().ptr());
os << "\n - base_pointer: "
<< reinterpret_cast<void*>(static_cast<Address>(base_ptr));
if (!IsJSArrayBuffer(buffer())) {
os << "\n <invalid buffer>\n";
return;
}
JSObjectPrintBody(os, this, false);
}
void JSTypedArray::JSTypedArrayPrint(std::ostream& os) {
JSAPIObjectWithEmbedderSlotsPrintHeader(os, this, "JSTypedArray");
os << "\n - buffer: " << Brief(buffer());
os << "\n - byte_offset: " << byte_offset();
os << "\n - byte_length: " << byte_length();
os << "\n - length: " << GetLength();
os << "\n - data_ptr: " << DataPtr();
Tagged_t base_ptr = static_cast<Tagged_t>(base_pointer().ptr());
os << "\n - base_pointer: "
<< reinterpret_cast<void*>(static_cast<Address>(base_ptr));
os << "\n - external_pointer: "
<< reinterpret_cast<void*>(external_pointer());
if (!IsJSArrayBuffer(buffer())) {
os << "\n <invalid buffer>\n";
return;
}
if (WasDetached()) os << "\n - detached";
if (is_length_tracking()) os << "\n - length-tracking";
if (is_backed_by_rab()) os << "\n - backed-by-rab";
JSObjectPrintBody(os, this, !WasDetached());
}
void JSArrayIterator::JSArrayIteratorPrint(std::ostream& os) { // NOLING
JSObjectPrintHeader(os, this, "JSArrayIterator");
os << "\n - iterated_object: " << Brief(iterated_object());
os << "\n - next_index: " << Brief(next_index());
os << "\n - kind: " << kind();
JSObjectPrintBody(os, this);
}
void JSDataView::JSDataViewPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSDataView");
os << "\n - buffer =" << Brief(buffer());
os << "\n - byte_offset: " << byte_offset();
os << "\n - byte_length: " << byte_length();
if (!IsJSArrayBuffer(buffer())) {
os << "\n <invalid buffer>";
return;
}
if (WasDetached()) os << "\n - detached";
JSObjectPrintBody(os, this, !WasDetached());
}
void JSRabGsabDataView::JSRabGsabDataViewPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSRabGsabDataView");
os << "\n - buffer =" << Brief(buffer());
os << "\n - byte_offset: " << byte_offset();
os << "\n - byte_length: " << byte_length();
if (is_length_tracking()) os << "\n - length-tracking";
if (is_backed_by_rab()) os << "\n - backed-by-rab";
if (!IsJSArrayBuffer(buffer())) {
os << "\n <invalid buffer>";
return;
}
if (WasDetached()) os << "\n - detached";
JSObjectPrintBody(os, this, !WasDetached());
}
void JSBoundFunction::JSBoundFunctionPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSBoundFunction");
os << "\n - bound_target_function: " << Brief(bound_target_function());
os << "\n - bound_this: " << Brief(bound_this());
os << "\n - bound_arguments: " << Brief(bound_arguments());
JSObjectPrintBody(os, this);
}
void JSFunction::JSFunctionPrint(std::ostream& os) {
Isolate* isolate = Isolate::Current();
JSObjectPrintHeader(os, this, "Function");
os << "\n - function prototype: ";
if (Is<JSFunctionWithPrototype>(this)) {
if (has_prototype()) {
os << Brief(prototype());
if (!IsJSReceiver(prototype())) {
os << " (non-instance prototype)";
}
}
os << "\n - initial_map: ";
if (has_initial_map()) os << Brief(initial_map());
} else {
os << "<no-prototype-slot>";
}
os << "\n - shared_info: " << Brief(shared());
os << "\n - name: " << Brief(shared()->Name());
// Print Builtin name for builtin functions
Builtin builtin = code(isolate)->builtin_id();
if (Builtins::IsBuiltinId(builtin)) {
os << "\n - builtin: " << isolate->builtins()->name(builtin);
}
os << "\n - formal_parameter_count: ";
int formal_parameter_count =
shared()->internal_formal_parameter_count_with_receiver();
if (formal_parameter_count == kDontAdaptArgumentsSentinel) {
os << "kDontAdaptArgumentsSentinel";
} else {
os << formal_parameter_count;
}
os << "\n - kind: " << shared()->kind();
os << "\n - context: " << Brief(context());
os << "\n - code: " << Brief(code(isolate));
os << "\n - dispatch_handle: 0x" << std::hex << dispatch_handle() << std::dec;
if (has_feedback_vector() &&
raw_feedback_cell()->dispatch_handle() != dispatch_handle()) {
os << "\n - canonical feedback cell dispatch_handle: 0x" << std::hex
<< raw_feedback_cell()->dispatch_handle() << std::dec;
}
if (IsTieringRequestedOrInProgress(isolate)) {
os << "\n - tiering request ";
if (tiering_in_progress()) {
os << "in_progress ";
}
isolate->js_dispatch_table().PrintCurrentTieringRequest(
dispatch_handle(), Isolate::Current(), os);
}
CodeKind code_kind = code(isolate)->kind();
if (code_kind == CodeKind::FOR_TESTING) {
os << "\n - FOR_TESTING";
} else if (code_kind == CodeKind::FOR_TESTING_JS) {
os << "\n - FOR_TESTING_JS";
} else if (ActiveTierIsIgnition(isolate)) {
os << "\n - interpreted";
if (shared()->HasBytecodeArray()) {
os << "\n - bytecode: " << shared()->GetBytecodeArray(isolate);
}
}
#if V8_ENABLE_WEBASSEMBLY
if (WasmExportedFunction::IsWasmExportedFunction(Tagged<HeapObject>(this))) {
Tagged<WasmExportedFunction> function = Cast<WasmExportedFunction>(this);
Tagged<WasmExportedFunctionData> data =
function->shared()->wasm_exported_function_data();
os << "\n - Wasm instance data: " << Brief(data->instance_data());
os << "\n - Wasm function index: " << data->function_index();
}
#endif // V8_ENABLE_WEBASSEMBLY
shared()->PrintSourceCode(os);
JSObjectPrintBody(os, this);
os << " - feedback vector: ";
if (!shared()->HasFeedbackMetadata()) {
os << "feedback metadata is not available in SFI\n";
} else if (has_feedback_vector()) {
feedback_vector()->FeedbackVectorPrint(os);
} else if (has_closure_feedback_cell_array()) {
os << "No feedback vector, but we have a closure feedback cell array\n";
closure_feedback_cell_array()->ClosureFeedbackCellArrayPrint(os);
} else {
os << "not available\n";
}
}
void JSFunctionWithoutPrototype::JSFunctionWithoutPrototypePrint(
std::ostream& os) {
Cast<JSFunction>(this)->JSFunctionPrint(os);
}
void JSFunctionWithPrototype::JSFunctionWithPrototypePrint(std::ostream& os) {
Cast<JSFunction>(this)->JSFunctionPrint(os);
}
void SharedFunctionInfo::PrintSourceCode(std::ostream& os) {
if (!HasSourceCode()) return;
os << "\n - source code: ";
Tagged<String> source = Cast<String>(Cast<Script>(script())->source());
int start = StartPosition();
int length = EndPosition() - start;
std::unique_ptr<char[]> source_string = source->ToCString(start, length);
os << source_string.get();
}
void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) {
PrintHeader(os, "SharedFunctionInfo");
os << "\n - name: ";
if (HasSharedName()) {
os << Brief(Name());
} else {
os << "<no-shared-name>";
}
if (HasInferredName()) {
os << "\n - inferred name: " << Brief(inferred_name());
}
if (class_scope_has_private_brand()) {
os << "\n - class_scope_has_private_brand";
}
if (has_static_private_methods_or_accessors()) {
os << "\n - has_static_private_methods_or_accessors";
}
if (private_name_lookup_skips_outer_class()) {
os << "\n - private_name_lookup_skips_outer_class";
}
os << "\n - kind: " << kind();
os << "\n - syntax kind: " << syntax_kind();
os << "\n - function_map_index: " << function_map_index();
os << "\n - formal_parameter_count: ";
int formal_parameter_count = internal_formal_parameter_count_with_receiver();
if (formal_parameter_count == kDontAdaptArgumentsSentinel) {
os << "kDontAdaptArgumentsSentinel";
} else {
os << formal_parameter_count;
}
os << "\n - expected_nof_properties: "
<< static_cast<int>(expected_nof_properties());
os << "\n - language_mode: " << language_mode();
if (HasTrustedData()) {
os << "\n - trusted_function_data: "
<< Brief(GetTrustedData(GetCurrentIsolateForSandbox()));
} else {
os << "\n - trusted_function_data: <empty>";
}
os << "\n - untrusted_function_data: " << Brief(GetUntrustedData());
os << "\n - code (from function_data): ";
Isolate* isolate;
if (GetIsolateFromHeapObject(Tagged<SharedFunctionInfo>(this), &isolate)) {
os << Brief(GetCode(isolate));
} else {
os << kUnavailableString;
}
PrintSourceCode(os);
// Script files are often large, thus only print their {Brief} representation.
os << "\n - script: " << Brief(script());
os << "\n - function token position: " << function_token_position();
os << "\n - start position: " << StartPosition();
os << "\n - end position: " << EndPosition();
os << "\n - scope info: " << Brief(scope_info());
if (HasOuterScopeInfo()) {
os << "\n - outer scope info: " << Brief(GetOuterScopeInfo());
}
os << "\n - length: " << length();
os << "\n - feedback_metadata: ";
if (HasFeedbackMetadata()) {
feedback_metadata()->FeedbackMetadataPrint(os);
} else {
os << "<none>";
}
os << "\n - function_literal_id: " << function_literal_id(kRelaxedLoad);
os << "\n - unique_id: " << unique_id();
os << "\n - age: " << age();
os << "\n";
}
void SharedFunctionInfoWrapper::SharedFunctionInfoWrapperPrint(
std::ostream& os) {
PrintHeader(os, "SharedFunctionInfoWrapper");
os << "\n sfi: " << Brief(shared_info());
}
void InterpreterData::InterpreterDataPrint(std::ostream& os) {
PrintHeader(os, "InterpreterData");
os << "\n - bytecode_array: " << Brief(bytecode_array());
os << "\n - interpreter_trampoline: " << Brief(interpreter_trampoline());
os << "\n";
}
void JSGlobalProxy::JSGlobalProxyPrint(std::ostream& os) {
JSAPIObjectWithEmbedderSlotsPrintHeader(os, this, "JSGlobalProxy");
JSObjectPrintBody(os, this);
}
void JSGlobalObject::JSGlobalObjectPrint(std::ostream& os) {
Tagged<JSGlobalObject> self(this);
JSAPIObjectWithEmbedderSlotsPrintHeader(os, Cast<JSObject>(self),
"JSGlobalObject");
os << "\n - global_proxy: " << Brief(global_proxy());
os << "\n - global_proxy_for_api: " << Brief(global_proxy_for_api());
JSObjectPrintBody(os, Cast<JSObject>(self));
}
void Cell::CellPrint(std::ostream& os) {
PrintHeader(os, "Cell");
os << "\n - maybe_value: " << Brief(maybe_value());
os << "\n";
}
void MegaDomHandler::MegaDomHandlerPrint(std::ostream& os) {
PrintHeader(os, "MegaDomHandler");
os << "\n - accessor: " << Brief(accessor());
os << "\n - context: " << Brief(context());
os << "\n";
}
void PropertyCell::PropertyCellPrint(std::ostream& os) {
PrintHeader(os, "PropertyCell");
os << "\n - name: ";
name()->NamePrint(os);
os << "\n - value: " << Brief(value(kAcquireLoad));
os << "\n - details: ";
PropertyDetails details = property_details(kAcquireLoad);
details.PrintAsSlowTo(os, true);
os << "\n - cell_type: " << details.cell_type();
os << "\n - dependent code: " << dependent_code();
os << "\n";
}
void InstructionStream::InstructionStreamPrint(std::ostream& os) {
code(kAcquireLoad)->CodePrint(os);
}
void Code::CodePrint(std::ostream& os, const char* name, Address current_pc) {
// This prints the entire {Code,InstructionStream} composite object.
//
// First, Code:
PrintHeader(os, "Code");
os << "\n - kind: " << CodeKindToString(kind());
if (is_builtin()) {
os << "\n - builtin_id: " << Builtins::name(builtin_id());
}
os << "\n - deoptimization_data_or_interpreter_data: "
<< Brief(raw_deoptimization_data_or_interpreter_data());
os << "\n - position_table: " << Brief(raw_position_table());
os << "\n - parameter_count: " << parameter_count();
os << "\n - instruction_stream: " << Brief(raw_instruction_stream());
os << "\n - instruction_start: "
<< reinterpret_cast<void*>(instruction_start());
os << "\n - is_disabled_builtin: " << is_disabled_builtin();
os << "\n - is_turbofanned: " << is_turbofanned();
os << "\n - stack_slots: " << stack_slots();
os << "\n - marked_for_deoptimization: " << marked_for_deoptimization();
os << "\n - embedded_objects_cleared: " << embedded_objects_cleared();
os << "\n - can_have_weak_objects: " << can_have_weak_objects();
os << "\n - instruction_size: " << instruction_size();
os << "\n - metadata_size: " << metadata_size();
if (kind() != CodeKind::WASM_TO_JS_FUNCTION) {
os << "\n - inlined_bytecode_size: " << inlined_bytecode_size();
} else {
os << "\n - wasm_js_tagged_parameter_count: "
<< wasm_js_tagged_parameter_count();
os << "\n - wasm_js_first_tagged_parameter: "
<< wasm_js_first_tagged_parameter();
}
os << "\n - osr_offset: " << osr_offset();
os << "\n - handler_table_offset: " << handler_table_offset();
os << "\n - unwinding_info_offset: " << unwinding_info_offset();
if (V8_EMBEDDED_CONSTANT_POOL_BOOL) {
os << "\n - constant_pool_offset: " << constant_pool_offset();
}
os << "\n - code_comments_offset: " << code_comments_offset();
// Then, InstructionStream:
if (has_instruction_stream()) {
Tagged<InstructionStream> istream = instruction_stream();
os << "\n - instruction_stream.relocation_info: "
<< Brief(istream->relocation_info());
os << "\n - instruction_stream.body_size: " << istream->body_size();
}
os << "\n - dispatch_handle: 0x" << std::hex << js_dispatch_handle()
<< std::dec;
os << "\n";
// Finally, the disassembly:
#ifdef ENABLE_DISASSEMBLER
os << "\n--- Disassembly: ---\n";
Disassemble(name, os, Isolate::Current(), current_pc);
#endif
}
void CodeWrapper::CodeWrapperPrint(std::ostream& os) {
PrintHeader(os, "CodeWrapper");
os << "\n - code: " << Brief(code(Isolate::Current()));
os << "\n";
}
void Foreign::ForeignPrint(std::ostream& os) {
PrintHeader(os, "Foreign");
os << "\n - foreign address: "
<< reinterpret_cast<void*>(foreign_address_unchecked());
os << "\n";
}
void CppGCManagedBase::CppGCManagedBasePrint(std::ostream& os) {
PrintHeader(os, "CppGCManagedBase");
os << "\n";
}
void TrustedForeign::TrustedForeignPrint(std::ostream& os) {
PrintHeader(os, "TrustedForeign");
os << "\n - foreign address: " << reinterpret_cast<void*>(foreign_address());
os << "\n";
}
void AsyncGeneratorRequest::AsyncGeneratorRequestPrint(std::ostream& os) {
PrintHeader(os, "AsyncGeneratorRequest");
const char* mode = "Invalid!";
switch (resume_mode()) {
case JSGeneratorObject::kNext:
mode = ".next()";
break;
case JSGeneratorObject::kReturn:
mode = ".return()";
break;
case JSGeneratorObject::kThrow:
mode = ".throw()";
break;
}
os << "\n - resume mode: " << mode;
os << "\n - value: " << Brief(value());
os << "\n - next: " << Brief(next());
os << "\n";
}
static void PrintModuleFields(Tagged<Module> module, std::ostream& os) {
os << "\n - exports: " << Brief(module->exports());
os << "\n - status: " << module->status();
os << "\n - exception: " << Brief(module->exception());
}
void Module::ModulePrint(std::ostream& os) {
if (Is<SourceTextModule>(this)) {
Cast<SourceTextModule>(this)->SourceTextModulePrint(os);
} else if (Is<SyntheticModule>(this)) {
Cast<SyntheticModule>(this)->SyntheticModulePrint(os);
} else {
UNREACHABLE();
}
}
void SyntheticModule::SyntheticModulePrint(std::ostream& os) {
PrintHeader(os, "SyntheticModule");
os << "\n - exports: " << Brief(exports());
os << "\n - hash: " << hash();
os << "\n - status: " << status();
os << "\n - module_namespace: " << Brief(module_namespace());
os << "\n - deferred_module_namespace: "
<< Brief(deferred_module_namespace());
os << "\n - exception: " << Brief(exception());
os << "\n - top_level_capability: " << Brief(top_level_capability());
os << "\n - name: " << Brief(name());
os << "\n - export_names: " << Brief(export_names());
os << "\n - evaluation_steps: " << Brief(evaluation_steps());
os << "\n";
}
void SourceTextModule::SourceTextModulePrint(std::ostream& os) {
PrintHeader(os, "SourceTextModule");
PrintModuleFields(this, os);
os << "\n - sfi/code/info: " << Brief(code());
Tagged<Script> script = GetScript();
os << "\n - script: " << Brief(script);
os << "\n - origin: " << Brief(script->GetNameOrSourceURL());
os << "\n - requested_modules: " << Brief(requested_modules());
os << "\n - import_meta: " << Brief(import_meta(kAcquireLoad));
os << "\n - cycle_root: " << Brief(cycle_root());
os << "\n - has_toplevel_await: " << has_toplevel_await();
os << "\n - async_evaluation_ordinal: " << async_evaluation_ordinal();
os << "\n";
}
void JSModuleNamespace::JSModuleNamespacePrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSModuleNamespace");
os << "\n - module: " << Brief(module());
JSObjectPrintBody(os, this);
}
void JSDeferredModuleNamespace::JSDeferredModuleNamespacePrint(
std::ostream& os) {
JSObjectPrintHeader(os, this, "JSDeferredModuleNamespace");
os << "\n - deferred module: " << Brief(module());
JSObjectPrintBody(os, this);
}
void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) {
PrintHeader(os, "PrototypeInfo");
os << "\n - module namespace: " << Brief(module_namespace());
os << "\n - prototype users: " << Brief(prototype_users());
os << "\n - registry slot: " << registry_slot();
os << "\n - derived maps: " << Brief(derived_maps());
os << "\n - should_be_fast_map: " << should_be_fast_map();
os << "\n - prototype_chain_enum_cache: "
<< Brief(prototype_chain_enum_cache());
os << "\n - prototype_shared_closure_info: "
<< Brief(prototype_shared_closure_info());
for (int i = 0; i < PrototypeInfo::kCachedHandlerCount; i++) {
os << "\n - cached_handler[" << i << "]: " << Brief(cached_handler(i));
}
os << "\n";
}
void ArrayBoilerplateDescription::ArrayBoilerplateDescriptionPrint(
std::ostream& os) {
PrintHeader(os, "ArrayBoilerplateDescription");
os << "\n - elements kind: " << ElementsKindToString(elements_kind());
os << "\n - constant elements: " << Brief(constant_elements());
os << "\n";
}
void TurboshaftWord32RangeType::TurboshaftWord32RangeTypePrint(
std::ostream& os) {
PrintHeader(os, "TurboshaftWord32RangeType");
os << "\n - from: " << from();
os << "\n - to: " << to();
os << "\n";
}
void TurboshaftWord32SetType::TurboshaftWord32SetTypePrint(std::ostream& os) {
PrintHeader(os, "TurboshaftWord32SetType");
os << "\n - set_size: " << set_size();
os << "\n";
}
void TurboshaftWord64RangeType::TurboshaftWord64RangeTypePrint(
std::ostream& os) {
PrintHeader(os, "TurboshaftWord64RangeType");
os << "\n - from_high: " << from_high();
os << "\n - from_low: " << from_low();
os << "\n - to_high: " << to_high();
os << "\n - to_low: " << to_low();
os << "\n";
}
void TurboshaftWord64SetType::TurboshaftWord64SetTypePrint(std::ostream& os) {
PrintHeader(os, "TurboshaftWord64SetType");
os << "\n - set_size: " << set_size();
os << "\n";
}
void TurboshaftFloat64RangeType::TurboshaftFloat64RangeTypePrint(
std::ostream& os) {
PrintHeader(os, "TurboshaftFloat64RangeType");
os << "\n - special_values: " << special_values();
os << "\n - min: " << min();
os << "\n - max: " << max();
os << "\n";
}
void TurboshaftFloat64SetType::TurboshaftFloat64SetTypePrint(std::ostream& os) {
PrintHeader(os, "TurboshaftFloat64SetType");
os << "\n - special_values: " << special_values();
os << "\n - set_size: " << set_size();
os << "\n";
}
void TurbofanBitsetType::TurbofanBitsetTypePrint(std::ostream& os) {
PrintHeader(os, "TurbofanBitsetType");
os << "\n - bitset_low: " << bitset_low();
os << "\n - bitset_high: " << bitset_high();
os << "\n";
}
void TurbofanUnionType::TurbofanUnionTypePrint(std::ostream& os) {
PrintHeader(os, "TurbofanUnionType");
os << "\n - type1: " << Brief(type1());
os << "\n - type2: " << Brief(type2());
os << "\n";
}
void TurbofanRangeType::TurbofanRangeTypePrint(std::ostream& os) {
PrintHeader(os, "TurbofanRangeType");
os << "\n - min: " << min();
os << "\n - max: " << max();
os << "\n";
}
void TurbofanHeapConstantType::TurbofanHeapConstantTypePrint(std::ostream& os) {
PrintHeader(os, "TurbofanHeapConstantType");
os << "\n - constant: " << Brief(constant());
os << "\n";
}
void TurbofanOtherNumberConstantType::TurbofanOtherNumberConstantTypePrint(
std::ostream& os) {
PrintHeader(os, "TurbofanOtherNumberConstantType");
os << "\n - constant: " << constant();
os << "\n";
}
void SortState::SortStatePrint(std::ostream& os) {
PrintHeader(os, "SortState");
os << "\n - receiver: " << Brief(receiver_.load());
os << "\n - initial_receiver_map: " << Brief(initial_receiver_map_.load());
os << "\n - initial_receiver_length: "
<< Brief(initial_receiver_length_.load());
os << "\n - user_cmp_fn: " << Brief(user_cmp_fn_.load());
os << "\n - is_reset_to_generic: " << Brief(is_reset_to_generic_.load());
os << "\n - min_gallop: " << Brief(min_gallop_.load());
os << "\n - pending_runs_size: " << Brief(pending_runs_size_.load());
os << "\n - pending_runs: " << Brief(pending_runs_.load());
os << "\n - pending_powers: " << Brief(pending_powers_.load());
os << "\n - work_array: " << Brief(work_array_.load());
os << "\n - temp_array: " << Brief(temp_array_.load());
os << "\n - sort_length: " << Brief(sort_length_.load());
os << "\n - number_of_undefined: " << Brief(number_of_undefined_.load());
os << "\n";
}
void OnHeapBasicBlockProfilerData::OnHeapBasicBlockProfilerDataPrint(
std::ostream& os) {
PrintHeader(os, "OnHeapBasicBlockProfilerData");
os << "\n - block_ids: " << Brief(block_ids());
os << "\n - counts: " << Brief(counts());
os << "\n - branches: " << Brief(branches());
os << "\n - name: " << Brief(name());
os << "\n - schedule: " << Brief(schedule());
os << "\n - code: " << Brief(code());
os << "\n - hash: " << Brief(hash());
os << "\n";
}
#if V8_ENABLE_WEBASSEMBLY
void WasmFastApiCallData::WasmFastApiCallDataPrint(std::ostream& os) {
PrintHeader(os, "WasmFastApiCallData");
os << "\n - signature: " << Brief(signature());
os << "\n - callback_data: " << Brief(callback_data());
os << "\n - cached_map: " << Brief(cached_map());
os << "\n";
}
void WasmStringViewIter::WasmStringViewIterPrint(std::ostream& os) {
PrintHeader(os, "WasmStringViewIter");
os << "\n - string: " << Brief(string());
os << "\n - offset: " << offset();
os << "\n";
}
void WasmExceptionTag::WasmExceptionTagPrint(std::ostream& os) {
PrintHeader(os, "WasmExceptionTag");
os << "\n - index: " << index();
os << "\n";
}
void WasmTypeInfo::WasmTypeInfoPrint(std::ostream& os) {
PrintHeader(os, "WasmTypeInfo");
os << "\n - canonical type: " << type().name();
os << "\n - element type: " << element_type().name();
os << "\n - supertypes: ";
for (int i = 0; i < supertypes_length(); i++) {
os << "\n - " << Brief(supertypes(i));
}
os << "\n";
}
namespace {
void WasmStructPrintImpl(std::ostream& os, HeapObject* obj, int header_size) {
const wasm::CanonicalStructType* struct_type =
wasm::GetTypeCanonicalizer()->LookupStruct(
obj->map()->wasm_type_info()->type_index());
if (!v8_flags.wasm_merged_descriptors && struct_type->is_descriptor()) {
os << "\n - describes RTT: "
<< Brief(Cast<WasmStruct>(obj)->described_rtt());
}
os << "\n - fields (" << struct_type->field_count() << "):";
for (uint32_t i = 0; i < struct_type->field_count(); i++) {
wasm::CanonicalValueType field = struct_type->field(i);
os << "\n - " << field.short_name() << ": ";
uint32_t field_offset = header_size + struct_type->field_offset(i);
Address field_address = obj->GetFieldAddress(field_offset);
switch (field.kind()) {
case wasm::kI32:
os << base::ReadUnalignedValue<int32_t>(field_address);
break;
case wasm::kI64:
os << base::ReadUnalignedValue<int64_t>(field_address);
break;
case wasm::kF16:
os << fp16_ieee_to_fp32_value(
base::ReadUnalignedValue<uint16_t>(field_address));
break;
case wasm::kF32:
os << base::ReadUnalignedValue<float>(field_address);
break;
case wasm::kF64:
os << base::ReadUnalignedValue<double>(field_address);
break;
case wasm::kI8:
os << base::ReadUnalignedValue<int8_t>(field_address);
break;
case wasm::kI16:
os << base::ReadUnalignedValue<int16_t>(field_address);
break;
case wasm::kRef:
case wasm::kRefNull: {
Tagged_t raw = base::ReadUnalignedValue<Tagged_t>(field_address);
#if V8_COMPRESS_POINTERS
Address value = V8HeapCompressionScheme::DecompressTagged(raw);
#else
Address value = raw;
#endif
os << Brief(Tagged<Object>(value));
break;
}
case wasm::kS128:
os << "0x" << std::hex << std::setfill('0');
#ifdef V8_TARGET_BIG_ENDIAN
for (int j = 0; j < kSimd128Size; j++) {
#else
for (int j = kSimd128Size - 1; j >= 0; j--) {
#endif
os << std::setw(2)
<< static_cast<int>(reinterpret_cast<uint8_t*>(field_address)[j]);
}
os << std::dec << std::setfill(' ');
break;
case wasm::kTop:
case wasm::kBottom:
case wasm::kVoid:
UNREACHABLE();
}
}
os << "\n";
}
} // namespace
void WasmStruct::WasmStructPrint(std::ostream& os) {
PrintHeader(os, "WasmStruct");
WasmStructPrintImpl(os, this, WasmStruct::kHeaderSize);
}
void WasmCustomMap::WasmCustomMapPrint(std::ostream& os) {
PrintHeader(os, "WasmCustomMap");
WasmStructPrintImpl(os, this, WasmCustomMap::kHeaderSize);
}
void WasmCustomMapWrapper::WasmCustomMapWrapperPrint(std::ostream& os) {
PrintHeader(os, "WasmCustomMapWrapper");
os << "\n - wrapped: " << Brief(wrapped());
}
void WasmArray::WasmArrayPrint(std::ostream& os) {
PrintHeader(os, "WasmArray");
const wasm::CanonicalValueType element_type =
map()->wasm_type_info()->element_type();
uint32_t len = length();
os << "\n - element type: " << element_type.name();
os << "\n - length: " << len;
Address data_ptr = ptr() + WasmArray::kHeaderSize - kHeapObjectTag;
switch (element_type.kind()) {
case wasm::kI32:
PrintTypedArrayElements(os, reinterpret_cast<int32_t*>(data_ptr), len,
true);
break;
case wasm::kI64:
PrintTypedArrayElements(os, reinterpret_cast<int64_t*>(data_ptr), len,
true);
break;
case wasm::kF16:
PrintTypedArrayElements(os, reinterpret_cast<Fp16Printer*>(data_ptr), len,
true);
break;
case wasm::kF32:
PrintTypedArrayElements(os, reinterpret_cast<float*>(data_ptr), len,
true);
break;
case wasm::kF64:
PrintTypedArrayElements(os, reinterpret_cast<double*>(data_ptr), len,
true);
break;
case wasm::kI8:
PrintTypedArrayElements(os, reinterpret_cast<int8_t*>(data_ptr), len,
true);
break;
case wasm::kI16:
PrintTypedArrayElements(os, reinterpret_cast<int16_t*>(data_ptr), len,
true);
break;
case wasm::kRef:
case wasm::kRefNull: {
os << "\n - elements:";
constexpr uint32_t kWasmArrayMaximumPrintedElements = 5;
for (uint32_t i = 0;
i < std::min(this->length(), kWasmArrayMaximumPrintedElements);
i++) {
os << "\n " << static_cast<int>(i) << " - "
<< Brief(TaggedField<Object>::load(this, this->element_offset(i)));
}
if (this->length() > kWasmArrayMaximumPrintedElements) os << "\n ...";
break;
}
case wasm::kS128: {
os << "\n - elements:";
constexpr uint32_t kWasmArrayMaximumPrintedElements = 5;
for (uint32_t i = 0;
i < std::min(this->length(), kWasmArrayMaximumPrintedElements);
i++) {
os << "\n " << static_cast<int>(i) << " - 0x" << std::hex
<< std::setfill('0');
#ifdef V8_TARGET_BIG_ENDIAN
for (int j = 0; j < kSimd128Size; j++) {
#else
for (int j = kSimd128Size - 1; j >= 0; j--) {
#endif
os << std::setw(2)
<< static_cast<int>(
reinterpret_cast<uint8_t*>(this->ElementAddress(i))[j]);
}
os << std::dec << std::setfill(' ');
}
if (this->length() > kWasmArrayMaximumPrintedElements) os << "\n ...";
break;
}
case wasm::kTop:
case wasm::kBottom:
case wasm::kVoid:
UNREACHABLE();
}
os << "\n";
}
void WasmSuspenderObject::WasmSuspenderObjectPrint(std::ostream& os) {
PrintHeader(os, "WasmSuspenderObject");
os << "\n - stack: " << (stack() == nullptr ? -1 : stack()->id());
os << "\n - parent: ";
if (has_parent()) {
os << Brief(parent());
} else {
os << "<empty>";
}
os << "\n - promise: " << promise();
os << "\n - resume: " << resume();
os << "\n - reject: " << reject();
os << "\n";
}
void WasmSuspendingObject::WasmSuspendingObjectPrint(std::ostream& os) {
PrintHeader(os, "WasmSuspendingObject");
os << "\n - callable: " << callable();
os << "\n";
}
void WasmContinuationObject::WasmContinuationObjectPrint(std::ostream& os) {
PrintHeader(os, "WasmContinuationObject");
os << "\n - stack_obj: " << Brief(stack_obj());
os << "\n";
}
void WasmStackObject::WasmStackObjectPrint(std::ostream& os) {
PrintHeader(os, "WasmStackObject");
os << "\n - stack: " << (stack() == nullptr ? -1 : stack()->id());
os << "\n";
}
void WasmInstanceObject::WasmInstanceObjectPrint(std::ostream& os) {
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
JSObjectPrintHeader(os, this, "WasmInstanceObject");
os << "\n - trusted_data: " << Brief(trusted_data(isolate));
os << "\n - module_object: " << Brief(module_object());
os << "\n - exports_object: " << Brief(exports_object());
JSObjectPrintBody(os, this);
os << "\n";
}
void WasmTrustedInstanceData::WasmTrustedInstanceDataPrint(std::ostream& os) {
#define PRINT_WASM_INSTANCE_FIELD(name, convert) \
os << "\n - " #name ": " << convert(name());
#define PRINT_OPTIONAL_WASM_INSTANCE_FIELD(name, convert) \
if (has_##name()) os << "\n - " #name ": " << convert(name());
auto to_void_ptr = [](auto value) {
static_assert(sizeof(value) == kSystemPointerSize);
return reinterpret_cast<void*>(value);
};
PrintHeader(os, "WasmTrustedInstanceData");
PRINT_OPTIONAL_WASM_INSTANCE_FIELD(instance_object, Brief);
PRINT_OPTIONAL_WASM_INSTANCE_FIELD(native_context, Brief);
PRINT_WASM_INSTANCE_FIELD(memory_objects, Brief);
PRINT_WASM_INSTANCE_FIELD(untagged_globals_buffer, Brief);
PRINT_WASM_INSTANCE_FIELD(tagged_globals_buffer, Brief);
PRINT_WASM_INSTANCE_FIELD(imported_mutable_globals_buffers, Brief);
PRINT_WASM_INSTANCE_FIELD(imported_mutable_globals_offsets, Brief);
#if V8_ENABLE_DRUMBRAKE
PRINT_OPTIONAL_WASM_INSTANCE_FIELD(interpreter_handle, Brief);
#endif // V8_ENABLE_DRUMBRAKE
PRINT_WASM_INSTANCE_FIELD(tables, Brief);
PRINT_WASM_INSTANCE_FIELD(dispatch_table0, Brief);
PRINT_WASM_INSTANCE_FIELD(dispatch_tables, Brief);
PRINT_WASM_INSTANCE_FIELD(dispatch_table_for_imports, Brief);
PRINT_OPTIONAL_WASM_INSTANCE_FIELD(tags_table, Brief);
PRINT_WASM_INSTANCE_FIELD(func_refs, Brief);
PRINT_WASM_INSTANCE_FIELD(managed_object_maps, Brief);
PRINT_WASM_INSTANCE_FIELD(feedback_vectors, Brief);
PRINT_WASM_INSTANCE_FIELD(well_known_imports, Brief);
PRINT_WASM_INSTANCE_FIELD(memory0_start, to_void_ptr);
PRINT_WASM_INSTANCE_FIELD(memory0_size, +);
#if V8_ENABLE_DRUMBRAKE
PRINT_WASM_INSTANCE_FIELD(imported_function_indices, Brief);
#endif // V8_ENABLE_DRUMBRAKE
PRINT_WASM_INSTANCE_FIELD(jump_table_start, to_void_ptr);
PRINT_WASM_INSTANCE_FIELD(data_segments, Brief);
PRINT_WASM_INSTANCE_FIELD(element_segments, Brief);
PRINT_WASM_INSTANCE_FIELD(hook_on_function_call_address, to_void_ptr);
PRINT_WASM_INSTANCE_FIELD(tiering_budget_array, to_void_ptr);
PRINT_WASM_INSTANCE_FIELD(memory_bases_and_sizes, Brief);
PRINT_WASM_INSTANCE_FIELD(break_on_entry, static_cast<int>);
os << "\n";
#undef PRINT_OPTIONAL_WASM_INSTANCE_FIELD
#undef PRINT_WASM_INSTANCE_FIELD
}
void WasmDispatchTable::WasmDispatchTablePrint(std::ostream& os) {
PrintHeader(os, "WasmDispatchTable");
int len = length();
os << "\n - length: " << len;
os << "\n - capacity: " << capacity();
Tagged<ProtectedWeakFixedArray> uses = protected_uses();
os << "\n - uses: " << Brief(uses);
os << "\n - table type: " << table_type().name();
// Only print up to 55 elements; otherwise print the first 50 and "[...]".
int printed = len > 55 ? 50 : len;
for (int i = 0; i < printed; ++i) {
os << "\n " << std::setw(8) << i << ": sig: " << sig(i)
<< "; target: " << AsHex::Address(target(i).value())
<< "; implicit_arg: " << Brief(implicit_arg(i));
}
if (printed != len) os << "\n [...]";
os << "\n";
}
void WasmDispatchTableForImports::WasmDispatchTableForImportsPrint(
std::ostream& os) {
PrintHeader(os, "WasmDispatchTableForImports");
int len = length();
os << "\n - length: " << len;
// Only print up to 55 elements; otherwise print the first 50 and "[...]".
int printed = len > 55 ? 50 : len;
for (int i = 0; i < printed; ++i) {
os << "\n " << std::setw(8) << i
<< ": target: " << AsHex::Address(target(i).value())
<< "; implicit_arg: " << Brief(implicit_arg(i));
}
if (printed != len) os << "\n [...]";
os << "\n";
}
// Never called directly, as WasmFunctionData is an "abstract" class.
void WasmFunctionData::WasmFunctionDataPrint(std::ostream& os) {
os << "\n - func_ref: " << Brief(func_ref());
os << "\n - internal: " << Brief(internal());
os << "\n - js_promise_flags: " << js_promise_flags();
// No newline here; the caller prints it after printing additional fields.
}
void WasmExportedFunctionData::WasmExportedFunctionDataPrint(std::ostream& os) {
PrintHeader(os, "WasmExportedFunctionData");
WasmFunctionDataPrint(os);
os << "\n - instance_data: " << Brief(instance_data());
os << "\n - function_index: " << function_index();
os << "\n - wrapper_budget: " << wrapper_budget()->value();
os << "\n - packed_args_size: " << packed_args_size();
os << "\n - c_wrapper_code: "
<< c_wrapper_code(GetCurrentIsolateForSandbox());
os << "\n";
}
void WasmResumeData::WasmResumeDataPrint(std::ostream& os) {
PrintHeader(os, "WasmResumeData");
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
os << "\n - suspender: " << Brief(trusted_suspender(isolate));
os << "\n - on_resume: " << on_resume();
os << '\n';
}
void WasmImportData::WasmImportDataPrint(std::ostream& os) {
PrintHeader(os, "WasmImportData");
os << "\n - native_context: " << Brief(native_context());
os << "\n - callable: " << Brief(callable());
os << "\n - importing_instance_data: ";
if (has_importing_instance_data()) {
os << Brief(importing_instance_data());
} else {
os << "<empty>";
}
os << "\n - suspend: " << static_cast<int>(suspend());
os << "\n - wrapper_budget: " << wrapper_budget()->value();
os << "\n - sig: " << sig() << " (" << sig()->parameter_count() << " params, "
<< sig()->return_count() << " returns)";
os << "\n";
}
void WasmInternalFunction::WasmInternalFunctionPrint(std::ostream& os) {
PrintHeader(os, "WasmInternalFunction");
os << "\n - implicit arg: " << Brief(implicit_arg());
os << "\n - external: " << Brief(external());
os << "\n - function_index: " << function_index();
os << "\n - call target: [" << call_target().value() << "] -> "
<< AsHex::Address(wasm::GetProcessWideWasmCodePointerTable()
->GetEntrypointWithoutSignatureCheck(call_target()));
os << "\n - sig: " << sig() << " (" << sig()->parameter_count() << " params, "
<< sig()->return_count() << " returns)";
os << "\n";
}
void WasmFuncRef::WasmFuncRefPrint(std::ostream& os) {
PrintHeader(os, "WasmFuncRef");
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
os << "\n - internal: " << Brief(internal(isolate));
os << "\n";
}
void WasmNull::WasmNullPrint(std::ostream& os) {
PrintHeader(os, "WasmNull");
os << "\n";
}
void WasmCapiFunctionData::WasmCapiFunctionDataPrint(std::ostream& os) {
PrintHeader(os, "WasmCapiFunctionData");
WasmFunctionDataPrint(os);
os << "\n - embedder_data: " << Brief(embedder_data());
os << "\n";
}
void WasmExceptionPackage::WasmExceptionPackagePrint(std::ostream& os) {
PrintHeader(os, "WasmExceptionPackage");
os << "\n";
}
void WasmModuleObject::WasmModuleObjectPrint(std::ostream& os) {
PrintHeader(os, "WasmModuleObject");
os << "\n - module: " << native_module()->module();
os << "\n - native module: " << native_module().raw();
os << "\n - script: " << Brief(script());
os << "\n";
}
void WasmGlobalObject::WasmGlobalObjectPrint(std::ostream& os) {
PrintHeader(os, "WasmGlobalObject");
if (has_trusted_data()) {
os << "\n - trusted_data: " << trusted_data(GetCurrentIsolateForSandbox());
}
os << "\n - buffer: " << Brief(buffer());
os << "\n - offset: " << offset();
os << "\n - type: " << unsafe_type();
os << "\n";
}
void WasmValueObject::WasmValueObjectPrint(std::ostream& os) {
PrintHeader(os, "WasmValueObject");
os << "\n - value: " << Brief(value());
os << "\n";
}
#endif // V8_ENABLE_WEBASSEMBLY
void ModuleRequest::ModuleRequestPrint(std::ostream& os) {
PrintHeader(os, "ModuleRequest");
os << "\n - specifier: " << Brief(specifier());
os << "\n - import_attributes: " << Brief(import_attributes());
os << "\n - position: " << position();
os << "\n - phase: " << static_cast<int>(phase());
os << '\n';
}
void SourceTextModuleInfoEntry::SourceTextModuleInfoEntryPrint(
std::ostream& os) {
PrintHeader(os, "SourceTextModuleInfoEntry");
os << "\n - export_name: " << Brief(export_name());
os << "\n - local_name: " << Brief(local_name());
os << "\n - import_name: " << Brief(import_name());
os << "\n - module_request: " << module_request();
os << "\n - cell_index: " << cell_index();
os << "\n - beg_pos: " << beg_pos();
os << "\n - end_pos: " << end_pos();
os << '\n';
}
void FunctionTemplateRareData::FunctionTemplateRareDataPrint(std::ostream& os) {
PrintHeader(os, "FunctionTemplateRareData");
os << "\n - prototype_template: " << Brief(prototype_template());
os << "\n - prototype_provider_template: "
<< Brief(prototype_provider_template());
os << "\n - parent_template: " << Brief(parent_template());
os << "\n - named_property_handler: " << Brief(named_property_handler());
os << "\n - indexed_property_handler: " << Brief(indexed_property_handler());
os << "\n - instance_template: " << Brief(instance_template());
os << "\n - instance_call_handler: " << Brief(instance_call_handler());
os << "\n - access_check_info: " << Brief(access_check_info());
os << "\n - c_function_overloads: " << Brief(c_function_overloads());
os << '\n';
}
void PrototypeSharedClosureInfo::PrototypeSharedClosureInfoPrint(
std::ostream& os) {
PrintHeader(os, "PrototypeSharedClosureInfo");
os << "\n - boilerplate_description: " << Brief(boilerplate_description());
os << "\n - closure_feedback_cell_array: "
<< Brief(closure_feedback_cell_array());
os << "\n - context: " << Brief(context());
os << '\n';
}
void Tuple2::Tuple2Print(std::ostream& os) {
this->PrintHeader(os, "Tuple2");
os << "\n - value1: " << Brief(this->value1());
os << "\n - value2: " << Brief(this->value2());
os << '\n';
}
void AliasedArgumentsEntry::AliasedArgumentsEntryPrint(std::ostream& os) {
PrintHeader(os, "AliasedArgumentsEntry");
os << "\n - aliased_context_slot: " << aliased_context_slot();
os << '\n';
}
void AccessorPair::AccessorPairPrint(std::ostream& os) {
this->PrintHeader(os, "AccessorPair");
os << "\n - getter: " << Brief(this->getter());
os << "\n - setter: " << Brief(this->setter());
os << '\n';
}
void ClassPositions::ClassPositionsPrint(std::ostream& os) {
this->PrintHeader(os, "ClassPositions");
os << "\n - start: " << this->start();
os << "\n - end: " << this->end();
os << "\n";
}
void DebugInfo::DebugInfoPrint(std::ostream& os) {
PrintHeader(os, "DebugInfo");
os << "\n - shared: " << Brief(shared());
os << "\n - debugger_hints: " << debugger_hints();
os << "\n - break_points: " << Brief(break_points());
os << "\n - flags: " << flags(kRelaxedLoad);
os << "\n - coverage_info: " << Brief(coverage_info());
if (has_original_bytecode_array()) {
os << "\n - original_bytecode_array: " << Brief(original_bytecode_array());
}
if (has_debug_bytecode_array()) {
os << "\n - debug_bytecode_array: " << Brief(debug_bytecode_array());
}
os << "\n";
}
void BreakPointInfo::BreakPointInfoPrint(std::ostream& os) {
this->PrintHeader(os, "BreakPointInfo");
os << "\n - source_position: " << this->source_position();
os << "\n - break_points: " << Brief(this->break_points());
os << "\n";
}
void BreakPoint::BreakPointPrint(std::ostream& os) {
this->PrintHeader(os, "BreakPoint");
os << "\n - id: " << this->id();
os << "\n - condition: " << Brief(this->condition());
os << "\n";
}
void StackFrameInfo::StackFrameInfoPrint(std::ostream& os) {
this->PrintHeader(os, "StackFrameInfo");
os << "\n - shared_or_script: " << Brief(this->shared_or_script());
os << "\n - function_name: " << Brief(this->function_name());
os << "\n - flags: " << this->flags();
os << "\n - bytecode_offset_or_source_position: "
<< this->bytecode_offset_or_source_position();
os << "\n";
}
void StackTraceInfo::StackTraceInfoPrint(std::ostream& os) {
this->PrintHeader(os, "StackTraceInfo");
os << "\n - id: " << this->id();
os << "\n - frames: " << Brief(this->frames());
os << "\n";
}
void ErrorStackData::ErrorStackDataPrint(std::ostream& os) {
this->PrintHeader(os, "ErrorStackData");
os << "\n - raw_data_for_call_site_infos_or_formatted_stack: "
<< Brief(this->raw_data_for_call_site_infos_or_formatted_stack());
os << "\n - stack_trace: " << Brief(this->stack_trace());
os << "\n";
}
void DebugScriptScopeInfo::DebugScriptScopeInfoPrint(std::ostream& os) {
this->PrintHeader(os, "DebugScriptScopeInfo");
os << "\n - numeric_data: " << Brief(this->numeric_data());
os << "\n - string_table: " << Brief(this->string_table());
os << "\n";
}
void LoadHandler::LoadHandlerPrint(std::ostream& os) {
PrintHeader(os, "LoadHandler");
// TODO(ishell): implement printing based on handler kind
os << "\n - handler: " << Brief(smi_handler());
os << "\n - validity_cell: " << Brief(validity_cell());
int data_count = data_field_count();
for (int i = 0; i < data_count; ++i) {
os << "\n - data" << (i + 1) << ": " << Brief(data()[i].load());
}
os << "\n";
}
void StoreHandler::StoreHandlerPrint(std::ostream& os) {
PrintHeader(os, "StoreHandler");
// TODO(ishell): implement printing based on handler kind
os << "\n - handler: " << Brief(smi_handler());
os << "\n - validity_cell: " << Brief(validity_cell());
int data_count = data_field_count();
for (int i = 0; i < data_count; ++i) {
os << "\n - data" << (i + 1) << ": " << Brief(data()[i].load());
}
os << "\n";
}
void AllocationSite::AllocationSitePrint(std::ostream& os) {
PrintHeader(os, "AllocationSite");
if (this->HasWeakNext()) {
os << "\n - weak_next: "
<< Brief(Cast<AllocationSiteWithWeakNext>(this)->weak_next());
}
os << "\n - dependent code: " << Brief(dependent_code());
os << "\n - nested site: " << Brief(nested_site());
os << "\n - memento found count: "
<< Brief(Smi::FromInt(memento_found_count()));
os << "\n - memento create count: "
<< Brief(Smi::FromInt(memento_create_count()));
os << "\n - pretenure decision: "
<< Brief(Smi::FromInt(pretenure_decision()));
os << "\n - transition_info: ";
if (!PointsToLiteral()) {
ElementsKind kind = GetElementsKind();
os << "Array allocation with ElementsKind " << ElementsKindToString(kind);
} else if (IsJSArray(boilerplate())) {
os << "Array literal with boilerplate " << Brief(boilerplate());
} else {
os << "Object literal with boilerplate " << Brief(boilerplate());
}
os << "\n";
}
void AllocationMemento::AllocationMementoPrint(std::ostream& os) {
PrintHeader(os, "AllocationMemento");
os << "\n - allocation site: ";
GetAllocationSite()->AllocationSitePrint(os);
}
void ScriptOrModule::ScriptOrModulePrint(std::ostream& os) {
PrintHeader(os, "ScriptOrModule");
os << "\n - host_defined_options: " << Brief(host_defined_options());
os << "\n - resource_name: " << Brief(resource_name());
}
void CallSiteInfo::CallSiteInfoPrint(std::ostream& os) {
PrintHeader(os, "CallSiteInfo");
IsolateForSandbox isolate = GetCurrentIsolateForSandbox();
os << "\n - code_object: " << Brief(code_object(isolate));
os << "\n - receiver_or_instance: " << Brief(receiver_or_instance());
os << "\n - function: " << Brief(function());
os << "\n - code_offset_or_source_position: "
<< code_offset_or_source_position();
os << "\n - flags: " << flags();
os << "\n";
}
void Microtask::MicrotaskPrint(std::ostream& os) {
#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
os << "\n - continuation_preserved_embedder_data: "
<< Brief(continuation_preserved_embedder_data());
#endif
}
void CallbackTask::CallbackTaskPrint(std::ostream& os) {
PrintHeader(os, "CallbackTask");
MicrotaskPrint(os);
os << "\n - callback: " << Brief(callback());
os << "\n - data: " << Brief(data());
os << "\n";
}
void CallableTask::CallableTaskPrint(std::ostream& os) {
PrintHeader(os, "CallableTask");
MicrotaskPrint(os);
os << "\n - callable: " << Brief(callable());
os << "\n - context: " << Brief(context());
os << "\n";
}
void AsyncResumeTask::AsyncResumeTaskPrint(std::ostream& os) {
PrintHeader(os, "AsyncResumeTask");
MicrotaskPrint(os);
os << "\n - generator: " << Brief(generator());
os << "\n - value: " << Brief(value());
os << "\n - kind: " << kind();
os << "\n";
}
void PromiseReactionJobTask::PromiseReactionJobTaskPrint(std::ostream& os) {
MicrotaskPrint(os);
os << "\n - argument: " << Brief(argument());
os << "\n - context: " << Brief(context());
os << "\n - handler: " << Brief(handler());
os << "\n - promise_or_capability: " << Brief(promise_or_capability());
os << "\n";
}
void PromiseFulfillReactionJobTask::PromiseFulfillReactionJobTaskPrint(
std::ostream& os) {
PrintHeader(os, "PromiseFulfillReactionJobTask");
PromiseReactionJobTaskPrint(os);
}
void PromiseRejectReactionJobTask::PromiseRejectReactionJobTaskPrint(
std::ostream& os) {
PrintHeader(os, "PromiseRejectReactionJobTask");
PromiseReactionJobTaskPrint(os);
}
void PromiseResolveThenableJobTask::PromiseResolveThenableJobTaskPrint(
std::ostream& os) {
PrintHeader(os, "PromiseResolveThenableJobTask");
MicrotaskPrint(os);
os << "\n - context: " << Brief(context());
os << "\n - promise_to_resolve: " << Brief(promise_to_resolve());
os << "\n - thenable: " << Brief(thenable());
os << "\n - then: " << Brief(then());
os << "\n";
}
void PromiseCapability::PromiseCapabilityPrint(std::ostream& os) {
PrintHeader(os, "PromiseCapability");
os << "\n - promise: " << Brief(promise());
os << "\n - resolve: " << Brief(resolve());
os << "\n - reject: " << Brief(reject());
os << "\n";
}
void PromiseReaction::PromiseReactionPrint(std::ostream& os) {
PrintHeader(os, "PromiseReaction");
#ifdef V8_ENABLE_CONTINUATION_PRESERVED_EMBEDDER_DATA
os << "\n - continuation_preserved_embedder_data: "
<< Brief(continuation_preserved_embedder_data());
#endif
os << "\n - next: " << Brief(next());
os << "\n - reject_handler: " << Brief(reject_handler());
os << "\n - fulfill_handler: " << Brief(fulfill_handler());
os << "\n - promise_or_capability: " << Brief(promise_or_capability());
os << "\n";
}
void Script::ScriptPrint(std::ostream& os) {
PrintHeader(os, "Script");
os << "\n - source: " << Brief(source());
os << "\n - name: " << Brief(name());
os << "\n - line_offset: " << line_offset();
os << "\n - column_offset: " << column_offset();
os << "\n - context data: " << Brief(context_data());
os << "\n - type: " << static_cast<int>(type());
os << "\n - line ends: " << Brief(line_ends());
if (!has_line_ends()) os << " (not set)";
os << "\n - id: " << id();
os << "\n - source_url: " << Brief(source_url());
os << "\n - source_mapping_url: " << Brief(source_mapping_url());
os << "\n - host_defined_options: " << Brief(host_defined_options());
os << "\n - compilation kind: " << ToString(compilation_kind());
os << "\n - compiled lazy function positions: "
<< compiled_lazy_function_positions();
bool is_wasm = false;
#if V8_ENABLE_WEBASSEMBLY
if ((is_wasm = (type() == Type::kWasm))) {
if (has_wasm_breakpoint_infos()) {
os << "\n - wasm_breakpoint_infos: " << Brief(wasm_breakpoint_infos());
}
}
#endif // V8_ENABLE_WEBASSEMBLY
if (!is_wasm) {
if (has_eval_from_shared()) {
os << "\n - eval from shared: " << Brief(eval_from_shared());
} else if (is_wrapped()) {
os << "\n - wrapped arguments: " << Brief(wrapped_arguments());
}
os << "\n - eval from position: " << eval_from_position();
}
os << "\n - infos: " << Brief(infos());
os << "\n";
}
#ifdef V8_TEMPORAL_SUPPORT
void JSTemporalPlainDate::JSTemporalPlainDatePrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSTemporalPlainDate");
JSObjectPrintBody(os, this);
}
void JSTemporalPlainTime::JSTemporalPlainTimePrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSTemporalPlainTime");
JSObjectPrintBody(os, this);
}
void JSTemporalPlainDateTime::JSTemporalPlainDateTimePrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSTemporalPlainDateTime");
JSObjectPrintBody(os, this);
}
void JSTemporalZonedDateTime::JSTemporalZonedDateTimePrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSTemporalZonedDateTime");
JSObjectPrintBody(os, this);
}
void JSTemporalDuration::JSTemporalDurationPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSTemporalDuration");
JSObjectPrintBody(os, this);
}
void JSTemporalInstant::JSTemporalInstantPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSTemporalInstant");
JSObjectPrintBody(os, this);
}
void JSTemporalPlainYearMonth::JSTemporalPlainYearMonthPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSTemporalPlainYearMonth");
JSObjectPrintBody(os, this);
}
void JSTemporalPlainMonthDay::JSTemporalPlainMonthDayPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSTemporalPlainMonthDay");
JSObjectPrintBody(os, this);
}
#endif // V8_TEMPORAL_SUPPORT
void JSRawJson::JSRawJsonPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSRawJson");
JSObjectPrintBody(os, this);
}
#ifdef V8_INTL_SUPPORT
void JSV8BreakIterator::JSV8BreakIteratorPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSV8BreakIterator");
os << "\n - locale: " << Brief(locale());
os << "\n - icu break iterator with text: "
<< Brief(icu_iterator_with_text());
os << "\n - bound adopt text: " << Brief(bound_adopt_text());
os << "\n - bound first: " << Brief(bound_first());
os << "\n - bound next: " << Brief(bound_next());
os << "\n - bound current: " << Brief(bound_current());
os << "\n - bound break type: " << Brief(bound_break_type());
os << "\n";
}
void JSCollator::JSCollatorPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSCollator");
os << "\n - icu collator: " << Brief(icu_collator());
os << "\n - bound compare: " << Brief(bound_compare());
JSObjectPrintBody(os, this);
}
void JSDateTimeFormat::JSDateTimeFormatPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSDateTimeFormat");
os << "\n - locale: " << Brief(locale());
os << "\n - icu locale: " << Brief(icu_locale());
os << "\n - icu simple date format: " << Brief(icu_simple_date_format());
os << "\n - icu date interval format: " << Brief(icu_date_interval_format());
os << "\n - bound format: " << Brief(bound_format());
os << "\n - hour cycle: " << HourCycleAsString(Isolate::Current());
JSObjectPrintBody(os, this);
}
void JSDisplayNames::JSDisplayNamesPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSDisplayNames");
os << "\n - internal: " << Brief(internal());
os << "\n - style: " << StyleAsString(Isolate::Current());
os << "\n - fallback: " << FallbackAsString(Isolate::Current());
JSObjectPrintBody(os, this);
}
void JSDurationFormat::JSDurationFormatPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSDurationFormat");
os << "\n - style_flags: " << style_flags();
os << "\n - display_flags: " << display_flags();
os << "\n - icu locale: " << Brief(icu_locale());
os << "\n - icu number formatter: " << Brief(icu_number_formatter());
JSObjectPrintBody(os, this);
}
void JSListFormat::JSListFormatPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSListFormat");
os << "\n - locale: " << Brief(locale());
os << "\n - style: " << StyleAsString(Isolate::Current());
os << "\n - type: " << TypeAsString(Isolate::Current());
os << "\n - icu formatter: " << Brief(icu_formatter());
JSObjectPrintBody(os, this);
}
void JSLocale::JSLocalePrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSLocale");
os << "\n - icu locale: " << Brief(icu_locale());
JSObjectPrintBody(os, this);
}
void JSNumberFormat::JSNumberFormatPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSNumberFormat");
os << "\n - locale: " << Brief(locale());
os << "\n - icu_number_formatter: " << Brief(icu_number_formatter());
os << "\n - bound_format: " << Brief(bound_format());
JSObjectPrintBody(os, this);
}
void JSPluralRules::JSPluralRulesPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSPluralRules");
os << "\n - locale: " << Brief(locale());
os << "\n - type: " << TypeAsString(Isolate::Current());
os << "\n - icu plural rules: " << Brief(icu_plural_rules());
os << "\n - icu_number_formatter: " << Brief(icu_number_formatter());
JSObjectPrintBody(os, this);
}
void JSRelativeTimeFormat::JSRelativeTimeFormatPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSRelativeTimeFormat");
os << "\n - locale: " << Brief(locale());
os << "\n - numberingSystem: " << Brief(numberingSystem());
os << "\n - numeric: " << NumericAsString(Isolate::Current());
os << "\n - icu formatter: " << Brief(icu_formatter());
os << "\n";
}
void JSSegmentIterator::JSSegmentIteratorPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSSegmentIterator");
os << "\n - icu break iterator with text: "
<< Brief(icu_iterator_with_text());
os << "\n - granularity: " << GranularityAsString(Isolate::Current());
os << "\n";
}
void JSSegmenter::JSSegmenterPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSSegmenter");
os << "\n - locale: " << Brief(locale());
os << "\n - granularity: " << GranularityAsString(Isolate::Current());
os << "\n - icu break iterator: " << Brief(icu_break_iterator());
JSObjectPrintBody(os, this);
}
void JSSegments::JSSegmentsPrint(std::ostream& os) {
JSObjectPrintHeader(os, this, "JSSegments");
os << "\n - icu break iterator with text: "
<< Brief(icu_iterator_with_text());
os << "\n - granularity: " << GranularityAsString(Isolate::Current());
JSObjectPrintBody(os, this);
}
#endif // V8_INTL_SUPPORT
namespace {
void PrintScopeInfoList(Tagged<ScopeInfo> scope_info, std::ostream& os,
const char* list_name, int length) {
DisallowGarbageCollection no_gc;
if (length <= 0) return;
os << "\n - " << list_name;
os << " {\n";
for (auto it : ScopeInfo::IterateLocalNames(scope_info, no_gc)) {
os << " - " << it->index() << ": " << it->name();
if (scope_info->ContextLocalIsParameter(it->index())) {
os << " [parameter "
<< scope_info->ContextLocalParameterNumber(it->index()) << "]";
} else {
int pos = scope_info->ContextLocalInitializerPosition(it->index());
if (pos != kNoSourcePosition) {
os << " [position " << pos << "]";
}
}
os << "\n";
}
os << " }";
}
} // namespace
void ScopeInfo::ScopeInfoPrint(std::ostream& os) {
PrintHeader(os, "ScopeInfo");
if (this->IsEmpty()) {
os << "\n - empty\n";
return;
}
int flags = Flags();
os << "\n - parameters: " << ParameterCount();
os << "\n - context locals : " << ContextLocalCount();
if (HasInlinedLocalNames()) {
os << "\n - inlined local names";
} else {
os << "\n - local names in a hashtable: "
<< Brief(context_local_names_hashtable());
}
os << "\n - scope type: " << scope_type();
if (SloppyEvalCanExtendVars()) {
os << "\n - sloppy eval";
os << "\n - dependent code: " << Brief(dependent_code());
}
os << "\n - language mode: " << language_mode();
if (is_declaration_scope()) os << "\n - declaration scope";
if (HasReceiver()) {
os << "\n - receiver: " << ReceiverVariableBits::decode(flags);
}
if (ClassScopeHasPrivateBrand()) os << "\n - class scope has private brand";
if (HasSavedClassVariable()) os << "\n - has saved class variable";
if (CanOnlyAccessFixedFormalParameters()) {
os << "\n - can only access fixed formal parameters";
}
if (HasFunctionName()) {
os << "\n - function name(" << FunctionVariableBits::decode(flags) << "): ";
ShortPrint(FunctionName(), os);
}
if (HasSimpleParameters()) os << "\n - simple parameters";
if (PrivateNameLookupSkipsOuterClass()) {
os << "\n - private name lookup skips outer class";
}
os << "\n - function kind: " << function_kind();
if (HasOuterScopeInfo()) {
os << "\n - outer scope info: " << Brief(OuterScopeInfo());
}
if (HasFunctionName()) {
os << "\n - function name: " << Brief(FunctionName());
}
if (HasInferredFunctionName()) {
os << "\n - inferred function name: " << Brief(InferredFunctionName());
}
if (HasContextExtensionSlot()) {
os << "\n - has context extension slot";
}
os << "\n - start position: " << StartPosition();
os << "\n - end position: " << EndPosition();
os << "\n - length: " << length();
if (length() > 0) {
PrintScopeInfoList(this, os, "context slots", ContextLocalCount());
// TODO(neis): Print module stuff if present.
}
os << "\n";
}
void PreparseData::PreparseDataPrint(std::ostream& os) {
PrintHeader(os, "PreparseData");
os << "\n - data_length: " << data_length();
os << "\n - children_length: " << children_length();
if (data_length() > 0) {
os << "\n - data-start: " << reinterpret_cast<Address>(data());
}
if (children_length() > 0) {
os << "\n - children-start: " << reinterpret_cast<Address>(children());
}
for (int i = 0; i < children_length(); ++i) {
os << "\n - [" << i << "]: " << Brief(get_child(i));
}
os << "\n";
}
void UncompiledDataWithoutPreparseData::UncompiledDataWithoutPreparseDataPrint(
std::ostream& os) {
PrintHeader(os, "UncompiledDataWithoutPreparseData");
os << "\n - inferred_name: " << Brief(inferred_name());
os << "\n - start_position: " << start_position();
os << "\n - end_position: " << end_position();
os << '\n';
}
void UncompiledDataWithPreparseData::UncompiledDataWithPreparseDataPrint(
std::ostream& os) {
PrintHeader(os, "UncompiledDataWithPreparseData");
os << "\n - inferred_name: " << Brief(inferred_name());
os << "\n - start_position: " << start_position();
os << "\n - end_position: " << end_position();
os << "\n - preparse_data: " << Brief(preparse_data());
os << '\n';
}
void UncompiledDataWithoutPreparseDataWithJob::
UncompiledDataWithoutPreparseDataWithJobPrint(std::ostream& os) {
PrintHeader(os, "UncompiledDataWithoutPreparseDataWithJob");
os << "\n - inferred_name: " << Brief(inferred_name());
os << "\n - start_position: " << start_position();
os << "\n - end_position: " << end_position();
os << "\n - job: " << job();
os << '\n';
}
void UncompiledDataWithPreparseDataAndJob::
UncompiledDataWithPreparseDataAndJobPrint(std::ostream& os) {
PrintHeader(os, "UncompiledDataWithPreparseDataAndJob");
os << "\n - inferred_name: " << Brief(inferred_name());
os << "\n - start_position: " << start_position();
os << "\n - end_position: " << end_position();
os << "\n - preparse_data: " << Brief(preparse_data());
os << "\n - job: " << job();
os << '\n';
}
void HeapNumber::HeapNumberPrint(std::ostream& os) {
PrintHeader(os, "HeapNumber");
os << "\n - value: ";
HeapNumberShortPrint(os);
os << "\n";
}
void HashSeedWrapper::HashSeedWrapperPrint(std::ostream& os) {
PrintHeader(os, "HashSeedWrapper");
const HashSeed::Data& d = data();
os << "\n - seed: " << d.seed;
os << "\n - secret0: " << d.secrets[0];
os << "\n - secret1: " << d.secrets[1];
os << "\n - secret2: " << d.secrets[2];
#ifdef V8_ENABLE_SEEDED_ARRAY_INDEX_HASH
os << "\n - m1: " << d.m1;
os << "\n - m1_inv: " << d.m1_inv;
os << "\n - m2: " << d.m2;
os << "\n - m2_inv: " << d.m2_inv;
os << "\n - m3: " << d.m3;
os << "\n - m3_inv: " << d.m3_inv;
#endif // V8_ENABLE_SEEDED_ARRAY_INDEX_HASH
os << "\n";
}
#endif // OBJECT_PRINT
void HeapObject::Print() { Print(this); }
// static
void HeapObject::Print(Tagged<Object> obj) { v8::internal::Print(obj); }
// static
void HeapObject::Print(Tagged<Object> obj, std::ostream& os) {
v8::internal::Print(obj, os);
}
void HeapObject::HeapObjectShortPrint(std::ostream& os) {
os << AsHex::Address(this->ptr()) << " ";
#if V8_ENABLE_WEBASSEMBLY
if (IsWasmNull(Tagged<HeapObject>(this))) {
os << "WasmNull";
return;
}
#endif // V8_ENABLE_WEBASSEMBLY
if (Is<String>(this)) {
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
Cast<String>(this)->StringShortPrint(&accumulator);
os << accumulator.ToCString().get();
return;
}
if (Is<JSObject>(this)) {
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
Cast<JSObject>(this)->JSObjectShortPrint(&accumulator);
os << accumulator.ToCString().get();
return;
}
InstanceType instance_type = map()->instance_type();
// Skip invalid trusted objects. Technically it'd be fine to still handle
// them below since we only print the objects, but such an object will
// quickly lead to out-of-sandbox segfaults and so fuzzers will complain.
if (InstanceTypeChecker::IsTrustedObject(instance_type) &&
!OutsideSandboxOrInReadonlySpace(this)) {
os << "<Invalid TrustedObject (outside trusted space)>\n";
return;
}
switch (instance_type) {
case MAP_TYPE: {
Tagged<Map> map = Cast<Map>(this);
if (IsMetaMap(map)) {
// This is one of the meta maps, print only relevant fields.
os << "<MetaMap (" << Brief(map->native_context_or_null()) << ")>";
} else {
if (map->is_extended_map()) {
os << "<ExtendedMap";
} else {
os << "<Map";
}
if (map->instance_size() != kVariableSizeSentinel) {
os << "[" << map->instance_size() << "]";
}
os << "(";
if (IsJSObjectMap(map)) {
os << ElementsKindToString(map->elements_kind());
} else {
os << map->instance_type();
}
os << ")>";
}
} break;
case AWAIT_CONTEXT_TYPE: {
os << "<AwaitContext generator= ";
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
ShortPrint(Cast<Context>(this)->extension(), &accumulator);
os << accumulator.ToCString().get();
os << '>';
break;
}
case BLOCK_CONTEXT_TYPE:
os << "<BlockContext[" << Cast<Context>(this)->length() << "]>";
break;
case CATCH_CONTEXT_TYPE:
os << "<CatchContext[" << Cast<Context>(this)->length() << "]>";
break;
case DEBUG_EVALUATE_CONTEXT_TYPE:
os << "<DebugEvaluateContext[" << Cast<Context>(this)->length() << "]>";
break;
case EVAL_CONTEXT_TYPE:
os << "<EvalContext[" << Cast<Context>(this)->length() << "]>";
break;
case FUNCTION_CONTEXT_TYPE:
os << "<FunctionContext[" << Cast<Context>(this)->length() << "]>";
break;
case MODULE_CONTEXT_TYPE:
os << "<ModuleContext[" << Cast<Context>(this)->length() << "]>";
break;
case NATIVE_CONTEXT_TYPE:
os << "<NativeContext[" << Cast<Context>(this)->length() << "]>";
break;
case SCRIPT_CONTEXT_TYPE:
os << "<ScriptContext[" << Cast<Context>(this)->length() << "]>";
break;
case WITH_CONTEXT_TYPE:
os << "<WithContext[" << Cast<Context>(this)->length() << "]>";
break;
case SCRIPT_CONTEXT_TABLE_TYPE:
os << "<ScriptContextTable["
<< Cast<ScriptContextTable>(this)->capacity().value() << "]>";
break;
case HASH_TABLE_TYPE:
os << "<HashTable[" << Cast<FixedArray>(this)->length() << "]>";
break;
case ORDERED_HASH_MAP_TYPE:
os << "<OrderedHashMap[" << Cast<FixedArray>(this)->length() << "]>";
break;
case ORDERED_HASH_SET_TYPE:
os << "<OrderedHashSet[" << Cast<FixedArray>(this)->length() << "]>";
break;
case ORDERED_NAME_DICTIONARY_TYPE:
os << "<OrderedNameDictionary[" << Cast<FixedArray>(this)->length()
<< "]>";
break;
case NAME_DICTIONARY_TYPE:
os << "<NameDictionary[" << Cast<FixedArray>(this)->length() << "]>";
break;
case SWISS_NAME_DICTIONARY_TYPE:
os << "<SwissNameDictionary["
<< Cast<SwissNameDictionary>(this)->Capacity() << "]>";
break;
case GLOBAL_DICTIONARY_TYPE:
os << "<GlobalDictionary[" << Cast<FixedArray>(this)->length() << "]>";
break;
case NUMBER_DICTIONARY_TYPE:
os << "<NumberDictionary[" << Cast<FixedArray>(this)->length() << "]>";
break;
case SIMPLE_NUMBER_DICTIONARY_TYPE:
os << "<SimpleNumberDictionary[" << Cast<FixedArray>(this)->length()
<< "]>";
break;
case FIXED_ARRAY_TYPE:
os << "<FixedArray[" << Cast<FixedArray>(this)->length() << "]>";
break;
case OBJECT_BOILERPLATE_DESCRIPTION_TYPE:
os << "<ObjectBoilerplateDescription["
<< Cast<ObjectBoilerplateDescription>(this)->capacity() << "]>";
break;
case FIXED_DOUBLE_ARRAY_TYPE:
os << "<FixedDoubleArray[" << Cast<FixedDoubleArray>(this)->length()
<< "]>";
break;
case BYTE_ARRAY_TYPE:
os << "<ByteArray[" << Cast<ByteArray>(this)->length() << "]>";
break;
case BYTECODE_ARRAY_TYPE:
os << "<BytecodeArray[" << TrustedCast<BytecodeArray>(this)->length()
<< "]>";
break;
case DESCRIPTOR_ARRAY_TYPE:
os << "<DescriptorArray["
<< Cast<DescriptorArray>(this)->number_of_descriptors() << "]>";
break;
case WEAK_FIXED_ARRAY_TYPE:
os << "<WeakFixedArray[" << Cast<WeakFixedArray>(this)->length() << "]>";
break;
case TRUSTED_FIXED_ARRAY_TYPE:
os << "<TrustedFixedArray["
<< TrustedCast<TrustedFixedArray>(this)->length() << "]>";
break;
case TRUSTED_WEAK_FIXED_ARRAY_TYPE:
os << "<TrustedWeakFixedArray["
<< TrustedCast<TrustedWeakFixedArray>(this)->length() << "]>";
break;
case PROTECTED_FIXED_ARRAY_TYPE:
os << "<ProtectedFixedArray["
<< TrustedCast<ProtectedFixedArray>(this)->length() << "]>";
break;
case PROTECTED_WEAK_FIXED_ARRAY_TYPE:
os << "<ProtectedWeakFixedArray["
<< TrustedCast<ProtectedWeakFixedArray>(this)->length() << "]>";
break;
case TRANSITION_ARRAY_TYPE:
os << "<TransitionArray[" << Cast<TransitionArray>(this)->length()
<< "]>";
break;
case PROPERTY_ARRAY_TYPE:
os << "<PropertyArray[" << Cast<PropertyArray>(this)->length() << "]>";
break;
case CONTEXT_CELL_TYPE: {
auto cell = Cast<ContextCell>(this);
os << "<ContextCell[" << cell->state();
switch (cell->state()) {
case ContextCell::kConst:
case ContextCell::kSmi:
os << "=" << Brief(cell->tagged_value());
break;
case ContextCell::kInt32:
os << "=" << cell->int32_value();
break;
case ContextCell::kFloat64:
os << "=" << cell->float64_value();
break;
case ContextCell::kDetached:
break;
}
os << "]>";
break;
}
case FEEDBACK_CELL_TYPE: {
{
ReadOnlyRoots roots = GetReadOnlyRoots();
os << "<FeedbackCell[";
if (map() == roots.no_closures_cell_map()) {
os << "no feedback";
} else if (map() == roots.one_closure_cell_map()) {
os << "one closure";
} else if (map() == roots.many_closures_cell_map()) {
os << "many closures";
} else {
os << "!!!INVALID MAP!!!";
}
os << "]>";
}
break;
}
case CLOSURE_FEEDBACK_CELL_ARRAY_TYPE:
os << "<ClosureFeedbackCellArray["
<< Cast<ClosureFeedbackCellArray>(this)->length() << "]>";
break;
case FEEDBACK_VECTOR_TYPE:
os << "<FeedbackVector[" << Cast<FeedbackVector>(this)->length() << "]>";
break;
case FREE_SPACE_TYPE:
os << "<FreeSpace[" << Cast<FreeSpace>(this)->size(kRelaxedLoad) << "]>";
break;
case PREPARSE_DATA_TYPE: {
Tagged<PreparseData> data = Cast<PreparseData>(this);
os << "<PreparseData[data=" << data->data_length()
<< " children=" << data->children_length() << "]>";
break;
}
case UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE: {
Tagged<UncompiledDataWithoutPreparseData> data =
TrustedCast<UncompiledDataWithoutPreparseData>(this);
os << "<UncompiledDataWithoutPreparseData (" << data->start_position()
<< ", " << data->end_position() << ")]>";
break;
}
case UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE: {
Tagged<UncompiledDataWithPreparseData> data =
TrustedCast<UncompiledDataWithPreparseData>(this);
os << "<UncompiledDataWithPreparseData (" << data->start_position()
<< ", " << data->end_position()
<< ") preparsed=" << Brief(data->preparse_data()) << ">";
break;
}
case SHARED_FUNCTION_INFO_TYPE: {
Tagged<SharedFunctionInfo> shared = Cast<SharedFunctionInfo>(this);
std::unique_ptr<char[]> debug_name = shared->DebugNameCStr();
if (debug_name[0] != '\0') {
os << "<SharedFunctionInfo " << debug_name.get() << ">";
} else {
os << "<SharedFunctionInfo>";
}
break;
}
case JS_MESSAGE_OBJECT_TYPE:
os << "<JSMessageObject>";
break;
#define MAKE_STRUCT_CASE(TYPE, Name, name) \
case TYPE: \
os << "<" #Name; \
Cast<Name>(this)->BriefPrintDetails(os); \
os << ">"; \
break;
STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE
case ALLOCATION_SITE_TYPE: {
os << "<AllocationSite>";
break;
}
case SCOPE_INFO_TYPE: {
Tagged<ScopeInfo> scope = Cast<ScopeInfo>(this);
os << "<ScopeInfo";
if (!scope->IsEmpty()) os << " " << scope->scope_type();
os << ">";
break;
}
case CODE_TYPE: {
Tagged<Code> code = TrustedCast<Code>(this);
os << "<Code " << CodeKindToString(code->kind());
if (code->is_builtin()) {
os << " " << Builtins::name(code->builtin_id());
}
os << ">";
break;
}
case HOLE_TYPE: {
#define PRINT_HOLE(Type, Value, _) \
if (Is##Type(this)) { \
os << "<" #Value ">"; \
break; \
}
HOLE_LIST(PRINT_HOLE)
#undef PRINT_HOLE
UNREACHABLE();
}
case INSTRUCTION_STREAM_TYPE: {
Tagged<InstructionStream> istream = TrustedCast<InstructionStream>(this);
Tagged<Code> code = istream->code(kAcquireLoad);
os << "<InstructionStream " << CodeKindToString(code->kind());
if (code->is_builtin()) {
os << " " << Builtins::name(code->builtin_id());
}
os << ">";
break;
}
case ODDBALL_TYPE: {
if (Is<Undefined>(this)) {
os << "<undefined>";
} else if (Is<Null>(this)) {
os << "<null>";
} else if (Is<True>(this)) {
os << "<true>";
} else if (Is<False>(this)) {
os << "<false>";
} else {
os << "<Odd Oddball: ";
os << Cast<Oddball>(this)->to_string()->ToCString().get();
os << ">";
}
break;
}
case SYMBOL_TYPE: {
Tagged<Symbol> symbol = Cast<Symbol>(this);
symbol->SymbolShortPrint(os);
break;
}
case HEAP_NUMBER_TYPE: {
os << "<HeapNumber ";
Cast<HeapNumber>(this)->HeapNumberShortPrint(os);
os << ">";
break;
}
case BIGINT_TYPE: {
os << "<BigInt ";
Cast<BigInt>(this)->BigIntBaseShortPrint(os);
os << ">";
break;
}
case JS_PROXY_TYPE:
os << "<JSProxy>";
break;
case FOREIGN_TYPE:
os << "<Foreign>";
break;
case CELL_TYPE: {
os << "<Cell value= ";
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
ShortPrint(Cast<Cell>(this)->maybe_value(), &accumulator);
os << accumulator.ToCString().get();
os << '>';
break;
}
case PROPERTY_CELL_TYPE: {
Tagged<PropertyCell> cell = Cast<PropertyCell>(this);
os << "<PropertyCell name=";
ShortPrint(cell->name(), os);
os << " value=";
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
ShortPrint(cell->value(kAcquireLoad), &accumulator);
os << accumulator.ToCString().get();
os << '>';
break;
}
case ACCESSOR_INFO_TYPE: {
Tagged<AccessorInfo> info = Cast<AccessorInfo>(this);
os << "<AccessorInfo ";
os << "name= " << Brief(info->name());
os << ", data= " << Brief(info->data());
os << ">";
break;
}
case FUNCTION_TEMPLATE_INFO_TYPE: {
Tagged<FunctionTemplateInfo> info = Cast<FunctionTemplateInfo>(this);
os << "<FunctionTemplateInfo ";
Isolate* isolate;
if (GetIsolateFromHeapObject(this, &isolate)) {
os << "callback= " << reinterpret_cast<void*>(info->callback(isolate));
} else {
os << "callback= " << kUnavailableString;
}
os << ", data= " << Brief(info->callback_data(kAcquireLoad));
os << ", has_side_effects= ";
if (info->has_side_effects()) {
os << "true>";
} else {
os << "false>";
}
break;
}
#if V8_ENABLE_WEBASSEMBLY
case WASM_DISPATCH_TABLE_TYPE:
os << "<WasmDispatchTable["
<< TrustedCast<WasmDispatchTable>(this)->length() << "]>";
break;
case WASM_DISPATCH_TABLE_FOR_IMPORTS_TYPE:
os << "<WasmDispatchTableForImports["
<< TrustedCast<WasmDispatchTableForImports>(this)->length() << "]>";
break;
#endif // V8_ENABLE_WEBASSEMBLY
default:
os << "<Other heap object (" << map()->instance_type() << ")>";
break;
}
}
void HeapNumber::HeapNumberShortPrint(std::ostream& os) {
PrintDouble(os, value());
}
// TODO(cbruni): remove once the new maptracer is in place.
void Name::NameShortPrint() {
if (Is<String>(this)) {
PrintF("%s", Cast<String>(this)->ToCString().get());
} else {
DCHECK(Is<Symbol>(this));
Tagged<Symbol> s = Cast<Symbol>(this);
if (IsUndefined(s->description())) {
PrintF("#<%s>", s->PrivateSymbolToName());
} else {
PrintF("<%s>", Cast<String>(s->description())->ToCString().get());
}
}
}
// TODO(cbruni): remove once the new maptracer is in place.
int Name::NameShortPrint(base::Vector<char> str) {
if (Is<String>(this)) {
return SNPrintF(str, "%s", Cast<String>(this)->ToCString().get());
} else {
DCHECK(Is<Symbol>(this));
Tagged<Symbol> s = Cast<Symbol>(this);
if (IsUndefined(s->description())) {
return SNPrintF(str, "#<%s>", s->PrivateSymbolToName());
} else {
return SNPrintF(str, "<%s>",
Cast<String>(s->description())->ToCString().get());
}
}
}
void Symbol::SymbolShortPrint(std::ostream& os) {
os << "<Symbol:";
if (!IsUndefined(description())) {
os << " ";
Tagged<String> description_as_string = Cast<String>(description());
description_as_string->PrintUC16(os, 0, description_as_string->length());
} else {
os << " (" << PrivateSymbolToName() << ")";
}
os << ">";
}
void Map::PrintMapDetails(std::ostream& os) {
DisallowGarbageCollection no_gc;
this->MapPrint(os);
instance_descriptors()->PrintDescriptors(os);
}
void Map::MapPrint(std::ostream& os) {
bool is_meta_map = IsMetaMap(this);
bool is_extended_map = Is<ExtendedMap>(this);
#if V8_ENABLE_WEBASSEMBLY
bool is_wasm_map = IsWasmObjectMap(this);
#else
constexpr bool is_wasm_map = false;
#endif // V8_ENABLE_WEBASSEMBLY
#ifdef OBJECT_PRINT
PrintHeader(os, is_meta_map ? "MetaMap"
: is_extended_map ? "ExtendedMap"
: "Map");
#else
os << (is_meta_map ? "MetaMap="
: is_extended_map ? "ExtendedMap="
: "Map=")
<< reinterpret_cast<void*>(ptr());
#endif
os << "\n - type: " << instance_type();
if (is_meta_map) {
// This is one of the meta maps, print only relevant fields.
os << "\n - native_context: " << Brief(native_context_or_null());
os << "\n - meta map size: " << AllocatedSize();
os << "\n";
return;
}
os << "\n - instance size: ";
if (instance_size() == kVariableSizeSentinel) {
os << "variable";
} else {
os << instance_size();
}
if (IsJSObjectMap(this)) {
os << "\n - inobject properties: " << GetInObjectProperties();
os << "\n - unused property fields: " << UnusedPropertyFields();
}
os << "\n - elements kind: " << ElementsKindToString(elements_kind());
os << "\n - enum length: ";
if (EnumLength() == kInvalidEnumCacheSentinel) {
os << "invalid";
} else {
os << EnumLength();
}
if (is_deprecated()) os << "\n - deprecated_map";
if (is_stable()) os << "\n - stable_map";
if (is_migration_target()) os << "\n - migration_target";
if (is_dictionary_map()) os << "\n - dictionary_map";
if (has_named_interceptor()) os << "\n - named_interceptor";
if (has_indexed_interceptor()) os << "\n - indexed_interceptor";
if (may_have_interesting_properties()) {
os << "\n - may_have_interesting_properties";
}
if (is_undetectable()) os << "\n - undetectable";
if (is_callable()) os << "\n - callable";
if (is_constructor()) os << "\n - constructor";
if (is_access_check_needed()) os << "\n - access_check_needed";
if (!is_extensible()) os << "\n - non-extensible";
if (IsContextMap(this)) {
os << "\n - native context: " << Brief(native_context());
} else if (is_prototype_map()) {
os << "\n - prototype_map";
os << "\n - prototype info: " << Brief(prototype_info());
#if V8_ENABLE_WEBASSEMBLY
} else if (is_wasm_map) {
os << "\n - wasm_type_info: " << Brief(wasm_type_info());
Tagged<Object> proto_info = prototype_info();
if (!IsSmi(proto_info)) os << "\n - prototype info: " << Brief(proto_info);
#endif // V8_ENABLE_WEBASSEMBLY
} else {
os << "\n - back pointer: " << Brief(GetBackPointer());
}
os << "\n - prototype_validity_cell: "
<< Brief(prototype_validity_cell(kRelaxedLoad));
if (!is_wasm_map) {
os << "\n - instance descriptors " << (owns_descriptors() ? "(own) " : "")
<< "#" << NumberOfOwnDescriptors() << ": "
<< Brief(instance_descriptors());
}
// Read-only maps can't have transitions, which is fortunate because we need
// the isolate to iterate over the transitions.
if (!HeapLayout::InReadOnlySpace(this)) {
Isolate* isolate = HeapLayout::InWritableSharedSpace(this)
? Isolate::Current()->shared_space_isolate()
: Isolate::Current();
TransitionsAccessor transitions(isolate, this);
int nof_transitions = transitions.NumberOfTransitions();
if (nof_transitions > 0 || transitions.HasPrototypeTransitions() ||
transitions.HasSideStepTransitions()) {
os << "\n - transitions #" << nof_transitions << ": ";
Tagged<HeapObject> heap_object;
Tagged<Smi> smi;
if (raw_transitions().ToSmi(&smi)) {
os << Brief(smi);
} else if (raw_transitions().GetHeapObject(&heap_object)) {
os << Brief(heap_object);
}
#ifdef OBJECT_PRINT
transitions.PrintTransitions(os);
#endif // OBJECT_PRINT
}
}
os << "\n - prototype: " << Brief(prototype());
if (!IsContextMap(this)) {
os << "\n - constructor: " << Brief(GetConstructor());
}
os << "\n - dependent code: " << Brief(dependent_code());
os << "\n - construction counter: " << construction_counter();
if (is_extended_map) {
Tagged<ExtendedMap> map_ex = UncheckedCast<ExtendedMap>(this);
os << "\n -------";
os << "\n - extended map kind: " << map_ex->map_kind();
os << "\n - extended map size: " << map_ex->map_size();
switch (map_ex->map_kind()) {
case ExtendedMapKind::kJSInterceptorMap: {
Tagged<JSInterceptorMap> js_interceptor_map =
UncheckedCast<JSInterceptorMap>(map_ex);
if (js_interceptor_map->supports_fast_iterable_to_list()) {
os << "\n - supports_fast_iterable_to_list";
}
os << "\n - named_interceptor: "
<< Brief(js_interceptor_map->named_interceptor());
os << "\n - indexed_interceptor: "
<< Brief(js_interceptor_map->indexed_interceptor());
os << "\n - fast_case_validity_cell: "
<< Brief(js_interceptor_map->fast_case_validity_cell());
break;
}
}
}
os << "\n";
}
void DescriptorArray::PrintDescriptors(std::ostream& os) {
for (InternalIndex i : InternalIndex::Range(number_of_descriptors())) {
Tagged<Name> key = GetKey(i);
os << "\n [" << i.as_int() << "]: ";
#ifdef OBJECT_PRINT
key->NamePrint(os);
#else
ShortPrint(key, os);
#endif
os << " ";
PrintDescriptorDetails(os, i, PropertyDetails::kPrintFull);
}
os << "\n";
}
void DescriptorArray::PrintDescriptorDetails(std::ostream& os,
InternalIndex descriptor,
PropertyDetails::PrintMode mode) {
PropertyDetails details = GetDetails(descriptor);
details.PrintAsFastTo(os, mode);
os << " @ ";
switch (details.location()) {
case PropertyLocation::kField: {
Tagged<FieldType> field_type = GetFieldType(descriptor);
FieldType::PrintTo(field_type, os);
break;
}
case PropertyLocation::kDescriptor:
Tagged<Object> value = GetStrongValue(descriptor);
os << Brief(value);
if (IsAccessorPair(value)) {
Tagged<AccessorPair> pair = Cast<AccessorPair>(value);
os << "(get: " << Brief(pair->getter())
<< ", set: " << Brief(pair->setter()) << ")";
}
break;
}
}
#if defined(DEBUG) || defined(OBJECT_PRINT)
// This method is only meant to be called from gdb for debugging purposes.
// Since the string can also be in two-byte encoding, non-Latin1 characters
// will be ignored in the output.
char* String::ToAsciiArray() {
// Static so that subsequent calls frees previously allocated space.
// This also means that previous results will be overwritten.
static char* buffer = nullptr;
if (buffer != nullptr) delete[] buffer;
buffer = new char[length() + 1];
WriteToFlat(this, reinterpret_cast<uint8_t*>(buffer), 0, length());
buffer[length()] = 0;
return buffer;
}
// static
void TransitionsAccessor::PrintOneTransition(std::ostream& os, Tagged<Name> key,
Tagged<Map> target) {
os << "\n ";
#ifdef OBJECT_PRINT
key->NamePrint(os);
#else
ShortPrint(key, os);
#endif
os << ": ";
ReadOnlyRoots roots = GetReadOnlyRoots();
if (key == roots.nonextensible_symbol()) {
os << "(transition to non-extensible)";
} else if (key == roots.sealed_symbol()) {
os << "(transition to sealed)";
} else if (key == roots.frozen_symbol()) {
os << "(transition to frozen)";
} else if (key == roots.elements_transition_symbol()) {
os << "(transition to " << ElementsKindToString(target->elements_kind())
<< ")";
} else if (key == roots.strict_function_transition_symbol()) {
os << " (transition to strict function)";
} else if (key == roots.detached_symbol()) {
os << " (transition to detached array buffer view)";
} else {
DCHECK(!IsSpecialTransition(roots, key));
os << "(transition to ";
InternalIndex descriptor = target->LastAdded();
Tagged<DescriptorArray> descriptors = target->instance_descriptors();
descriptors->PrintDescriptorDetails(os, descriptor,
PropertyDetails::kForTransitions);
os << ")";
}
os << " -> " << Brief(target);
}
void TransitionArray::PrintInternal(std::ostream& os) {
Isolate* isolate = Isolate::Current();
{
int num_transitions = number_of_transitions();
os << "\n Transitions #" << num_transitions << ":";
for (int i = 0; i < num_transitions; i++) {
Tagged<Name> key = GetKey(i);
Tagged<Map> target;
GetTargetIfExists(i, isolate, &target);
TransitionsAccessor::PrintOneTransition(os, key, target);
}
}
if (HasPrototypeTransitions()) {
auto prototype_transitions = GetPrototypeTransitions();
const uint32_t num_transitions =
NumberOfPrototypeTransitions(prototype_transitions);
os << "\n Prototype transitions #" << num_transitions << ": "
<< Brief(prototype_transitions);
for (uint32_t i = 0; i < num_transitions; i++) {
auto maybe = prototype_transitions->get(
TransitionArray::kProtoTransitionHeaderSize + i);
Tagged<HeapObject> target;
if (maybe.GetHeapObjectIfWeak(&target)) {
auto map = Cast<Map>(target);
os << "\n " << Brief(map->prototype()) << " -> "
<< Brief(Cast<Map>(target));
}
}
}
if (HasSideStepTransitions()) {
auto sidestep_transitions = GetSideStepTransitions();
uint32_t num_transitions = sidestep_transitions->ulength().value();
os << "\n Sidestep transitions #" << num_transitions << ": "
<< Brief(sidestep_transitions);
for (uint32_t i = 0; i < num_transitions; i++) {
SideStepTransition::Kind kind = static_cast<SideStepTransition::Kind>(i);
auto maybe_target = sidestep_transitions->get(i);
os << "\n " << kind << " -> " << Brief(maybe_target);
}
}
}
void TransitionsAccessor::PrintTransitions(std::ostream& os) {
switch (encoding()) {
case kPrototypeInfo:
case kPrototypeSharedClosureInfo:
case kUninitialized:
case kMigrationTarget:
return;
case kWeakRef: {
Tagged<Map> target =
Cast<Map>(raw_transitions_.GetHeapObjectAssumeWeak());
Tagged<Name> key = GetSimpleTransitionKey(target);
PrintOneTransition(os, key, target);
break;
}
case kFullTransitionArray:
return transitions()->PrintInternal(os);
}
}
void TransitionsAccessor::PrintTransitionTree() {
StdoutStream os;
os << (IsUndefined(map_->GetBackPointer()) ? "root_" : "")
<< "map= " << Brief(map_);
DisallowGarbageCollection no_gc;
PrintTransitionTree(os, 0, &no_gc);
os << "\n" << std::flush;
}
void TransitionsAccessor::PrintTransitionTree(
std::ostream& os, int level, DisallowGarbageCollection* no_gc) {
ReadOnlyRoots roots = ReadOnlyRoots(isolate_);
int pos = 0;
int proto_pos = 0;
ForEachTransitionWithKey(
no_gc,
[&](Tagged<Name> key, Tagged<Map> target) {
os << std::endl
<< " " << level << "/" << pos << ":" << std::setw(level * 2 + 2)
<< " ";
pos++;
std::stringstream ss;
ss << Brief(target);
os << std::left << std::setw(50) << ss.str() << ": ";
if (key == roots.nonextensible_symbol()) {
os << "to non-extensible";
} else if (key == roots.sealed_symbol()) {
os << "to sealed ";
} else if (key == roots.frozen_symbol()) {
os << "to frozen";
} else if (key == roots.elements_transition_symbol()) {
os << "to " << ElementsKindToString(target->elements_kind());
} else if (key == roots.strict_function_transition_symbol()) {
os << "to strict function";
} else if (key == roots.detached_symbol()) {
os << "to detached array buffer view";
} else {
#ifdef OBJECT_PRINT
key->NamePrint(os);
#else
ShortPrint(key, os);
#endif
os << " ";
DCHECK(!IsSpecialTransition(ReadOnlyRoots(isolate_), key));
os << "to ";
InternalIndex descriptor = target->LastAdded();
Tagged<DescriptorArray> descriptors = target->instance_descriptors();
descriptors->PrintDescriptorDetails(os, descriptor,
PropertyDetails::kForTransitions);
}
TransitionsAccessor transitions(isolate_, target);
transitions.PrintTransitionTree(os, level + 1, no_gc);
},
[&](Tagged<Map> target) {
os << std::endl
<< " " << level << "/p" << proto_pos << ":"
<< std::setw(level * 2 + 2) << " ";
proto_pos++;
std::stringstream ss;
ss << Brief(target);
os << std::left << std::setw(50) << ss.str() << ": to proto ";
ShortPrint(target->prototype(), os);
TransitionsAccessor transitions(isolate_, target);
transitions.PrintTransitionTree(os, level + 1, no_gc);
},
[&](SideStepTransition::Kind kind, Tagged<Object> side_step) {
os << std::endl
<< " " << level << "/s:" << std::setw(level * 2 + 2) << " ";
std::stringstream ss;
ss << Brief(side_step);
os << std::left << std::setw(50) << ss.str() << ": sidestep " << kind;
});
}
void JSObject::PrintTransitions(std::ostream& os) {
TransitionsAccessor ta(Isolate::Current(), map());
if (ta.NumberOfTransitions() != 0 || ta.HasPrototypeTransitions()) {
os << "\n - transitions";
ta.PrintTransitions(os);
}
}
#endif // defined(DEBUG) || defined(OBJECT_PRINT)
} // namespace v8::internal
namespace {
inline i::Tagged<i::Object> GetObjectFromRaw(void* object) {
i::Address object_ptr = reinterpret_cast<i::Address>(object);
#ifdef V8_COMPRESS_POINTERS
if (RoundDown<i::kPtrComprCageBaseAlignment>(object_ptr) == i::kNullAddress) {
// Try to decompress pointer.
i::Isolate* isolate = i::Isolate::TryGetCurrent();
if (isolate != nullptr) {
object_ptr = i::V8HeapCompressionScheme::DecompressTagged(
static_cast<i::Tagged_t>(object_ptr));
} else {
object_ptr = i::V8HeapCompressionScheme::DecompressTagged(
static_cast<i::Tagged_t>(object_ptr));
}
}
#endif
return i::Tagged<i::Object>(object_ptr);
}
} // namespace
//
// The following functions are used by our gdb macros.
//
V8_DEBUGGING_EXPORT extern i::Tagged<i::Object> _v8_internal_Get_Object(
void* object) {
return GetObjectFromRaw(object);
}
// Defines _As_XXX functions which are useful for inspecting object properties
// in debugger:
//
// (gdb) p _As_ScopeInfo(0xead)->IsEmpty()
// $1 = 0x1
// (gdb) p _As_ScopeInfo(0x0e830009d555)->scope_type()
// $2 = v8::internal::FUNCTION_SCOPE
// (gdb) p _As_Undefined(0x11)->kind()
// $3 = 0x4
// (gdb) job _As_Oddball(0x2d)->to_string()
// 0xe8300005309: [String] in ReadOnlySpace: #null
//
#define AS_HELPER_DEF(Type, ...) \
V8_DEBUGGING_EXPORT auto& _As_##Type(i::Address ptr) { \
i::Tagged<i::Type> tagged = UncheckedCast<i::Type>( \
GetObjectFromRaw(reinterpret_cast<void*>(ptr))); \
/* _As_XXX(..) functions provide storage for TaggedOperatorArrowRef<T> */ \
/* temporary object and return a reference as a measure against gdb */ \
/* error "Attempt to take address of value not located in memory." */ \
static auto result = tagged.operator->(); /* There's no default ctor. */ \
result = tagged.operator->(); \
return result; \
} \
V8_DEBUGGING_EXPORT auto& _As_##Type(i::Tagged<i::HeapObject>& obj) { \
return _As_##Type(obj.ptr()); \
}
HEAP_OBJECT_ORDINARY_TYPE_LIST(AS_HELPER_DEF)
HEAP_OBJECT_TRUSTED_TYPE_LIST(AS_HELPER_DEF)
ODDBALL_LIST(AS_HELPER_DEF)
#undef AS_HELPER_DEF
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_Object(void* object) {
i::AllowHandleDereference allow_deref;
i::AllowHandleUsageOnAllThreads allow_deref_all_threads;
i::Print(GetObjectFromRaw(object));
}
// Used by lldb_visualizers.py to create a representation of a V8 object.
V8_DEBUGGING_EXPORT extern std::string _v8_internal_Print_Object_To_String(
void* object) {
std::stringstream strm;
i::Print(GetObjectFromRaw(object), strm);
return strm.str();
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_LoadHandler(
void* object) {
#ifdef OBJECT_PRINT
i::StdoutStream os;
i::LoadHandler::PrintHandler(GetObjectFromRaw(object), os);
os << std::endl << std::flush;
#endif
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_StoreHandler(
void* object) {
#ifdef OBJECT_PRINT
i::StdoutStream os;
i::StoreHandler::PrintHandler(GetObjectFromRaw(object), os);
os << std::flush;
#endif
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_Code(void* object) {
i::Address address = reinterpret_cast<i::Address>(object);
i::Isolate* isolate = i::Isolate::Current();
#if V8_ENABLE_WEBASSEMBLY
{
if (auto* wasm_code =
i::wasm::GetWasmCodeManager()->LookupCode(isolate, address)) {
i::StdoutStream os;
wasm_code->Disassemble(nullptr, os, address);
return;
}
}
#endif // V8_ENABLE_WEBASSEMBLY
std::optional<i::Tagged<i::Code>> lookup_result =
isolate->heap()->TryFindCodeForInnerPointerForPrinting(address);
if (!lookup_result.has_value()) {
i::PrintF(
"%p is not within the current isolate's code or embedded spaces\n",
object);
return;
}
#if defined(OBJECT_PRINT)
i::StdoutStream os;
lookup_result.value()->CodePrint(os, nullptr, address);
#elif defined(ENABLE_DISASSEMBLER)
i::StdoutStream os;
lookup_result.value()->Disassemble(nullptr, os, isolate, address);
#else
i::Print(lookup_result.value());
#endif
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_Dispatch_Handle(
uint32_t handle) {
i::Isolate::Current()->js_dispatch_table().PrintEntry(
i::JSDispatchHandle(handle));
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_OnlyCode(
void* object, size_t range_limit) {
i::Address address = reinterpret_cast<i::Address>(object);
i::Isolate* isolate = i::Isolate::Current();
#if V8_ENABLE_WEBASSEMBLY
{
if (i::wasm::GetWasmCodeManager()->LookupCode(isolate, address)) {
i::PrintF("Not supported on wasm code");
return;
}
}
#endif // V8_ENABLE_WEBASSEMBLY
std::optional<i::Tagged<i::Code>> lookup_result =
isolate->heap()->TryFindCodeForInnerPointerForPrinting(address);
if (!lookup_result.has_value()) {
i::PrintF(
"%p is not within the current isolate's code or embedded spaces\n",
object);
return;
}
#if defined(ENABLE_DISASSEMBLER)
i::StdoutStream os;
lookup_result.value()->DisassembleOnlyCode(nullptr, os, isolate, address,
range_limit);
#endif
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_StackTrace() {
i::Isolate* isolate = i::Isolate::Current();
isolate->PrintStack(stdout);
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_StackTraceConcise() {
i::Isolate* isolate = i::Isolate::Current();
isolate->PrintStack(stdout, i::Isolate::PrintStackMode::kPrintStackConcise);
}
namespace _v8_internal_debugonly {
// This class is easy to navigate in a GUI debugger and not intended for
// use elsewhere.
struct StackTraceDebugDetails {
i::StackFrame::Type type;
std::string summary;
std::vector<i::Tagged<i::SharedFunctionInfo>> functions;
std::vector<i::Tagged<i::Object>> expressions;
};
} // namespace _v8_internal_debugonly
// Used by lldb_visualizers.py to create a representation of the V8 stack.
V8_DEBUGGING_EXPORT extern std::vector<
_v8_internal_debugonly::StackTraceDebugDetails>
_v8_internal_Expand_StackTrace(i::Isolate* isolate) {
std::vector<_v8_internal_debugonly::StackTraceDebugDetails> stack;
i::DisallowGarbageCollection no_gc;
int frame_index = 0;
for (i::StackFrameIterator it(isolate); !it.done(); it.Advance()) {
i::CommonFrame* frame = i::CommonFrame::cast(it.frame());
_v8_internal_debugonly::StackTraceDebugDetails details;
details.type = frame->type();
if (frame->is_javascript()) {
i::JavaScriptFrame::cast(frame)->GetFunctions(&details.functions);
if (!frame->is_optimized_js()) {
int exprcount = frame->ComputeExpressionsCount();
for (int i = 0; i < exprcount; i++) {
details.expressions.push_back(frame->GetExpression(i));
}
}
}
i::HandleScope scope(isolate);
i::StringStream::ClearMentionedObjectCache(isolate);
i::HeapStringAllocator allocator;
i::StringStream accumulator(&allocator);
frame->Print(&accumulator, i::StackFrame::OVERVIEW, frame_index++);
std::unique_ptr<char[]> overview = accumulator.ToCString();
details.summary = overview.get();
stack.push_back(std::move(details));
}
return stack;
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_TransitionTree(
void* object, bool start_at_root = false) {
i::Tagged<i::Object> o(GetObjectFromRaw(object));
if (!IsMap(o)) {
printf("Please provide a valid Map\n");
} else {
#if defined(DEBUG) || defined(OBJECT_PRINT)
i::Tagged<i::Map> map = i::UncheckedCast<i::Map>(o);
i::TransitionsAccessor transitions(
i::Isolate::Current(), start_at_root ? map->FindRootMap() : map);
transitions.PrintTransitionTree();
#endif
}
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_Object_MarkBit(
void* object) {
#ifdef OBJECT_PRINT
const auto mark_bit = v8::internal::MarkBit::From(
i::Isolate::Current(), reinterpret_cast<i::Address>(object));
i::StdoutStream os;
os << "Object " << object << " is "
<< (mark_bit.Get() ? "marked" : "unmarked") << std::endl;
os << " mark-bit cell: " << mark_bit.CellAddress()
<< ", mask: " << mark_bit.Mask() << std::endl;
#endif
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_FunctionCallbackInfo(
void* function_callback_info) {
#ifdef OBJECT_PRINT
i::PrintFunctionCallbackInfo(function_callback_info);
#endif
}
V8_DEBUGGING_EXPORT extern "C" void _v8_internal_Print_PropertyCallbackInfo(
void* property_callback_info) {
#ifdef OBJECT_PRINT
i::PrintPropertyCallbackInfo(property_callback_info);
#endif
}