mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[vm] Add feature to connect AOT code to code comments via .debug_line
Option --write_code_comments_as_synthetic_source_to=file tells AOT compiler to create a file which contains code comments for all code objects written into an ELF snapshot and then emit a DWARF line number program into .debug_line section which attributes code to code comments in a way similar to how our own disassembler does it. This enables tools like objdump to display our code comments as part of the disassembly. This CL also tweaks ifdefs in such a way that IL printer and code comments facilities is now included into PRODUCT gen_snapshot binary. TEST=manually run product build with --print-flow-graph and --write_code_comments_as_synthetic_source_to Change-Id: Id6741013d43e1733b4ddeb34891a4d2fc06b9313 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/181380 Commit-Queue: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Tess Strickland <sstrickl@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
e5e88fd3cc
commit
d7a57a5df4
|
@ -712,6 +712,16 @@ inline D bit_copy(const S& source) {
|
|||
// Undefine math.h definition which clashes with our condition names.
|
||||
#undef OVERFLOW
|
||||
|
||||
// Include IL printer functionality into non-PRODUCT builds or in all AOT
|
||||
// compiler builds or when forced.
|
||||
#if !defined(PRODUCT) || defined(DART_PRECOMPILER) || \
|
||||
defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) && defined(PRODUCT)
|
||||
#error Requested to include IL printer into PRODUCT AOT runtime
|
||||
#endif
|
||||
#define INCLUDE_IL_PRINTER 1
|
||||
#endif
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_PLATFORM_GLOBALS_H_
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
// 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.
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) && \
|
||||
(!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
|
||||
#include "vm/globals.h" // For INCLUDE_IL_PRINTER
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
#include "vm/code_comments.h"
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#include "vm/compiler/assembler/assembler.h"
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#include "vm/object.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
const Code::Comments& CreateCommentsFrom(compiler::Assembler* assembler) {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
const CodeComments& CreateCommentsFrom(compiler::Assembler* assembler) {
|
||||
const auto& comments = assembler->comments();
|
||||
Code::Comments& result = Code::Comments::New(comments.length());
|
||||
|
||||
|
@ -19,7 +24,7 @@ const Code::Comments& CreateCommentsFrom(compiler::Assembler* assembler) {
|
|||
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
} // namespace dart
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) && \
|
||||
// (!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
|
|
@ -5,41 +5,31 @@
|
|||
#ifndef RUNTIME_VM_CODE_COMMENTS_H_
|
||||
#define RUNTIME_VM_CODE_COMMENTS_H_
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) && \
|
||||
(!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
|
||||
|
||||
#include "vm/code_observers.h"
|
||||
#include "vm/compiler/assembler/assembler.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/globals.h" // For INCLUDE_IL_PRINTER
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
namespace dart {
|
||||
|
||||
class CodeCommentsWrapper final : public CodeComments {
|
||||
// An abstract representation of comments associated with the given code
|
||||
// object. We assume that comments are sorted by PCOffset.
|
||||
class CodeComments {
|
||||
public:
|
||||
explicit CodeCommentsWrapper(const Code::Comments& comments)
|
||||
: comments_(comments), string_(String::Handle()) {}
|
||||
CodeComments() = default;
|
||||
virtual ~CodeComments() = default;
|
||||
|
||||
intptr_t Length() const override { return comments_.Length(); }
|
||||
|
||||
intptr_t PCOffsetAt(intptr_t i) const override {
|
||||
return comments_.PCOffsetAt(i);
|
||||
}
|
||||
|
||||
const char* CommentAt(intptr_t i) const override {
|
||||
string_ = comments_.CommentAt(i);
|
||||
return string_.ToCString();
|
||||
}
|
||||
|
||||
private:
|
||||
const Code::Comments& comments_;
|
||||
String& string_;
|
||||
virtual intptr_t Length() const = 0;
|
||||
virtual intptr_t PCOffsetAt(intptr_t index) const = 0;
|
||||
virtual const char* CommentAt(intptr_t index) const = 0;
|
||||
};
|
||||
|
||||
const Code::Comments& CreateCommentsFrom(compiler::Assembler* assembler);
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
namespace compiler {
|
||||
class Assembler;
|
||||
}
|
||||
const CodeComments& CreateCommentsFrom(compiler::Assembler* assembler);
|
||||
#endif
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) && \
|
||||
// (!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
#endif // RUNTIME_VM_CODE_COMMENTS_H_
|
||||
|
|
|
@ -10,25 +10,10 @@
|
|||
|
||||
#include "include/dart_api.h"
|
||||
|
||||
#if !defined(PRODUCT)
|
||||
namespace dart {
|
||||
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
||||
// An abstract representation of comments associated with the given code
|
||||
// object. We assume that comments are sorted by PCOffset.
|
||||
class CodeComments : public ValueObject {
|
||||
public:
|
||||
CodeComments() = default;
|
||||
virtual ~CodeComments() = default;
|
||||
|
||||
virtual intptr_t Length() const = 0;
|
||||
virtual intptr_t PCOffsetAt(intptr_t index) const = 0;
|
||||
virtual const char* CommentAt(intptr_t index) const = 0;
|
||||
};
|
||||
|
||||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
||||
#if !defined(PRODUCT)
|
||||
class CodeComments;
|
||||
|
||||
// Object observing code creation events. Used by external profilers and
|
||||
// debuggers to map address ranges to function names.
|
||||
|
@ -86,8 +71,7 @@ class CodeObservers : public AllStatic {
|
|||
static CodeObserver** observers_;
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_CODE_OBSERVERS_H_
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#include "platform/globals.h" // For INCLUDE_IL_PRINTER
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
#include "vm/compiler/api/print_filter.h"
|
||||
|
||||
|
@ -67,4 +68,4 @@ bool PrintFilter::ShouldPrint(const Function& function) {
|
|||
|
||||
} // namespace dart
|
||||
|
||||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
#ifndef RUNTIME_VM_COMPILER_API_PRINT_FILTER_H_
|
||||
#define RUNTIME_VM_COMPILER_API_PRINT_FILTER_H_
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
||||
#include "platform/globals.h" // For INCLUDE_IL_PRINTER
|
||||
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
#include "platform/allocation.h"
|
||||
|
||||
|
@ -23,5 +26,5 @@ class PrintFilter : public AllStatic {
|
|||
|
||||
} // namespace dart
|
||||
|
||||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
#endif // RUNTIME_VM_COMPILER_API_PRINT_FILTER_H_
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "platform/text_buffer.h"
|
||||
#include "platform/unaligned.h"
|
||||
#include "vm/code_comments.h"
|
||||
#include "vm/code_patcher.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/deopt_instructions.h"
|
||||
|
@ -161,7 +162,7 @@ void Disassembler::Disassemble(uword start,
|
|||
uword end,
|
||||
DisassemblyFormatter* formatter,
|
||||
const Code& code,
|
||||
const Code::Comments* comments) {
|
||||
const CodeComments* comments) {
|
||||
if (comments == nullptr) {
|
||||
comments = code.IsNull() ? &Code::Comments::New(0) : &code.comments();
|
||||
}
|
||||
|
@ -177,9 +178,7 @@ void Disassembler::Disassemble(uword start,
|
|||
const intptr_t old_comment_finger = comment_finger;
|
||||
while (comment_finger < comments->Length() &&
|
||||
comments->PCOffsetAt(comment_finger) <= offset) {
|
||||
formatter->Print(
|
||||
" ;; %s\n",
|
||||
String::Handle(comments->CommentAt(comment_finger)).ToCString());
|
||||
formatter->Print(" ;; %s\n", comments->CommentAt(comment_finger));
|
||||
comment_finger++;
|
||||
}
|
||||
if (old_comment_finger != comment_finger && !code.IsNull()) {
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
namespace dart {
|
||||
|
||||
// Forward declaration.
|
||||
class MemoryRegion;
|
||||
class CodeComments;
|
||||
class JSONArray;
|
||||
class MemoryRegion;
|
||||
|
||||
// Disassembly formatter interface, which consumes the
|
||||
// disassembled instructions in any desired form.
|
||||
|
@ -122,7 +123,7 @@ class Disassembler : public AllStatic {
|
|||
uword end,
|
||||
DisassemblyFormatter* formatter,
|
||||
const Code& code,
|
||||
const Code::Comments* comments = nullptr);
|
||||
const CodeComments* comments = nullptr);
|
||||
|
||||
static void Disassemble(uword start,
|
||||
uword end,
|
||||
|
@ -133,7 +134,7 @@ class Disassembler : public AllStatic {
|
|||
static void Disassemble(uword start,
|
||||
uword end,
|
||||
DisassemblyFormatter* formatter,
|
||||
const Code::Comments* comments) {
|
||||
const CodeComments* comments) {
|
||||
Disassemble(start, end, formatter, Code::Handle(), comments);
|
||||
}
|
||||
|
||||
|
|
|
@ -1547,15 +1547,12 @@ void FlowGraphCompiler::GenerateListTypeCheck(
|
|||
}
|
||||
|
||||
void FlowGraphCompiler::EmitComment(Instruction* instr) {
|
||||
if (!FLAG_support_il_printer || !FLAG_support_disassembler) {
|
||||
return;
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
char buffer[256];
|
||||
BufferFormatter f(buffer, sizeof(buffer));
|
||||
instr->PrintTo(&f);
|
||||
assembler()->Comment("%s", buffer);
|
||||
#endif
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
}
|
||||
|
||||
bool FlowGraphCompiler::NeedsEdgeCounter(BlockEntryInstr* block) {
|
||||
|
|
|
@ -146,9 +146,9 @@ class Value : public ZoneAllocated {
|
|||
void SetReachingType(CompileType* type);
|
||||
void RefineReachingType(CompileType* type);
|
||||
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
void PrintTo(BaseTextBuffer* f) const;
|
||||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
SExpression* ToSExpression(FlowGraphSerializer* s) const;
|
||||
|
||||
|
@ -560,18 +560,14 @@ FOR_EACH_ABSTRACT_INSTRUCTION(FORWARD_DECLARATION)
|
|||
DECLARE_INSTRUCTION_NO_BACKEND(type) \
|
||||
DECLARE_COMPARISON_METHODS
|
||||
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
#define PRINT_TO_SUPPORT virtual void PrintTo(BaseTextBuffer* f) const;
|
||||
#else
|
||||
#define PRINT_TO_SUPPORT
|
||||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#define PRINT_OPERANDS_TO_SUPPORT \
|
||||
virtual void PrintOperandsTo(BaseTextBuffer* f) const;
|
||||
#else
|
||||
#define PRINT_TO_SUPPORT
|
||||
#define PRINT_OPERANDS_TO_SUPPORT
|
||||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
#define TO_S_EXPRESSION_SUPPORT \
|
||||
virtual SExpression* ToSExpression(FlowGraphSerializer* s) const;
|
||||
|
@ -934,10 +930,8 @@ class Instruction : public ZoneAllocated {
|
|||
|
||||
// Printing support.
|
||||
const char* ToCString() const;
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
virtual void PrintTo(BaseTextBuffer* f) const;
|
||||
virtual void PrintOperandsTo(BaseTextBuffer* f) const;
|
||||
#endif
|
||||
PRINT_TO_SUPPORT
|
||||
PRINT_OPERANDS_TO_SUPPORT
|
||||
virtual SExpression* ToSExpression(FlowGraphSerializer* s) const;
|
||||
virtual void AddOperandsToSExpression(SExpList* sexp,
|
||||
FlowGraphSerializer* s) const;
|
||||
|
@ -2397,7 +2391,7 @@ class Definition : public Instruction {
|
|||
type_ = type;
|
||||
}
|
||||
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
const char* TypeAsCString() const {
|
||||
return HasType() ? type_->ToCString() : "";
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
DEFINE_FLAG(bool,
|
||||
display_sorted_ic_data,
|
||||
false,
|
||||
|
@ -1238,7 +1237,7 @@ const char* Environment::ToCString() const {
|
|||
return Thread::Current()->zone()->MakeCopyOfString(buffer);
|
||||
}
|
||||
|
||||
#else // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#else // defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
const char* Instruction::ToCString() const {
|
||||
return DebugName();
|
||||
|
@ -1276,6 +1275,6 @@ bool FlowGraphPrinter::ShouldPrint(const Function& function) {
|
|||
return false;
|
||||
}
|
||||
|
||||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
} // namespace dart
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "vm/dwarf.h"
|
||||
|
||||
#include "vm/code_comments.h"
|
||||
#include "vm/code_descriptors.h"
|
||||
#include "vm/elf.h"
|
||||
#include "vm/image_snapshot.h"
|
||||
|
@ -13,6 +14,11 @@ namespace dart {
|
|||
|
||||
#if defined(DART_PRECOMPILER)
|
||||
|
||||
DEFINE_FLAG(charp,
|
||||
write_code_comments_as_synthetic_source_to,
|
||||
nullptr,
|
||||
"Print comments associated with instructions into the given file");
|
||||
|
||||
class DwarfPosition {
|
||||
public:
|
||||
DwarfPosition(int32_t line, int32_t column) : line_(line), column_(column) {
|
||||
|
@ -506,6 +512,212 @@ void Dwarf::WriteInliningNode(DwarfWriteStream* stream,
|
|||
stream->uleb128(0); // End of children.
|
||||
}
|
||||
|
||||
// Helper class for tracking state of DWARF registers and emitting
|
||||
// line number program commands to set these registers to the right
|
||||
// state.
|
||||
class LineNumberProgramWriter {
|
||||
public:
|
||||
explicit LineNumberProgramWriter(DwarfWriteStream* stream)
|
||||
: stream_(stream) {}
|
||||
|
||||
void SetFile(intptr_t file) {
|
||||
if (file != file_) {
|
||||
stream_->u1(Dwarf::DW_LNS_set_file);
|
||||
stream_->uleb128(file);
|
||||
file_ = file;
|
||||
}
|
||||
}
|
||||
|
||||
void SetLine(intptr_t line) {
|
||||
if (line != line_) {
|
||||
stream_->u1(Dwarf::DW_LNS_advance_line);
|
||||
stream_->sleb128(line - line_);
|
||||
line_ = line;
|
||||
}
|
||||
}
|
||||
|
||||
void SetColumn(intptr_t column) {
|
||||
if (column != column_) {
|
||||
stream_->u1(Dwarf::DW_LNS_set_column);
|
||||
stream_->uleb128(column);
|
||||
column_ = column;
|
||||
}
|
||||
}
|
||||
|
||||
void SetPCOffset(const char* asm_name, intptr_t pc_offset) {
|
||||
if (asm_name_ == nullptr) {
|
||||
auto const instr_size = 1 + compiler::target::kWordSize;
|
||||
stream_->u1(0); // This is an extended opcode
|
||||
stream_->u1(instr_size); // that is 5 or 9 bytes long
|
||||
stream_->u1(Dwarf::DW_LNE_set_address);
|
||||
stream_->OffsetFromSymbol(asm_name, pc_offset);
|
||||
} else {
|
||||
// Emit LNP row if the address register has been updated to a
|
||||
// non-zero value (dartbug.com/41756).
|
||||
stream_->u1(Dwarf::DW_LNS_copy);
|
||||
stream_->u1(Dwarf::DW_LNS_advance_pc);
|
||||
stream_->DistanceBetweenSymbolOffsets(asm_name, pc_offset, asm_name_,
|
||||
pc_offset_);
|
||||
}
|
||||
asm_name_ = asm_name;
|
||||
pc_offset_ = pc_offset;
|
||||
}
|
||||
|
||||
private:
|
||||
DwarfWriteStream* const stream_;
|
||||
// The initial values for the line number program state machine registers
|
||||
// according to the DWARF standard.
|
||||
intptr_t pc_offset_ = 0;
|
||||
intptr_t file_ = 1;
|
||||
intptr_t line_ = 1;
|
||||
intptr_t column_ = 0;
|
||||
|
||||
// Other info not stored in the state machine registers.
|
||||
const char* asm_name_ = nullptr;
|
||||
};
|
||||
|
||||
void Dwarf::WriteSyntheticLineNumberProgram(LineNumberProgramWriter* writer) {
|
||||
// We emit it last after all other scripts.
|
||||
const intptr_t comments_file_index = scripts_.length() + 1;
|
||||
|
||||
auto file_open = Dart::file_open_callback();
|
||||
auto file_write = Dart::file_write_callback();
|
||||
auto file_close = Dart::file_close_callback();
|
||||
if ((file_open == nullptr) || (file_write == nullptr) ||
|
||||
(file_close == nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TextBuffer comments_buffer(128 * KB);
|
||||
|
||||
auto comments_file = file_open(
|
||||
FLAG_write_code_comments_as_synthetic_source_to, /*write=*/true);
|
||||
if (comments_file == nullptr) {
|
||||
OS::PrintErr("Failed to open file %s\n",
|
||||
FLAG_write_code_comments_as_synthetic_source_to);
|
||||
return;
|
||||
}
|
||||
|
||||
intptr_t current_line = 0;
|
||||
|
||||
writer->SetFile(comments_file_index);
|
||||
|
||||
for (intptr_t i = 0; i < codes_.length(); i++) {
|
||||
const Code& code = *(codes_[i]);
|
||||
auto const asm_name = code_to_name_.LookupValue(&code);
|
||||
ASSERT(asm_name != nullptr);
|
||||
|
||||
auto& comments = code.comments();
|
||||
for (intptr_t i = 0, len = comments.Length(); i < len;) {
|
||||
intptr_t current_pc_offset = comments.PCOffsetAt(i);
|
||||
writer->SetPCOffset(asm_name, current_pc_offset);
|
||||
while (i < len && current_pc_offset == comments.PCOffsetAt(i)) {
|
||||
comments_buffer.AddString(comments.CommentAt(i));
|
||||
comments_buffer.AddChar('\n');
|
||||
current_line++;
|
||||
i++;
|
||||
}
|
||||
|
||||
writer->SetLine(current_line);
|
||||
}
|
||||
}
|
||||
|
||||
file_write(comments_buffer.buffer(), comments_buffer.length(), comments_file);
|
||||
file_close(comments_file);
|
||||
}
|
||||
|
||||
void Dwarf::WriteLineNumberProgramFromCodeSourceMaps(
|
||||
LineNumberProgramWriter* writer) {
|
||||
Function& root_function = Function::Handle(zone_);
|
||||
Script& script = Script::Handle(zone_);
|
||||
CodeSourceMap& map = CodeSourceMap::Handle(zone_);
|
||||
Array& functions = Array::Handle(zone_);
|
||||
GrowableArray<const Function*> function_stack(zone_, 8);
|
||||
GrowableArray<DwarfPosition> token_positions(zone_, 8);
|
||||
|
||||
for (intptr_t i = 0; i < codes_.length(); i++) {
|
||||
const Code& code = *(codes_[i]);
|
||||
auto const asm_name = code_to_name_.LookupValue(&code);
|
||||
ASSERT(asm_name != nullptr);
|
||||
|
||||
map = code.code_source_map();
|
||||
if (map.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
root_function = code.function();
|
||||
functions = code.inlined_id_to_function();
|
||||
|
||||
NoSafepointScope no_safepoint;
|
||||
ReadStream code_map_stream(map.Data(), map.Length());
|
||||
|
||||
function_stack.Clear();
|
||||
token_positions.Clear();
|
||||
|
||||
int32_t current_pc_offset = 0;
|
||||
function_stack.Add(&root_function);
|
||||
token_positions.Add(kNoDwarfPositionInfo);
|
||||
|
||||
while (code_map_stream.PendingBytes() > 0) {
|
||||
int32_t arg1;
|
||||
int32_t arg2 = -1;
|
||||
const uint8_t opcode =
|
||||
CodeSourceMapOps::Read(&code_map_stream, &arg1, &arg2);
|
||||
switch (opcode) {
|
||||
case CodeSourceMapOps::kChangePosition: {
|
||||
const DwarfPosition& old_pos =
|
||||
token_positions[token_positions.length() - 1];
|
||||
token_positions[token_positions.length() - 1] = DwarfPosition(
|
||||
Utils::AddWithWrapAround(old_pos.line(), arg1), arg2);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kAdvancePC: {
|
||||
current_pc_offset += arg1;
|
||||
|
||||
const Function& function = *(function_stack.Last());
|
||||
script = function.script();
|
||||
intptr_t file = LookupScript(script);
|
||||
|
||||
// 1. Update LNP file.
|
||||
writer->SetFile(file);
|
||||
|
||||
// 2. Update LNP line.
|
||||
// The DWARF standard uses 0 to denote missing line or column
|
||||
// information.
|
||||
writer->SetLine(token_positions.Last().line() < 0
|
||||
? 0
|
||||
: token_positions.Last().line());
|
||||
writer->SetColumn(token_positions.Last().column() < 0
|
||||
? 0
|
||||
: token_positions.Last().column());
|
||||
|
||||
writer->SetPCOffset(asm_name, current_pc_offset);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kPushFunction: {
|
||||
auto child_func =
|
||||
&Function::Handle(zone_, Function::RawCast(functions.At(arg1)));
|
||||
function_stack.Add(child_func);
|
||||
token_positions.Add(kNoDwarfPositionInfo);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kPopFunction: {
|
||||
// We never pop the root function.
|
||||
ASSERT(function_stack.length() > 1);
|
||||
ASSERT(token_positions.length() > 1);
|
||||
function_stack.RemoveLast();
|
||||
token_positions.RemoveLast();
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kNullCheck: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
|
||||
// 6.2.4 The Line Number Program Header
|
||||
|
||||
|
@ -559,141 +771,21 @@ void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
|
|||
stream->uleb128(0); // File modification time.
|
||||
stream->uleb128(0); // File length.
|
||||
}
|
||||
if (FLAG_write_code_comments_as_synthetic_source_to != nullptr) {
|
||||
stream->string(FLAG_write_code_comments_as_synthetic_source_to); // NOLINT
|
||||
stream->uleb128(0); // Include directory index.
|
||||
stream->uleb128(0); // File modification time.
|
||||
stream->uleb128(0); // File length.
|
||||
}
|
||||
stream->u1(0); // End of file names.
|
||||
stream->SetSize(lineheader_size_fixup, lineheader_prefix, lineheader_start);
|
||||
|
||||
// 6.2.5 The Line Number Program
|
||||
|
||||
// The initial values for the line number program state machine registers
|
||||
// according to the DWARF standard.
|
||||
intptr_t previous_pc_offset = 0;
|
||||
intptr_t previous_file = 1;
|
||||
intptr_t previous_line = 1;
|
||||
intptr_t previous_column = 0;
|
||||
// Other info not stored in the state machine registers.
|
||||
const char* previous_asm_name = nullptr;
|
||||
|
||||
Function& root_function = Function::Handle(zone_);
|
||||
Script& script = Script::Handle(zone_);
|
||||
CodeSourceMap& map = CodeSourceMap::Handle(zone_);
|
||||
Array& functions = Array::Handle(zone_);
|
||||
GrowableArray<const Function*> function_stack(zone_, 8);
|
||||
GrowableArray<DwarfPosition> token_positions(zone_, 8);
|
||||
|
||||
for (intptr_t i = 0; i < codes_.length(); i++) {
|
||||
const Code& code = *(codes_[i]);
|
||||
auto const asm_name = code_to_name_.LookupValue(&code);
|
||||
ASSERT(asm_name != nullptr);
|
||||
|
||||
map = code.code_source_map();
|
||||
if (map.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
root_function = code.function();
|
||||
functions = code.inlined_id_to_function();
|
||||
|
||||
NoSafepointScope no_safepoint;
|
||||
ReadStream code_map_stream(map.Data(), map.Length());
|
||||
|
||||
function_stack.Clear();
|
||||
token_positions.Clear();
|
||||
|
||||
int32_t current_pc_offset = 0;
|
||||
function_stack.Add(&root_function);
|
||||
token_positions.Add(kNoDwarfPositionInfo);
|
||||
|
||||
while (code_map_stream.PendingBytes() > 0) {
|
||||
int32_t arg1;
|
||||
int32_t arg2 = -1;
|
||||
const uint8_t opcode =
|
||||
CodeSourceMapOps::Read(&code_map_stream, &arg1, &arg2);
|
||||
switch (opcode) {
|
||||
case CodeSourceMapOps::kChangePosition: {
|
||||
const DwarfPosition& old_pos =
|
||||
token_positions[token_positions.length() - 1];
|
||||
token_positions[token_positions.length() - 1] = DwarfPosition(
|
||||
Utils::AddWithWrapAround(old_pos.line(), arg1), arg2);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kAdvancePC: {
|
||||
current_pc_offset += arg1;
|
||||
|
||||
const Function& function = *(function_stack.Last());
|
||||
script = function.script();
|
||||
intptr_t file = LookupScript(script);
|
||||
|
||||
// 1. Update LNP file.
|
||||
if (file != previous_file) {
|
||||
stream->u1(DW_LNS_set_file);
|
||||
stream->uleb128(file);
|
||||
previous_file = file;
|
||||
}
|
||||
|
||||
// 2. Update LNP line.
|
||||
// The DWARF standard uses 0 to denote missing line or column
|
||||
// information.
|
||||
const intptr_t line = token_positions.Last().line() < 0
|
||||
? 0
|
||||
: token_positions.Last().line();
|
||||
const intptr_t column = token_positions.Last().column() < 0
|
||||
? 0
|
||||
: token_positions.Last().column();
|
||||
if (line != previous_line) {
|
||||
stream->u1(DW_LNS_advance_line);
|
||||
stream->sleb128(line - previous_line);
|
||||
previous_line = line;
|
||||
}
|
||||
if (column != previous_column) {
|
||||
stream->u1(DW_LNS_set_column);
|
||||
stream->uleb128(column);
|
||||
previous_column = column;
|
||||
}
|
||||
|
||||
// 3. Emit LNP row if the address register has been updated to a
|
||||
// non-zero value (dartbug.com/41756).
|
||||
if (previous_asm_name != nullptr) {
|
||||
stream->u1(DW_LNS_copy);
|
||||
}
|
||||
|
||||
// 4. Update LNP pc.
|
||||
if (previous_asm_name == nullptr) {
|
||||
auto const instr_size = 1 + compiler::target::kWordSize;
|
||||
stream->u1(0); // This is an extended opcode
|
||||
stream->u1(instr_size); // that is 5 or 9 bytes long
|
||||
stream->u1(DW_LNE_set_address);
|
||||
stream->OffsetFromSymbol(asm_name, current_pc_offset);
|
||||
} else {
|
||||
stream->u1(DW_LNS_advance_pc);
|
||||
stream->DistanceBetweenSymbolOffsets(asm_name, current_pc_offset,
|
||||
previous_asm_name,
|
||||
previous_pc_offset);
|
||||
}
|
||||
previous_asm_name = asm_name;
|
||||
previous_pc_offset = current_pc_offset;
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kPushFunction: {
|
||||
const Function& child_func =
|
||||
Function::Handle(zone_, Function::RawCast(functions.At(arg1)));
|
||||
function_stack.Add(&child_func);
|
||||
token_positions.Add(kNoDwarfPositionInfo);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kPopFunction: {
|
||||
// We never pop the root function.
|
||||
ASSERT(function_stack.length() > 1);
|
||||
ASSERT(token_positions.length() > 1);
|
||||
function_stack.RemoveLast();
|
||||
token_positions.RemoveLast();
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kNullCheck: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
LineNumberProgramWriter lnp_writer(stream);
|
||||
if (FLAG_write_code_comments_as_synthetic_source_to != nullptr) {
|
||||
WriteSyntheticLineNumberProgram(&lnp_writer);
|
||||
} else {
|
||||
WriteLineNumberProgramFromCodeSourceMaps(&lnp_writer);
|
||||
}
|
||||
|
||||
// Advance pc to end of the compilation unit if not already there.
|
||||
|
@ -703,16 +795,7 @@ void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
|
|||
const intptr_t last_pc_offset = last_code.Size();
|
||||
const char* last_asm_name = code_to_name_.LookupValue(&last_code);
|
||||
ASSERT(last_asm_name != nullptr);
|
||||
|
||||
stream->u1(DW_LNS_advance_pc);
|
||||
if (previous_asm_name != nullptr) {
|
||||
stream->DistanceBetweenSymbolOffsets(
|
||||
last_asm_name, last_pc_offset, previous_asm_name, previous_pc_offset);
|
||||
} else {
|
||||
// No LNP entries (e.g., only stub code).
|
||||
ASSERT(previous_pc_offset == 0);
|
||||
stream->uleb128(last_pc_offset);
|
||||
}
|
||||
lnp_writer.SetPCOffset(last_asm_name, last_pc_offset);
|
||||
}
|
||||
|
||||
// End of contiguous machine code.
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace dart {
|
|||
#ifdef DART_PRECOMPILER
|
||||
|
||||
class InliningNode;
|
||||
class LineNumberProgramWriter;
|
||||
|
||||
struct ScriptIndexPair {
|
||||
// Typedefs needed for the DirectChainedHashMap template.
|
||||
|
@ -250,6 +251,8 @@ class Dwarf : public ZoneAllocated {
|
|||
void WriteLineNumberProgram(DwarfWriteStream* stream);
|
||||
|
||||
private:
|
||||
friend class LineNumberProgramWriter;
|
||||
|
||||
static const intptr_t DW_TAG_compile_unit = 0x11;
|
||||
static const intptr_t DW_TAG_inlined_subroutine = 0x1d;
|
||||
static const intptr_t DW_TAG_subprogram = 0x2e;
|
||||
|
@ -307,6 +310,10 @@ class Dwarf : public ZoneAllocated {
|
|||
const char* root_code_name,
|
||||
const Script& parent_script);
|
||||
|
||||
void WriteSyntheticLineNumberProgram(LineNumberProgramWriter* writer);
|
||||
void WriteLineNumberProgramFromCodeSourceMaps(
|
||||
LineNumberProgramWriter* writer);
|
||||
|
||||
const char* Deobfuscate(const char* cstr);
|
||||
static Trie<const char>* CreateReverseObfuscationTrie(Zone* zone);
|
||||
|
||||
|
|
|
@ -73,13 +73,12 @@ void NativeCallbackTrampolines::AllocateTrampoline() {
|
|||
ASSERT(!Thread::Current()->IsAtSafepoint());
|
||||
if (CodeObservers::AreActive()) {
|
||||
const auto& comments = CreateCommentsFrom(&assembler);
|
||||
CodeCommentsWrapper wrapper(comments);
|
||||
CodeObservers::NotifyAll(name,
|
||||
/*base=*/memory->start(),
|
||||
/*prologue_offset=*/0,
|
||||
/*size=*/assembler.CodeSize(),
|
||||
/*optimized=*/false, // not really relevant
|
||||
&wrapper);
|
||||
&comments);
|
||||
}
|
||||
#endif
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
|
|
@ -45,6 +45,12 @@ constexpr bool kDartUseBackgroundCompilation = true;
|
|||
R(support_disassembler, false, bool, true, "Support the disassembler.")
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
constexpr bool FLAG_support_il_printer = true;
|
||||
#else
|
||||
constexpr bool FLAG_support_il_printer = false;
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
// List of VM-global (i.e. non-isolate specific) flags.
|
||||
//
|
||||
// The value used for those flags at snapshot generation time needs to be the
|
||||
|
@ -92,8 +98,7 @@ constexpr bool kDartUseBackgroundCompilation = true;
|
|||
"Run optimizing compilation in background") \
|
||||
P(check_token_positions, bool, false, \
|
||||
"Check validity of token positions while compiling flow graphs") \
|
||||
R(code_comments, false, bool, false, \
|
||||
"Include comments into code and disassembly.") \
|
||||
P(code_comments, bool, false, "Include comments into code and disassembly.") \
|
||||
P(collect_code, bool, false, "Attempt to GC infrequently used code.") \
|
||||
P(collect_dynamic_function_names, bool, true, \
|
||||
"Collects all dynamic function names to identify unique targets") \
|
||||
|
@ -197,7 +202,6 @@ constexpr bool kDartUseBackgroundCompilation = true;
|
|||
"well).") \
|
||||
P(show_invisible_frames, bool, false, \
|
||||
"Show invisible frames in stack traces.") \
|
||||
R(support_il_printer, false, bool, true, "Support the IL printer.") \
|
||||
D(trace_cha, bool, false, "Trace CHA operations") \
|
||||
R(trace_field_guards, false, bool, false, "Trace changes in field's cids.") \
|
||||
D(trace_ic, bool, false, "Trace IC handling") \
|
||||
|
|
|
@ -16057,6 +16057,7 @@ ObjectPtr WeakSerializationReference::Wrap(Zone* zone, const Object& target) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
Code::Comments& Code::Comments::New(intptr_t count) {
|
||||
Comments* comments;
|
||||
if (count < 0 || count > (kIntptrMax / kNumberOfEntries)) {
|
||||
|
@ -16091,15 +16092,18 @@ void Code::Comments::SetPCOffsetAt(intptr_t idx, intptr_t pc) {
|
|||
Smi::Handle(Smi::New(pc)));
|
||||
}
|
||||
|
||||
StringPtr Code::Comments::CommentAt(intptr_t idx) const {
|
||||
return String::RawCast(comments_.At(idx * kNumberOfEntries + kCommentEntry));
|
||||
const char* Code::Comments::CommentAt(intptr_t idx) const {
|
||||
string_ ^= comments_.At(idx * kNumberOfEntries + kCommentEntry);
|
||||
return string_.ToCString();
|
||||
}
|
||||
|
||||
void Code::Comments::SetCommentAt(intptr_t idx, const String& comment) {
|
||||
comments_.SetAt(idx * kNumberOfEntries + kCommentEntry, comment);
|
||||
}
|
||||
|
||||
Code::Comments::Comments(const Array& comments) : comments_(comments) {}
|
||||
Code::Comments::Comments(const Array& comments)
|
||||
: comments_(comments), string_(String::Handle()) {}
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
const char* Code::EntryKindToCString(EntryKind kind) {
|
||||
switch (kind) {
|
||||
|
@ -16368,23 +16372,67 @@ void Code::Disassemble(DisassemblyFormatter* formatter) const {
|
|||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
}
|
||||
|
||||
const Code::Comments& Code::comments() const {
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
#if defined(PRODUCT)
|
||||
Comments* comments = new Code::Comments(Array::Handle());
|
||||
#else
|
||||
Comments* comments = new Code::Comments(Array::Handle(untag()->comments()));
|
||||
// In PRODUCT builds we don't have space in Code object to store code comments
|
||||
// so we move them into malloced heap (and leak them). This functionality
|
||||
// is only indended to be used in AOT compiler so leaking is fine.
|
||||
class MallocCodeComments final : public CodeComments {
|
||||
public:
|
||||
explicit MallocCodeComments(const CodeComments& comments)
|
||||
: length_(comments.Length()), comments_(new Comment[comments.Length()]) {
|
||||
for (intptr_t i = 0; i < length_; i++) {
|
||||
comments_[i].pc_offset = comments.PCOffsetAt(i);
|
||||
comments_[i].comment =
|
||||
Utils::CreateCStringUniquePtr(strdup(comments.CommentAt(i)));
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t Length() const override { return length_; }
|
||||
|
||||
intptr_t PCOffsetAt(intptr_t i) const override {
|
||||
return comments_[i].pc_offset;
|
||||
}
|
||||
|
||||
const char* CommentAt(intptr_t i) const override {
|
||||
return comments_[i].comment.get();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Comment {
|
||||
intptr_t pc_offset;
|
||||
Utils::CStringUniquePtr comment{nullptr, std::free};
|
||||
};
|
||||
|
||||
intptr_t length_;
|
||||
std::unique_ptr<Comment[]> comments_;
|
||||
};
|
||||
#endif
|
||||
|
||||
const CodeComments& Code::comments() const {
|
||||
#if defined(PRODUCT)
|
||||
auto comments =
|
||||
static_cast<CodeComments*>(Thread::Current()->heap()->GetPeer(ptr()));
|
||||
return (comments != nullptr) ? *comments : Code::Comments::New(0);
|
||||
#else
|
||||
return *new Code::Comments(Array::Handle(untag()->comments()));
|
||||
#endif
|
||||
return *comments;
|
||||
}
|
||||
|
||||
void Code::set_comments(const Code::Comments& comments) const {
|
||||
#if defined(PRODUCT)
|
||||
UNREACHABLE();
|
||||
void Code::set_comments(const CodeComments& comments) const {
|
||||
#if !defined(PRODUCT)
|
||||
auto& wrapper = static_cast<const Code::Comments&>(comments);
|
||||
ASSERT(wrapper.comments_.IsOld());
|
||||
untag()->set_comments(wrapper.comments_.ptr());
|
||||
#else
|
||||
ASSERT(comments.comments_.IsOld());
|
||||
untag()->set_comments(comments.comments_.ptr());
|
||||
if (FLAG_code_comments && comments.Length() > 0) {
|
||||
Thread::Current()->heap()->SetPeer(ptr(), new MallocCodeComments(comments));
|
||||
} else {
|
||||
Thread::Current()->heap()->SetPeer(ptr(), nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
void Code::SetPrologueOffset(intptr_t offset) const {
|
||||
#if defined(PRODUCT)
|
||||
|
@ -16436,7 +16484,9 @@ CodePtr Code::New(intptr_t pointer_offsets_length) {
|
|||
result.set_is_optimized(false);
|
||||
result.set_is_force_optimized(false);
|
||||
result.set_is_alive(false);
|
||||
NOT_IN_PRODUCT(result.set_comments(Comments::New(0)));
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
result.set_comments(Comments::New(0));
|
||||
#endif
|
||||
NOT_IN_PRODUCT(result.set_compile_timestamp(0));
|
||||
result.set_pc_descriptors(Object::empty_descriptors());
|
||||
result.set_compressed_stackmaps(Object::empty_compressed_stackmaps());
|
||||
|
@ -16603,9 +16653,12 @@ CodePtr Code::FinalizeCode(FlowGraphCompiler* compiler,
|
|||
CPU::FlushICache(instrs.PayloadStart(), instrs.Size());
|
||||
}
|
||||
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
code.set_comments(CreateCommentsFrom(assembler));
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
#ifndef PRODUCT
|
||||
code.set_compile_timestamp(OS::GetCurrentMonotonicMicros());
|
||||
code.set_comments(CreateCommentsFrom(assembler));
|
||||
if (assembler->prologue_offset() >= 0) {
|
||||
code.SetPrologueOffset(assembler->prologue_offset());
|
||||
} else {
|
||||
|
@ -16656,10 +16709,9 @@ void Code::NotifyCodeObservers(const char* name,
|
|||
ASSERT(!Thread::Current()->IsAtSafepoint());
|
||||
if (CodeObservers::AreActive()) {
|
||||
const auto& instrs = Instructions::Handle(code.instructions());
|
||||
CodeCommentsWrapper comments_wrapper(code.comments());
|
||||
CodeObservers::NotifyAll(name, instrs.PayloadStart(),
|
||||
code.GetPrologueOffset(), instrs.Size(), optimized,
|
||||
&comments_wrapper);
|
||||
&code.comments());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "platform/thread_sanitizer.h"
|
||||
#include "platform/utils.h"
|
||||
#include "vm/bitmap.h"
|
||||
#include "vm/code_comments.h"
|
||||
#include "vm/code_entry_kind.h"
|
||||
#include "vm/compiler/assembler/object_pool_builder.h"
|
||||
#include "vm/compiler/method_recognizer.h"
|
||||
|
@ -6181,17 +6182,18 @@ class Code : public Object {
|
|||
|
||||
void Disassemble(DisassemblyFormatter* formatter = NULL) const;
|
||||
|
||||
class Comments : public ZoneAllocated {
|
||||
#if defined(INCLUDE_IL_PRINTER)
|
||||
class Comments : public ZoneAllocated, public CodeComments {
|
||||
public:
|
||||
static Comments& New(intptr_t count);
|
||||
|
||||
intptr_t Length() const;
|
||||
intptr_t Length() const override;
|
||||
|
||||
void SetPCOffsetAt(intptr_t idx, intptr_t pc_offset);
|
||||
void SetCommentAt(intptr_t idx, const String& comment);
|
||||
|
||||
intptr_t PCOffsetAt(intptr_t idx) const;
|
||||
StringPtr CommentAt(intptr_t idx) const;
|
||||
intptr_t PCOffsetAt(intptr_t idx) const override;
|
||||
const char* CommentAt(intptr_t idx) const override;
|
||||
|
||||
private:
|
||||
explicit Comments(const Array& comments);
|
||||
|
@ -6204,14 +6206,16 @@ class Code : public Object {
|
|||
};
|
||||
|
||||
const Array& comments_;
|
||||
String& string_;
|
||||
|
||||
friend class Code;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Comments);
|
||||
};
|
||||
|
||||
const Comments& comments() const;
|
||||
void set_comments(const Comments& comments) const;
|
||||
const CodeComments& comments() const;
|
||||
void set_comments(const CodeComments& comments) const;
|
||||
#endif // defined(INCLUDE_IL_PRINTER)
|
||||
|
||||
ObjectPtr return_address_metadata() const {
|
||||
#if defined(PRODUCT)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "platform/memory_sanitizer.h"
|
||||
#include "platform/utils.h"
|
||||
#include "vm/code_comments.h"
|
||||
#include "vm/code_observers.h"
|
||||
#include "vm/dart.h"
|
||||
#include "vm/flags.h"
|
||||
|
|
Loading…
Reference in a new issue