[vm] Add --disassemble-relative to use PC offsets instead of absolute PCs in --disassemble's output.
Makes it much easier to compare the output when making VM changes.
--disassemble:
;; Invocation Count Check
0x7f82b8900320 498b7c2437 movq rdi,[r12+0x37]
0x7f82b8900325 ff8783000000 incl [rdi+0x83]
0x7f82b890032b 81bf8300000030750000 cmpl [rdi+0x83],0x7530
0x7f82b8900335 7c07 jl 0x00007f82b890033e
0x7f82b8900337 41ffa6b8010000 jmp [thr+0x1b8]
--disassemble --disassemble-relative:
;; Invocation Count Check
0x0 498b7c2437 movq rdi,[r12+0x37]
0x5 ff8783000000 incl [rdi+0x83]
0xb 81bf8300000030750000 cmpl [rdi+0x83],0x7530
0x15 7c07 jl +9
0x17 41ffa6b8010000 jmp [thr+0x1b8]
Change-Id: Ie05c532f830027b928d7d1d57509eb237157a127
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106600
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Reviewed-by: Aart Bik <ajcbik@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
a468780d96
commit
240f25a940
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Verify running with --disassemble --disassemble-relative produces
|
||||
// deterministic output. This is useful for removing noise when tracking down
|
||||
// the effects of compiler changes.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'snapshot_test_helper.dart';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
int fib(int n) {
|
||||
if (n <= 1) return 1;
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
if (args.contains('--child')) {
|
||||
print(fib(35));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Platform.script.toString().endsWith(".dart")) {
|
||||
return; // Not running from source: skip for app-jit and app-aot.
|
||||
}
|
||||
if (Platform.executable.contains("Product")) {
|
||||
return; // No disassembler in product mode.
|
||||
}
|
||||
if (Platform.executable.contains("IA32")) {
|
||||
return; // Our IA32 code is not position independent.
|
||||
}
|
||||
|
||||
final result1 = await runDart('GENERATE DISASSEMBLY 1', [
|
||||
'--deterministic',
|
||||
'--disassemble',
|
||||
'--disassemble-relative',
|
||||
Platform.script.toFilePath(),
|
||||
'--child'
|
||||
]);
|
||||
final asm1 = result1.processResult.stderr;
|
||||
|
||||
final result2 = await runDart('GENERATE DISASSEMBLY 2', [
|
||||
'--deterministic',
|
||||
'--disassemble',
|
||||
'--disassemble-relative',
|
||||
Platform.script.toFilePath(),
|
||||
'--child'
|
||||
]);
|
||||
final asm2 = result2.processResult.stderr;
|
||||
|
||||
Expect.isTrue(
|
||||
asm1.contains("Code for function"), "Printed at least one function");
|
||||
Expect.stringEquals(asm1, asm2);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Verify running with --print-flow-graph produces deterministic output. This is
|
||||
// useful for removing noise when tracking down the effects of compiler changes.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'snapshot_test_helper.dart';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
int fib(int n) {
|
||||
if (n <= 1) return 1;
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
if (args.contains('--child')) {
|
||||
print(fib(35));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Platform.script.toString().endsWith(".dart")) {
|
||||
return; // Not running from source: skip for app-jit and app-aot.
|
||||
}
|
||||
if (Platform.executable.contains("Product")) {
|
||||
return; // No flow graph printer in product mode.
|
||||
}
|
||||
|
||||
final result1 = await runDart('GENERATE CFG 1', [
|
||||
'--deterministic',
|
||||
'--print-flow-graph',
|
||||
Platform.script.toFilePath(),
|
||||
'--child'
|
||||
]);
|
||||
final cfg1 = result1.processResult.stderr;
|
||||
|
||||
final result2 = await runDart('GENERATE CFG 2', [
|
||||
'--deterministic',
|
||||
'--print-flow-graph',
|
||||
Platform.script.toFilePath(),
|
||||
'--child'
|
||||
]);
|
||||
final cfg2 = result2.processResult.stderr;
|
||||
|
||||
Expect.isTrue(
|
||||
cfg1.contains("*** BEGIN CFG"), "Printed at least one function");
|
||||
Expect.stringEquals(cfg1, cfg2);
|
||||
}
|
||||
@@ -28,8 +28,11 @@ void DisassembleToStdout::ConsumeInstruction(char* hex_buffer,
|
||||
Object* object,
|
||||
uword pc) {
|
||||
static const int kHexColumnWidth = 23;
|
||||
uint8_t* pc_ptr = reinterpret_cast<uint8_t*>(pc);
|
||||
THR_Print("%p %s", pc_ptr, hex_buffer);
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
THR_Print("0x%" Px32 " %s", static_cast<uint32_t>(pc), hex_buffer);
|
||||
#else
|
||||
THR_Print("0x%" Px64 " %s", static_cast<uint64_t>(pc), hex_buffer);
|
||||
#endif
|
||||
int hex_length = strlen(hex_buffer);
|
||||
if (hex_length < kHexColumnWidth) {
|
||||
for (int i = kHexColumnWidth - hex_length; i > 0; i--) {
|
||||
@@ -201,7 +204,8 @@ void Disassembler::Disassemble(uword start,
|
||||
sizeof(human_buffer), &instruction_length, code, &object,
|
||||
pc);
|
||||
formatter->ConsumeInstruction(hex_buffer, sizeof(hex_buffer), human_buffer,
|
||||
sizeof(human_buffer), object, pc);
|
||||
sizeof(human_buffer), object,
|
||||
FLAG_disassemble_relative ? offset : pc);
|
||||
pc += instruction_length;
|
||||
}
|
||||
}
|
||||
@@ -248,6 +252,7 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,
|
||||
|
||||
const auto& instructions = Instructions::Handle(code.instructions());
|
||||
const uword start = instructions.PayloadStart();
|
||||
const uword base = FLAG_disassemble_relative ? 0 : start;
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
const Array& deopt_table = Array::Handle(zone, code.deopt_info_array());
|
||||
@@ -263,7 +268,7 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,
|
||||
DeoptTable::ReasonField::decode(reason_and_flags.Value());
|
||||
ASSERT((0 <= reason) && (reason < ICData::kDeoptNumReasons));
|
||||
THR_Print(
|
||||
"%4" Pd ": 0x%" Px " %s (%s)\n", i, start + offset.Value(),
|
||||
"%4" Pd ": 0x%" Px " %s (%s)\n", i, base + offset.Value(),
|
||||
DeoptInfo::ToCString(deopt_table, info),
|
||||
DeoptReasonToCString(static_cast<ICData::DeoptReasonId>(reason)));
|
||||
}
|
||||
@@ -322,19 +327,20 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,
|
||||
THR_Print("Entry points for function '%s' {\n", function_fullname);
|
||||
THR_Print(" [code+0x%02" Px "] %" Px " kNormal\n",
|
||||
Code::entry_point_offset(CodeEntryKind::kNormal) - kHeapObjectTag,
|
||||
Instructions::EntryPoint(instructions.raw()));
|
||||
THR_Print(
|
||||
" [code+0x%02" Px "] %" Px " kUnchecked\n",
|
||||
Code::entry_point_offset(CodeEntryKind::kUnchecked) - kHeapObjectTag,
|
||||
Instructions::UncheckedEntryPoint(instructions.raw()));
|
||||
Instructions::EntryPoint(instructions.raw()) - start + base);
|
||||
THR_Print(
|
||||
" [code+0x%02" Px "] %" Px " kMonomorphic\n",
|
||||
Code::entry_point_offset(CodeEntryKind::kMonomorphic) - kHeapObjectTag,
|
||||
Instructions::MonomorphicEntryPoint(instructions.raw()));
|
||||
Instructions::MonomorphicEntryPoint(instructions.raw()) - start + base);
|
||||
THR_Print(
|
||||
" [code+0x%02" Px "] %" Px " kUnchecked\n",
|
||||
Code::entry_point_offset(CodeEntryKind::kUnchecked) - kHeapObjectTag,
|
||||
Instructions::UncheckedEntryPoint(instructions.raw()) - start + base);
|
||||
THR_Print(" [code+0x%02" Px "] %" Px " kMonomorphicUnchecked\n",
|
||||
Code::entry_point_offset(CodeEntryKind::kMonomorphicUnchecked) -
|
||||
kHeapObjectTag,
|
||||
Instructions::MonomorphicUncheckedEntryPoint(instructions.raw()));
|
||||
Instructions::MonomorphicUncheckedEntryPoint(instructions.raw()) -
|
||||
start + base);
|
||||
THR_Print("}\n");
|
||||
}
|
||||
|
||||
@@ -376,17 +382,15 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,
|
||||
if (function.IsNull()) {
|
||||
cls ^= code.owner();
|
||||
if (cls.IsNull()) {
|
||||
THR_Print(" 0x%" Px ": %s, %p (%s)%s\n", start + offset,
|
||||
code.QualifiedName(), code.raw(), skind, s_entry_point);
|
||||
THR_Print(" 0x%" Px ": %s, (%s)%s\n", base + offset,
|
||||
code.QualifiedName(), skind, s_entry_point);
|
||||
} else {
|
||||
THR_Print(" 0x%" Px ": allocation stub for %s, %p (%s)%s\n",
|
||||
start + offset, cls.ToCString(), code.raw(), skind,
|
||||
s_entry_point);
|
||||
THR_Print(" 0x%" Px ": allocation stub for %s, (%s)%s\n",
|
||||
base + offset, cls.ToCString(), skind, s_entry_point);
|
||||
}
|
||||
} else {
|
||||
THR_Print(" 0x%" Px ": %s, %p (%s)%s\n", start + offset,
|
||||
function.ToFullyQualifiedCString(), code.raw(), skind,
|
||||
s_entry_point);
|
||||
THR_Print(" 0x%" Px ": %s, (%s)%s\n", base + offset,
|
||||
function.ToFullyQualifiedCString(), skind, s_entry_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,11 +403,17 @@ int ARMDecoder::FormatOption(Instr* instr, const char* format) {
|
||||
case 'd': {
|
||||
if (format[1] == 'e') { // 'dest: branch destination
|
||||
ASSERT(STRING_STARTS_WITH(format, "dest"));
|
||||
int off = (instr->SImmed24Field() << 2) + 8;
|
||||
uword destination = reinterpret_cast<uword>(instr) + off;
|
||||
buffer_pos_ +=
|
||||
Utils::SNPrint(current_position_in_buffer(),
|
||||
remaining_size_in_buffer(), "%#" Px "", destination);
|
||||
const int32_t off = (instr->SImmed24Field() << 2) + 8;
|
||||
if (FLAG_disassemble_relative) {
|
||||
buffer_pos_ +=
|
||||
Utils::SNPrint(current_position_in_buffer(),
|
||||
remaining_size_in_buffer(), "%+" Pd32 "", off);
|
||||
} else {
|
||||
uword destination = reinterpret_cast<uword>(instr) + off;
|
||||
buffer_pos_ += Utils::SNPrint(current_position_in_buffer(),
|
||||
remaining_size_in_buffer(), "%#" Px "",
|
||||
destination);
|
||||
}
|
||||
return 4;
|
||||
} else {
|
||||
return FormatDRegister(instr, format);
|
||||
|
||||
@@ -395,29 +395,28 @@ int ARM64Decoder::FormatOption(Instr* instr, const char* format) {
|
||||
}
|
||||
}
|
||||
case 'd': {
|
||||
int64_t off;
|
||||
if (format[4] == '2') {
|
||||
ASSERT(STRING_STARTS_WITH(format, "dest26"));
|
||||
int64_t off = instr->SImm26Field() << 2;
|
||||
off = instr->SImm26Field() << 2;
|
||||
} else {
|
||||
if (format[5] == '4') {
|
||||
ASSERT(STRING_STARTS_WITH(format, "dest14"));
|
||||
off = instr->SImm14Field() << 2;
|
||||
} else {
|
||||
ASSERT(STRING_STARTS_WITH(format, "dest19"));
|
||||
off = instr->SImm19Field() << 2;
|
||||
}
|
||||
}
|
||||
if (FLAG_disassemble_relative) {
|
||||
buffer_pos_ +=
|
||||
Utils::SNPrint(current_position_in_buffer(),
|
||||
remaining_size_in_buffer(), "%+" Pd64 "", off);
|
||||
} else {
|
||||
uword destination = reinterpret_cast<uword>(instr) + off;
|
||||
buffer_pos_ +=
|
||||
Utils::SNPrint(current_position_in_buffer(),
|
||||
remaining_size_in_buffer(), "%#" Px "", destination);
|
||||
} else {
|
||||
if (format[5] == '4') {
|
||||
ASSERT(STRING_STARTS_WITH(format, "dest14"));
|
||||
int64_t off = instr->SImm14Field() << 2;
|
||||
uword destination = reinterpret_cast<uword>(instr) + off;
|
||||
buffer_pos_ += Utils::SNPrint(current_position_in_buffer(),
|
||||
remaining_size_in_buffer(), "%#" Px "",
|
||||
destination);
|
||||
} else {
|
||||
ASSERT(STRING_STARTS_WITH(format, "dest19"));
|
||||
int64_t off = instr->SImm19Field() << 2;
|
||||
uword destination = reinterpret_cast<uword>(instr) + off;
|
||||
buffer_pos_ += Utils::SNPrint(current_position_in_buffer(),
|
||||
remaining_size_in_buffer(), "%#" Px "",
|
||||
destination);
|
||||
}
|
||||
}
|
||||
return 6;
|
||||
}
|
||||
|
||||
@@ -324,6 +324,7 @@ class DisassemblerX64 : public ValueObject {
|
||||
}
|
||||
|
||||
void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
|
||||
void PrintJump(uint8_t* pc, int32_t disp);
|
||||
void PrintAddress(uint8_t* addr);
|
||||
|
||||
int PrintOperands(const char* mnem, OperandType op_order, uint8_t* data);
|
||||
@@ -772,6 +773,14 @@ int DisassemblerX64::PrintOperands(const char* mnem,
|
||||
return advance;
|
||||
}
|
||||
|
||||
void DisassemblerX64::PrintJump(uint8_t* pc, int32_t disp) {
|
||||
if (FLAG_disassemble_relative) {
|
||||
Print("%+d", disp);
|
||||
} else {
|
||||
PrintAddress(pc + disp);
|
||||
}
|
||||
}
|
||||
|
||||
void DisassemblerX64::PrintAddress(uint8_t* addr_byte_ptr) {
|
||||
#if defined(TARGET_ARCH_X64)
|
||||
Print("%#018" Px64 "", reinterpret_cast<uint64_t>(addr_byte_ptr));
|
||||
@@ -790,9 +799,9 @@ void DisassemblerX64::PrintAddress(uint8_t* addr_byte_ptr) {
|
||||
int DisassemblerX64::JumpShort(uint8_t* data) {
|
||||
ASSERT(0xEB == *data);
|
||||
uint8_t b = *(data + 1);
|
||||
uint8_t* dest = data + static_cast<int8_t>(b) + 2;
|
||||
int32_t disp = static_cast<int8_t>(b) + 2;
|
||||
Print("jmp ");
|
||||
PrintAddress(dest);
|
||||
PrintJump(data, disp);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -800,10 +809,10 @@ int DisassemblerX64::JumpShort(uint8_t* data) {
|
||||
int DisassemblerX64::JumpConditional(uint8_t* data) {
|
||||
ASSERT(0x0F == *data);
|
||||
uint8_t cond = *(data + 1) & 0x0F;
|
||||
uint8_t* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6;
|
||||
int32_t disp = *reinterpret_cast<int32_t*>(data + 2) + 6;
|
||||
const char* mnem = conditional_code_suffix[cond];
|
||||
Print("j%s ", mnem);
|
||||
PrintAddress(dest);
|
||||
PrintJump(data, disp);
|
||||
return 6; // includes 0x0F
|
||||
}
|
||||
|
||||
@@ -811,10 +820,10 @@ int DisassemblerX64::JumpConditional(uint8_t* data) {
|
||||
int DisassemblerX64::JumpConditionalShort(uint8_t* data) {
|
||||
uint8_t cond = *data & 0x0F;
|
||||
uint8_t b = *(data + 1);
|
||||
uint8_t* dest = data + static_cast<int8_t>(b) + 2;
|
||||
int32_t disp = static_cast<int8_t>(b) + 2;
|
||||
const char* mnem = conditional_code_suffix[cond];
|
||||
Print("j%s ", mnem);
|
||||
PrintAddress(dest);
|
||||
PrintJump(data, disp);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -1190,9 +1199,9 @@ bool DisassemblerX64::DecodeInstructionType(uint8_t** data) {
|
||||
}
|
||||
|
||||
case CALL_JUMP_INSTR: {
|
||||
uint8_t* addr = *data + *reinterpret_cast<int32_t*>(*data + 1) + 5;
|
||||
int32_t disp = *reinterpret_cast<int32_t*>(*data + 1) + 5;
|
||||
Print("%s ", idesc.mnem);
|
||||
PrintAddress(addr);
|
||||
PrintJump(*data, disp);
|
||||
(*data) += 5;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ constexpr bool kDartPrecompiledRuntime = false;
|
||||
R(disable_alloc_stubs_after_gc, false, bool, false, "Stress testing flag.") \
|
||||
R(disassemble, false, bool, false, "Disassemble dart code.") \
|
||||
R(disassemble_optimized, false, bool, false, "Disassemble optimized code.") \
|
||||
R(disassemble_relative, false, bool, false, \
|
||||
"Use offsets instead of absolute PCs") \
|
||||
R(dump_megamorphic_stats, false, bool, false, \
|
||||
"Dump megamorphic cache statistics") \
|
||||
R(dump_symbol_stats, false, bool, false, "Dump symbol table statistics") \
|
||||
|
||||
+23
-16
@@ -5536,8 +5536,8 @@ const char* TypeArguments::ToCString() const {
|
||||
return "TypeArguments: null";
|
||||
}
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const char* prev_cstr = OS::SCreate(zone, "TypeArguments: (@%p H%" Px ")",
|
||||
raw(), Smi::Value(raw_ptr()->hash_));
|
||||
const char* prev_cstr = OS::SCreate(zone, "TypeArguments: (H%" Px ")",
|
||||
Smi::Value(raw_ptr()->hash_));
|
||||
for (int i = 0; i < Length(); i++) {
|
||||
const AbstractType& type_at = AbstractType::Handle(zone, TypeAt(i));
|
||||
const char* type_cstr = type_at.IsNull() ? "null" : type_at.ToCString();
|
||||
@@ -12619,17 +12619,24 @@ const char* ObjectPool::ToCString() const {
|
||||
}
|
||||
|
||||
void ObjectPool::DebugPrint() const {
|
||||
THR_Print("Object Pool: 0x%" Px "{\n", reinterpret_cast<uword>(raw()));
|
||||
THR_Print("ObjectPool len:%" Pd " {\n", Length());
|
||||
for (intptr_t i = 0; i < Length(); i++) {
|
||||
intptr_t offset = OffsetFromIndex(i);
|
||||
THR_Print(" %" Pd " PP+0x%" Px ": ", i, offset);
|
||||
THR_Print(" [pp+0x%" Px "] ", offset);
|
||||
if ((TypeAt(i) == EntryType::kTaggedObject) ||
|
||||
(TypeAt(i) == EntryType::kNativeEntryData)) {
|
||||
RawObject* obj = ObjectAt(i);
|
||||
THR_Print("0x%" Px " %s (obj)\n", reinterpret_cast<uword>(obj),
|
||||
Object::Handle(obj).ToCString());
|
||||
const Object& obj = Object::Handle(ObjectAt(i));
|
||||
THR_Print("%s (obj)\n", obj.ToCString());
|
||||
} else if (TypeAt(i) == EntryType::kNativeFunction) {
|
||||
THR_Print("0x%" Px " (native function)\n", RawValueAt(i));
|
||||
uword pc = RawValueAt(i);
|
||||
uintptr_t start = 0;
|
||||
char* name = NativeSymbolResolver::LookupSymbolName(pc, &start);
|
||||
if (name != NULL) {
|
||||
THR_Print("%s (native function)\n", name);
|
||||
NativeSymbolResolver::FreeSymbolName(name);
|
||||
} else {
|
||||
THR_Print("0x%" Px " (native function)\n", pc);
|
||||
}
|
||||
} else if (TypeAt(i) == EntryType::kNativeFunctionWrapper) {
|
||||
THR_Print("0x%" Px " (native function wrapper)\n", RawValueAt(i));
|
||||
} else {
|
||||
@@ -15451,7 +15458,7 @@ void Context::Dump(int indent) const {
|
||||
}
|
||||
|
||||
IndentN(indent);
|
||||
THR_Print("Context@%p vars(%" Pd ") {\n", this->raw(), num_variables());
|
||||
THR_Print("Context vars(%" Pd ") {\n", num_variables());
|
||||
Object& obj = Object::Handle();
|
||||
for (intptr_t i = 0; i < num_variables(); i++) {
|
||||
IndentN(indent + 2);
|
||||
@@ -18061,8 +18068,8 @@ const char* Type::ToCString() const {
|
||||
return OS::SCreate(zone, "Type: class '%s'", class_name);
|
||||
} else if (IsFinalized() && IsRecursive()) {
|
||||
const intptr_t hash = Hash();
|
||||
return OS::SCreate(zone, "Type: (@%p H%" Px ") class '%s', args:[%s]",
|
||||
raw(), hash, class_name, args_cstr);
|
||||
return OS::SCreate(zone, "Type: (H%" Px ") class '%s', args:[%s]", hash,
|
||||
class_name, args_cstr);
|
||||
} else {
|
||||
return OS::SCreate(zone, "Type: class '%s', args:[%s]", class_name,
|
||||
args_cstr);
|
||||
@@ -18196,17 +18203,17 @@ RawTypeRef* TypeRef::New(const AbstractType& type) {
|
||||
}
|
||||
|
||||
const char* TypeRef::ToCString() const {
|
||||
AbstractType& ref_type = AbstractType::Handle(type());
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
AbstractType& ref_type = AbstractType::Handle(zone, type());
|
||||
if (ref_type.IsNull()) {
|
||||
return "TypeRef: null";
|
||||
}
|
||||
const char* type_cstr = String::Handle(ref_type.Name()).ToCString();
|
||||
const char* type_cstr = String::Handle(zone, ref_type.Name()).ToCString();
|
||||
if (ref_type.IsFinalized()) {
|
||||
const intptr_t hash = ref_type.Hash();
|
||||
return OS::SCreate(Thread::Current()->zone(), "TypeRef: %s (@%p H%" Px ")",
|
||||
type_cstr, ref_type.raw(), hash);
|
||||
return OS::SCreate(zone, "TypeRef: %s (H%" Px ")", type_cstr, hash);
|
||||
} else {
|
||||
return OS::SCreate(Thread::Current()->zone(), "TypeRef: %s", type_cstr);
|
||||
return OS::SCreate(zone, "TypeRef: %s", type_cstr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user