Files
sdk/runtime/vm/code_descriptors.cc
T
Tess Strickland e2df4d30a0 [vm] Remove non-private uses of TokenPosition::value().
Instead, split each old use into the following cases:

* If the TokenPosition value is expected to be a real token position,
  then use TokenPosition::Pos().

* If the TokenPosition is being serialized in some way, then use
  TokenPosition::Serialize() and change the place where the
  TokenPosition is recreated to use TokenPosition::Deserialize().

* If the value of the TokenPosition is being printed for debugging
  purposes, then just use TokenPosition::ToCString() instead.

That is, we try to pin down when token positions are expected to
be real vs. when other types of token positions can be found.

Another source of possible error when using token positions is to
convert between synthetic and real token positions. In the past,
synthetic token positions may have been based off real token positions,
but that is no longer the case. Thus, all methods that allow
that conversion have been removed, and instead there is a new static
method for constructing synthetic tokens from valid nonces.

This CL also makes it so that Pos() and relational operators on token
positions are only defined on real token positions, to avoid any
assumptions about what the value encoded in synthetic positions mean. To
help with cases where non-real token positions may occur, four helper
methods are added:

* TokenPosition::Min(a, b): A static method that returns the smallest
  real token position provided. If neither `a` or `b` are real,
  returns `a`.

* TokenPosition::Max(a, b): A static method that returns the largest
  real token position provided. If neither `a` or `b` are real,
  returns `a`.

* TokenPosition::IsWithin(start, end): Determines whether `this` falls
  between `start` and `end` (inclusive). If `this` is non-real, then it
  must be either `start` or `end` if synthetic, otherwise false.
  Otherwise, we mimic the old style of range checking, which means that
  non-real starts and ends are treated as less than every real token.

* TokenPosition::CompareForSorting(other): Unlike the relational
  operators, provides a comparison between any types of token positions
  for purposes such as sorting.  Currently only used in the profiler.

It also changes TokenPosition::ToCString() to tag synthetic token
positions, so they can be distinguished from real ones at a glance.

TEST=Existing test suite on trybots, especially the observatory tests
which make heavy use of the debugger and the unit tests for the
profiler/source report modules.

Bug: https://github.com/dart-lang/sdk/issues/44436

Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-linux-debug-x64-try,vm-kernel-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try
Change-Id: Ic06aa0bc7a1f0fbac7257ed22ca5e7e0ccd7f3f2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/174924
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
2020-12-16 08:27:32 +00:00

715 lines
23 KiB
C++

// Copyright (c) 2012, 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.
#include "vm/code_descriptors.h"
#include "vm/compiler/api/deopt_id.h"
#include "vm/log.h"
#include "vm/object_store.h"
#include "vm/zone_text_buffer.h"
namespace dart {
void DescriptorList::AddDescriptor(PcDescriptorsLayout::Kind kind,
intptr_t pc_offset,
intptr_t deopt_id,
TokenPosition token_pos,
intptr_t try_index,
intptr_t yield_index) {
// yield index 0 is reserved for normal entry.
RELEASE_ASSERT(yield_index != 0);
ASSERT((kind == PcDescriptorsLayout::kRuntimeCall) ||
(kind == PcDescriptorsLayout::kBSSRelocation) ||
(kind == PcDescriptorsLayout::kOther) ||
(yield_index != PcDescriptorsLayout::kInvalidYieldIndex) ||
(deopt_id != DeoptId::kNone));
// When precompiling, we only use pc descriptors for exceptions,
// relocations and yield indices.
if (!FLAG_precompiled_mode || try_index != -1 ||
yield_index != PcDescriptorsLayout::kInvalidYieldIndex ||
kind == PcDescriptorsLayout::kBSSRelocation) {
const int32_t kind_and_metadata =
PcDescriptorsLayout::KindAndMetadata::Encode(kind, try_index,
yield_index);
encoded_data_.WriteSLEB128(kind_and_metadata);
encoded_data_.WriteSLEB128(pc_offset - prev_pc_offset);
prev_pc_offset = pc_offset;
if (!FLAG_precompiled_mode) {
const int32_t encoded_pos = token_pos.Serialize();
encoded_data_.WriteSLEB128(deopt_id - prev_deopt_id);
encoded_data_.WriteSLEB128(encoded_pos - prev_token_pos);
prev_deopt_id = deopt_id;
prev_token_pos = encoded_pos;
}
}
}
PcDescriptorsPtr DescriptorList::FinalizePcDescriptors(uword entry_point) {
if (encoded_data_.bytes_written() == 0) {
return Object::empty_descriptors().raw();
}
return PcDescriptors::New(encoded_data_.buffer(),
encoded_data_.bytes_written());
}
void CompressedStackMapsBuilder::AddEntry(intptr_t pc_offset,
BitmapBuilder* bitmap,
intptr_t spill_slot_bit_count) {
ASSERT(bitmap != nullptr);
ASSERT(pc_offset > last_pc_offset_);
ASSERT(spill_slot_bit_count >= 0 && spill_slot_bit_count <= bitmap->Length());
const uword pc_delta = pc_offset - last_pc_offset_;
const uword non_spill_slot_bit_count =
bitmap->Length() - spill_slot_bit_count;
encoded_bytes_.WriteLEB128(pc_delta);
encoded_bytes_.WriteLEB128(spill_slot_bit_count);
encoded_bytes_.WriteLEB128(non_spill_slot_bit_count);
bitmap->AppendAsBytesTo(&encoded_bytes_);
last_pc_offset_ = pc_offset;
}
CompressedStackMapsPtr CompressedStackMapsBuilder::Finalize() const {
if (encoded_bytes_.bytes_written() == 0) {
return Object::empty_compressed_stackmaps().raw();
}
return CompressedStackMaps::NewInlined(encoded_bytes_.buffer(),
encoded_bytes_.bytes_written());
}
ExceptionHandlersPtr ExceptionHandlerList::FinalizeExceptionHandlers(
uword entry_point) const {
intptr_t num_handlers = Length();
if (num_handlers == 0) {
return Object::empty_exception_handlers().raw();
}
const ExceptionHandlers& handlers =
ExceptionHandlers::Handle(ExceptionHandlers::New(num_handlers));
for (intptr_t i = 0; i < num_handlers; i++) {
// Assert that every element in the array has been initialized.
if (list_[i].handler_types == NULL) {
// Unreachable handler, entry not computed.
// Initialize it to some meaningful value.
const bool has_catch_all = false;
// Check it is uninitialized.
ASSERT((list_[i].outer_try_index == -1) &&
(list_[i].pc_offset == ExceptionHandlers::kInvalidPcOffset));
handlers.SetHandlerInfo(i, list_[i].outer_try_index, list_[i].pc_offset,
list_[i].needs_stacktrace, has_catch_all,
list_[i].is_generated);
handlers.SetHandledTypes(i, Array::empty_array());
} else {
const bool has_catch_all = ContainsCatchAllType(*list_[i].handler_types);
handlers.SetHandlerInfo(i, list_[i].outer_try_index, list_[i].pc_offset,
list_[i].needs_stacktrace, has_catch_all,
list_[i].is_generated);
handlers.SetHandledTypes(i, *list_[i].handler_types);
}
}
return handlers.raw();
}
#if !defined(DART_PRECOMPILED_RUNTIME)
class CatchEntryMovesMapBuilder::TrieNode : public ZoneAllocated {
public:
TrieNode() : move_(), entry_state_offset_(-1) {}
TrieNode(CatchEntryMove move, intptr_t index)
: move_(move), entry_state_offset_(index) {}
intptr_t Offset() { return entry_state_offset_; }
TrieNode* Insert(TrieNode* node) {
children_.Add(node);
return node;
}
TrieNode* Follow(CatchEntryMove next) {
for (intptr_t i = 0; i < children_.length(); i++) {
if (children_[i]->move_ == next) return children_[i];
}
return NULL;
}
private:
CatchEntryMove move_;
const intptr_t entry_state_offset_;
GrowableArray<TrieNode*> children_;
};
CatchEntryMovesMapBuilder::CatchEntryMovesMapBuilder()
: zone_(Thread::Current()->zone()),
root_(new TrieNode()),
current_pc_offset_(0),
stream_(zone_, 64) {}
void CatchEntryMovesMapBuilder::Append(const CatchEntryMove& move) {
moves_.Add(move);
}
void CatchEntryMovesMapBuilder::NewMapping(intptr_t pc_offset) {
moves_.Clear();
current_pc_offset_ = pc_offset;
}
void CatchEntryMovesMapBuilder::EndMapping() {
intptr_t suffix_length = 0;
TrieNode* suffix = root_;
// Find the largest common suffix, get the last node of the path.
for (intptr_t i = moves_.length() - 1; i >= 0; i--) {
TrieNode* n = suffix->Follow(moves_[i]);
if (n == NULL) break;
suffix_length++;
suffix = n;
}
intptr_t length = moves_.length() - suffix_length;
intptr_t current_offset = stream_.bytes_written();
typedef ZoneWriteStream::Raw<sizeof(intptr_t), intptr_t> Writer;
Writer::Write(&stream_, current_pc_offset_);
Writer::Write(&stream_, length);
Writer::Write(&stream_, suffix_length);
Writer::Write(&stream_, suffix->Offset());
// Write the unshared part, adding it to the trie.
TrieNode* node = suffix;
for (intptr_t i = length - 1; i >= 0; i--) {
moves_[i].WriteTo(&stream_);
TrieNode* child = new (zone_) TrieNode(moves_[i], current_offset);
node->Insert(child);
node = child;
}
}
TypedDataPtr CatchEntryMovesMapBuilder::FinalizeCatchEntryMovesMap() {
TypedData& td = TypedData::Handle(TypedData::New(
kTypedDataInt8ArrayCid, stream_.bytes_written(), Heap::kOld));
NoSafepointScope no_safepoint;
uint8_t* dest = reinterpret_cast<uint8_t*>(td.DataAddr(0));
uint8_t* src = stream_.buffer();
for (intptr_t i = 0; i < stream_.bytes_written(); i++) {
dest[i] = src[i];
}
return td.raw();
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
const TokenPosition& CodeSourceMapBuilder::kInitialPosition =
TokenPosition::kDartCodePrologue;
CodeSourceMapBuilder::CodeSourceMapBuilder(
Zone* zone,
bool stack_traces_only,
const GrowableArray<intptr_t>& caller_inline_id,
const GrowableArray<TokenPosition>& inline_id_to_token_pos,
const GrowableArray<const Function*>& inline_id_to_function)
: buffered_pc_offset_(0),
buffered_inline_id_stack_(),
buffered_token_pos_stack_(),
written_pc_offset_(0),
written_inline_id_stack_(),
written_token_pos_stack_(),
caller_inline_id_(caller_inline_id),
inline_id_to_token_pos_(inline_id_to_token_pos),
inline_id_to_function_(inline_id_to_function),
inlined_functions_(
GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld))),
stream_(zone, 64),
stack_traces_only_(stack_traces_only) {
buffered_inline_id_stack_.Add(0);
buffered_token_pos_stack_.Add(kInitialPosition);
written_inline_id_stack_.Add(0);
written_token_pos_stack_.Add(kInitialPosition);
}
void CodeSourceMapBuilder::FlushBuffer() {
FlushBufferStack();
FlushBufferPosition();
FlushBufferPC();
}
void CodeSourceMapBuilder::FlushBufferStack() {
for (intptr_t i = buffered_inline_id_stack_.length() - 1; i >= 0; i--) {
intptr_t buffered_id = buffered_inline_id_stack_[i];
if (i < written_inline_id_stack_.length()) {
intptr_t written_id = written_inline_id_stack_[i];
if (buffered_id == written_id) {
// i is the top-most position where the buffered and written stack
// match.
while (written_inline_id_stack_.length() > i + 1) {
WritePop();
}
for (intptr_t j = i + 1; j < buffered_inline_id_stack_.length(); j++) {
TokenPosition buffered_pos = buffered_token_pos_stack_[j - 1];
TokenPosition written_pos = written_token_pos_stack_[j - 1];
if (buffered_pos != written_pos) {
WriteChangePosition(buffered_pos);
}
WritePush(buffered_inline_id_stack_[j]);
}
return;
}
}
}
UNREACHABLE();
}
void CodeSourceMapBuilder::FlushBufferPosition() {
ASSERT(buffered_token_pos_stack_.length() ==
written_token_pos_stack_.length());
intptr_t top = buffered_token_pos_stack_.length() - 1;
TokenPosition buffered_pos = buffered_token_pos_stack_[top];
TokenPosition written_pos = written_token_pos_stack_[top];
if (buffered_pos != written_pos) {
WriteChangePosition(buffered_pos);
}
}
void CodeSourceMapBuilder::FlushBufferPC() {
if (buffered_pc_offset_ != written_pc_offset_) {
WriteAdvancePC(buffered_pc_offset_ - written_pc_offset_);
}
}
void CodeSourceMapBuilder::StartInliningInterval(int32_t pc_offset,
intptr_t inline_id) {
if (buffered_inline_id_stack_.Last() == inline_id) {
// No change in function stack.
return;
}
if (inline_id == -1) {
// Basic blocking missing an inline_id.
return;
}
if (!stack_traces_only_) {
FlushBuffer();
}
// Find a minimal set of pops and pushes to bring us to the new function
// stack.
// Pop to a common ancestor.
intptr_t common_parent = inline_id;
while (!IsOnBufferedStack(common_parent)) {
common_parent = caller_inline_id_[common_parent];
}
while (buffered_inline_id_stack_.Last() != common_parent) {
BufferPop();
}
// Push to the new top-of-stack function.
GrowableArray<intptr_t> to_push;
intptr_t id = inline_id;
while (id != common_parent) {
to_push.Add(id);
id = caller_inline_id_[id];
}
for (intptr_t i = to_push.length() - 1; i >= 0; i--) {
intptr_t callee_id = to_push[i];
TokenPosition call_token = TokenPosition::kNoSource;
if (callee_id != 0) {
// TODO(rmacnak): Should make this array line up with the others.
call_token = inline_id_to_token_pos_[callee_id - 1];
} else {
UNREACHABLE();
}
// Report caller as at the position of the call.
BufferChangePosition(call_token);
BufferPush(callee_id);
}
}
void CodeSourceMapBuilder::BeginCodeSourceRange(int32_t pc_offset) {}
void CodeSourceMapBuilder::EndCodeSourceRange(int32_t pc_offset,
TokenPosition pos) {
if (pc_offset == buffered_pc_offset_) {
return; // Empty intermediate instruction.
}
if (pos != buffered_token_pos_stack_.Last()) {
if (!stack_traces_only_) {
FlushBuffer();
}
BufferChangePosition(pos);
}
BufferAdvancePC(pc_offset - buffered_pc_offset_);
}
void CodeSourceMapBuilder::NoteDescriptor(PcDescriptorsLayout::Kind kind,
int32_t pc_offset,
TokenPosition pos) {
const uint8_t kCanThrow =
PcDescriptorsLayout::kIcCall | PcDescriptorsLayout::kUnoptStaticCall |
PcDescriptorsLayout::kRuntimeCall | PcDescriptorsLayout::kOther;
if ((kind & kCanThrow) != 0) {
BufferChangePosition(pos);
BufferAdvancePC(pc_offset - buffered_pc_offset_);
FlushBuffer();
}
}
void CodeSourceMapBuilder::NoteNullCheck(int32_t pc_offset,
TokenPosition pos,
intptr_t name_index) {
BufferChangePosition(pos);
BufferAdvancePC(pc_offset - buffered_pc_offset_);
FlushBuffer();
WriteNullCheck(name_index);
}
intptr_t CodeSourceMapBuilder::GetFunctionId(intptr_t inline_id) {
const Function& function = *inline_id_to_function_[inline_id];
for (intptr_t i = 0; i < inlined_functions_.Length(); i++) {
if (inlined_functions_.At(i) == function.raw()) {
return i;
}
}
RELEASE_ASSERT(!function.IsNull());
inlined_functions_.Add(function, Heap::kOld);
return inlined_functions_.Length() - 1;
}
ArrayPtr CodeSourceMapBuilder::InliningIdToFunction() {
if (inlined_functions_.Length() == 0) {
return Object::empty_array().raw();
}
return Array::MakeFixedLength(inlined_functions_);
}
CodeSourceMapPtr CodeSourceMapBuilder::Finalize() {
if (!stack_traces_only_) {
FlushBuffer();
}
intptr_t length = stream_.bytes_written();
const CodeSourceMap& map = CodeSourceMap::Handle(CodeSourceMap::New(length));
NoSafepointScope no_safepoint;
memmove(map.Data(), stream_.buffer(), length);
return map.raw();
}
void CodeSourceMapBuilder::WriteChangePosition(TokenPosition pos) {
stream_.Write<uint8_t>(kChangePosition);
intptr_t position_or_line = pos.Serialize();
#if defined(DART_PRECOMPILER)
intptr_t column = TokenPosition::kNoSource.Serialize();
if (FLAG_precompiled_mode) {
// Don't use the raw position value directly in precompiled mode. Instead,
// use the value of kNoSource as a fallback when no line or column
// information is found.
position_or_line = TokenPosition::kNoSource.Serialize();
intptr_t inline_id = buffered_inline_id_stack_.Last();
if (inline_id < inline_id_to_function_.length()) {
const Function* function = inline_id_to_function_[inline_id];
Script& script = Script::Handle(function->script());
script.GetTokenLocation(pos, &position_or_line, &column);
}
}
#endif
stream_.Write<int32_t>(position_or_line);
#if defined(DART_PRECOMPILER)
// For non-symbolic stack traces, the CodeSourceMaps are not serialized,
// so we need not worry about increasing snapshot size by including more
// information here.
if (FLAG_dwarf_stack_traces_mode) {
stream_.Write<int32_t>(column);
}
#endif
written_token_pos_stack_.Last() = pos;
}
void CodeSourceMapReader::GetInlinedFunctionsAt(
int32_t pc_offset,
GrowableArray<const Function*>* function_stack,
GrowableArray<TokenPosition>* token_positions) {
function_stack->Clear();
token_positions->Clear();
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
function_stack->Add(&root_);
token_positions->Add(CodeSourceMapBuilder::kInitialPosition);
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
(*token_positions)[token_positions->length() - 1] =
ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
current_pc_offset += delta;
if (current_pc_offset > pc_offset) {
return;
}
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func = stream.Read<int32_t>();
function_stack->Add(
&Function::Handle(Function::RawCast(functions_.At(func))));
token_positions->Add(CodeSourceMapBuilder::kInitialPosition);
break;
}
case CodeSourceMapBuilder::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 CodeSourceMapBuilder::kNullCheck: {
stream.Read<int32_t>();
break;
}
default:
UNREACHABLE();
}
}
}
#ifndef PRODUCT
void CodeSourceMapReader::PrintJSONInlineIntervals(JSONObject* jsobj) {
{
JSONArray inlined_functions(jsobj, "_inlinedFunctions");
Function& function = Function::Handle();
for (intptr_t i = 0; i < functions_.Length(); i++) {
function ^= functions_.At(i);
ASSERT(!function.IsNull());
inlined_functions.AddValue(function);
}
}
GrowableArray<intptr_t> function_stack;
JSONArray inline_intervals(jsobj, "_inlinedIntervals");
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
function_stack.Add(0);
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
// Format: [start, end, inline functions...]
JSONArray inline_interval(&inline_intervals);
inline_interval.AddValue(static_cast<intptr_t>(current_pc_offset));
inline_interval.AddValue(
static_cast<intptr_t>(current_pc_offset + delta - 1));
for (intptr_t i = 0; i < function_stack.length(); i++) {
inline_interval.AddValue(function_stack[i]);
}
current_pc_offset += delta;
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func = stream.Read<int32_t>();
function_stack.Add(func);
break;
}
case CodeSourceMapBuilder::kPopFunction: {
// We never pop the root function.
ASSERT(function_stack.length() > 1);
function_stack.RemoveLast();
break;
}
case CodeSourceMapBuilder::kNullCheck: {
stream.Read<int32_t>();
break;
}
default:
UNREACHABLE();
}
}
}
#endif // !PRODUCT
void CodeSourceMapReader::DumpInlineIntervals(uword start) {
GrowableArray<const Function*> function_stack;
LogBlock lb;
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
function_stack.Add(&root_);
THR_Print("Inline intervals for function '%s' {\n",
root_.ToFullyQualifiedCString());
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
start + current_pc_offset + delta - 1);
for (intptr_t i = 0; i < function_stack.length(); i++) {
THR_Print("%s ", function_stack[i]->ToCString());
}
THR_Print("\n");
current_pc_offset += delta;
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func = stream.Read<int32_t>();
function_stack.Add(
&Function::Handle(Function::RawCast(functions_.At(func))));
break;
}
case CodeSourceMapBuilder::kPopFunction: {
// We never pop the root function.
ASSERT(function_stack.length() > 1);
function_stack.RemoveLast();
break;
}
case CodeSourceMapBuilder::kNullCheck: {
stream.Read<int32_t>();
break;
}
default:
UNREACHABLE();
}
}
THR_Print("}\n");
}
void CodeSourceMapReader::DumpSourcePositions(uword start) {
GrowableArray<const Function*> function_stack;
GrowableArray<TokenPosition> token_positions;
LogBlock lb;
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
function_stack.Add(&root_);
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
THR_Print("Source positions for function '%s' {\n",
root_.ToFullyQualifiedCString());
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
token_positions[token_positions.length() - 1] = ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
start + current_pc_offset + delta - 1);
for (intptr_t i = 0; i < function_stack.length(); i++) {
THR_Print("%s@%s", function_stack[i]->ToCString(),
token_positions[i].ToCString());
}
THR_Print("\n");
current_pc_offset += delta;
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func = stream.Read<int32_t>();
function_stack.Add(
&Function::Handle(Function::RawCast(functions_.At(func))));
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
break;
}
case CodeSourceMapBuilder::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 CodeSourceMapBuilder::kNullCheck: {
const intptr_t name_index = stream.Read<int32_t>();
THR_Print("%" Px "-%" Px ": null check PP#%" Pd "\n",
start + current_pc_offset, start + current_pc_offset,
name_index);
break;
}
default:
UNREACHABLE();
}
}
THR_Print("}\n");
}
intptr_t CodeSourceMapReader::GetNullCheckNameIndexAt(int32_t pc_offset) {
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
current_pc_offset += delta;
RELEASE_ASSERT(current_pc_offset <= pc_offset);
break;
}
case CodeSourceMapBuilder::kPushFunction: {
stream.Read<int32_t>();
break;
}
case CodeSourceMapBuilder::kPopFunction: {
break;
}
case CodeSourceMapBuilder::kNullCheck: {
const int32_t name_index = stream.Read<int32_t>();
if (current_pc_offset == pc_offset) {
return name_index;
}
break;
}
default:
UNREACHABLE();
}
}
UNREACHABLE();
return -1;
}
TokenPosition CodeSourceMapReader::ReadPosition(ReadStream* stream) {
const TokenPosition line =
TokenPosition::Deserialize(stream->Read<int32_t>());
#if defined(DART_PRECOMPILER)
// The special handling for non-symbolic stack trace mode only needs to
// happen in the precompiler, because those CSMs are not serialized in
// precompiled snapshots.
if (FLAG_dwarf_stack_traces_mode) {
stream->Read<int32_t>(); // Discard the column information.
}
#endif
return line;
}
} // namespace dart