[vm/bytecode] Lookup source and line starts for bytecode scripts specifying uri only.

Collect token positions of field declared in bytecode.
Improve debugger debugging: Disassemble both bytecode and code when applicable.

Change-Id: I56188cf24ef4bfe8a93cce11c2f4505152abdd02
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/112680
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Régis Crelier 2019-08-22 18:31:18 +00:00 committed by commit-bot@chromium.org
parent 0176526799
commit 223724fa76
5 changed files with 154 additions and 118 deletions

View file

@ -1944,6 +1944,13 @@ RawScript* BytecodeReaderHelper::ReadSourceFile(const String& uri,
source = ReadString(/* is_canonical = */ false);
}
if (source.IsNull() && line_starts.IsNull()) {
// This script provides a uri only, but no source or line_starts array.
// The source is set to the empty symbol, indicating that source and
// line_starts array are lazily looked up if needed.
source = Symbols::Empty().raw(); // Lookup is postponed.
}
const Script& script = Script::Handle(
Z, Script::New(import_uri, uri, source, RawScript::kKernelTag));
script.set_line_starts(line_starts);

View file

@ -10,6 +10,7 @@
#include "vm/code_patcher.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
#include "vm/compiler/frontend/bytecode_reader.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/dart_entry.h"
@ -745,16 +746,16 @@ void ActivationFrame::PrintDescriptorsError(const char* message) {
OS::PrintErr("pc_ %" Px "\n", pc_);
OS::PrintErr("deopt_id_ %" Px "\n", deopt_id_);
OS::PrintErr("context_level_ %" Px "\n", context_level_);
DisassembleToStdout formatter;
OS::PrintErr("token_pos_ %s\n", token_pos_.ToCString());
if (function().is_declared_in_bytecode()) {
#if !defined(DART_PRECOMPILED_RUNTIME)
ASSERT(function().HasBytecode());
const Bytecode& bytecode = Bytecode::Handle(function().bytecode());
bytecode.Disassemble(&formatter);
KernelBytecodeDisassembler::Disassemble(function());
#else
UNREACHABLE();
#endif // !defined(DART_PRECOMPILED_RUNTIME)
} else {
}
if (!IsInterpreted()) {
DisassembleToStdout formatter;
code().Disassemble(&formatter);
PcDescriptors::Handle(code().pc_descriptors()).Print();
}
@ -827,7 +828,7 @@ intptr_t ActivationFrame::ContextLevel() {
}
}
if (!found) {
PrintDescriptorsError("Missing context level");
PrintDescriptorsError("Missing context level in var descriptors");
}
ASSERT(context_level_ >= 0);
}

View file

@ -326,9 +326,9 @@ static void CollectBytecodeFunctionTokenPositions(
}
void CollectTokenPositionsFor(const Script& interesting_script) {
ASSERT(interesting_script.url() != String::null());
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
interesting_script.LookupSourceAndLineStarts(zone);
TranslationHelper helper(thread);
helper.InitFromScript(interesting_script);
@ -383,6 +383,8 @@ void CollectTokenPositionsFor(const Script& interesting_script) {
continue;
}
if (temp_field.is_declared_in_bytecode()) {
token_positions.Add(temp_field.token_pos().value());
token_positions.Add(temp_field.end_token_pos().value());
if (temp_field.is_static() && temp_field.has_initializer()) {
temp_function = temp_field.EnsureInitializerFunction();
CollectBytecodeFunctionTokenPositions(temp_function,
@ -463,6 +465,8 @@ void CollectTokenPositionsFor(const Script& interesting_script) {
continue;
}
if (field.is_declared_in_bytecode()) {
token_positions.Add(field.token_pos().value());
token_positions.Add(field.end_token_pos().value());
if (field.is_static() && field.has_initializer()) {
temp_function = field.EnsureInitializerFunction();
CollectBytecodeFunctionTokenPositions(temp_function,

View file

@ -7971,7 +7971,7 @@ RawString* Function::GetSource() const {
// (3) "var foo = () => null;": End token is `;', but in this case the
// token semicolon belongs to the assignment so we skip it.
const String& src = String::Handle(func_script.Source());
if (src.IsNull()) {
if (src.IsNull() || src.Length() == 0) {
return Symbols::OptimizedOut().raw();
}
uint16_t end_char = src.CharAt(end_token_pos().value());
@ -9370,63 +9370,93 @@ RawTypedData* Script::kernel_string_offsets() const {
return program_info.string_offsets();
}
void Script::LookupSourceAndLineStarts(Zone* zone) const {
#if !defined(DART_PRECOMPILED_RUNTIME)
if (Source() == String::null()) {
// This is a script without source info.
return;
}
const String& uri = String::Handle(zone, resolved_url());
ASSERT(uri.IsSymbol());
if (uri.Length() > 0 && Source() == Symbols::Empty().raw()) {
// Entry included only to provide URI - actual source should already exist
// in the VM, so try to find it.
Library& lib = Library::Handle(zone);
Script& script = Script::Handle(zone);
const GrowableObjectArray& libs = GrowableObjectArray::Handle(
zone, Isolate::Current()->object_store()->libraries());
for (intptr_t i = 0; i < libs.Length(); i++) {
lib ^= libs.At(i);
script = lib.LookupScript(uri, /* useResolvedUri = */ true);
if (!script.IsNull() && script.kind() == RawScript::kKernelTag &&
script.Source() != Symbols::Empty().raw()) {
set_source(String::Handle(zone, script.Source()));
set_line_starts(TypedData::Handle(zone, script.line_starts()));
// Note that we may find a script without source info (null source).
// We will not repeat the lookup in this case.
return;
}
}
set_source(Object::null_string());
// No script found. Set source to null to prevent further lookup.
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
}
RawGrowableObjectArray* Script::GenerateLineNumberArray() const {
ASSERT(kind() == RawScript::kKernelTag);
Zone* zone = Thread::Current()->zone();
const GrowableObjectArray& info =
GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
const Object& line_separator = Object::Handle(zone);
if (kind() == RawScript::kKernelTag) {
const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
if (line_starts_data.IsNull()) {
// Scripts in the AOT snapshot do not have a line starts array.
// A well-formed line number array has a leading null.
info.Add(line_separator); // New line.
return info.raw();
}
#if !defined(DART_PRECOMPILED_RUNTIME)
Smi& value = Smi::Handle(zone);
intptr_t line_count = line_starts_data.Length();
const Array& debug_positions_array = Array::Handle(debug_positions());
intptr_t token_count = debug_positions_array.Length();
int token_index = 0;
kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
intptr_t previous_start = 0;
for (int line_index = 0; line_index < line_count; ++line_index) {
intptr_t start = previous_start + line_starts_reader.DeltaAt(line_index);
// Output the rest of the tokens if we have no next line.
intptr_t end = TokenPosition::kMaxSourcePos;
if (line_index + 1 < line_count) {
end = start + line_starts_reader.DeltaAt(line_index + 1);
}
bool first = true;
while (token_index < token_count) {
value ^= debug_positions_array.At(token_index);
intptr_t debug_position = value.Value();
if (debug_position >= end) break;
if (first) {
info.Add(line_separator); // New line.
value = Smi::New(line_index + 1); // Line number.
info.Add(value);
first = false;
}
value ^= debug_positions_array.At(token_index);
info.Add(value); // Token position.
value = Smi::New(debug_position - start + 1); // Column.
info.Add(value);
++token_index;
}
previous_start = start;
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
LookupSourceAndLineStarts(zone);
if (line_starts() == TypedData::null()) {
// Scripts in the AOT snapshot do not have a line starts array.
// Neither do some scripts coming from bytecode.
// A well-formed line number array has a leading null.
info.Add(line_separator); // New line.
return info.raw();
}
#if !defined(DART_PRECOMPILED_RUNTIME)
Smi& value = Smi::Handle(zone);
const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
intptr_t line_count = line_starts_data.Length();
const Array& debug_positions_array = Array::Handle(debug_positions());
intptr_t token_count = debug_positions_array.Length();
int token_index = 0;
UNREACHABLE();
return GrowableObjectArray::null();
kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
intptr_t previous_start = 0;
for (int line_index = 0; line_index < line_count; ++line_index) {
intptr_t start = previous_start + line_starts_reader.DeltaAt(line_index);
// Output the rest of the tokens if we have no next line.
intptr_t end = TokenPosition::kMaxSourcePos;
if (line_index + 1 < line_count) {
end = start + line_starts_reader.DeltaAt(line_index + 1);
}
bool first = true;
while (token_index < token_count) {
value ^= debug_positions_array.At(token_index);
intptr_t debug_position = value.Value();
if (debug_position >= end) break;
if (first) {
info.Add(line_separator); // New line.
value = Smi::New(line_index + 1); // Line number.
info.Add(value);
first = false;
}
value ^= debug_positions_array.At(token_index);
info.Add(value); // Token position.
value = Smi::New(debug_position - start + 1); // Column.
info.Add(value);
++token_index;
}
previous_start = start;
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
return info.raw();
}
const char* Script::GetKindAsCString() const {
@ -9580,80 +9610,76 @@ void Script::GetTokenLocation(TokenPosition token_pos,
ASSERT(line != NULL);
Zone* zone = Thread::Current()->zone();
if (kind() == RawScript::kKernelTag) {
const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
if (line_starts_data.IsNull()) {
// Scripts in the AOT snapshot do not have a line starts array.
*line = -1;
if (column != NULL) {
*column = -1;
}
if (token_len != NULL) {
*token_len = 1;
}
return;
ASSERT(kind() == RawScript::kKernelTag);
LookupSourceAndLineStarts(zone);
if (line_starts() == TypedData::null()) {
// Scripts in the AOT snapshot do not have a line starts array.
// Neither do some scripts coming from bytecode.
*line = -1;
if (column != NULL) {
*column = -1;
}
#if !defined(DART_PRECOMPILED_RUNTIME)
ASSERT(line_starts_data.Length() > 0);
kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
line_starts_reader.LocationForPosition(token_pos.value(), line, column);
if (token_len != NULL) {
*token_len = 1;
// We don't explicitly save this data: Load the source
// and find it from there.
const String& source = String::Handle(zone, Source());
if (!source.IsNull()) {
intptr_t offset = token_pos.value();
if (offset < source.Length() &&
IsIdentStartChar(source.CharAt(offset))) {
for (intptr_t i = offset + 1;
i < source.Length() && IsIdentChar(source.CharAt(i)); ++i) {
++*token_len;
}
}
return;
}
#if !defined(DART_PRECOMPILED_RUNTIME)
const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
line_starts_reader.LocationForPosition(token_pos.value(), line, column);
if (token_len != NULL) {
*token_len = 1;
// We don't explicitly save this data: Load the source
// and find it from there.
const String& source = String::Handle(zone, Source());
if (!source.IsNull()) {
intptr_t offset = token_pos.value();
if (offset < source.Length() && IsIdentStartChar(source.CharAt(offset))) {
for (intptr_t i = offset + 1;
i < source.Length() && IsIdentChar(source.CharAt(i)); ++i) {
++*token_len;
}
}
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
return;
}
UNREACHABLE();
#endif // !defined(DART_PRECOMPILED_RUNTIME)
}
void Script::TokenRangeAtLine(intptr_t line_number,
TokenPosition* first_token_index,
TokenPosition* last_token_index) const {
ASSERT(kind() == RawScript::kKernelTag);
ASSERT(first_token_index != NULL && last_token_index != NULL);
ASSERT(line_number > 0);
if (kind() == RawScript::kKernelTag) {
const TypedData& line_starts_data = TypedData::Handle(line_starts());
if (line_starts_data.IsNull()) {
// Scripts in the AOT snapshot do not have a line starts array.
*first_token_index = TokenPosition::kNoSource;
*last_token_index = TokenPosition::kNoSource;
return;
}
#if !defined(DART_PRECOMPILED_RUNTIME)
const String& source = String::Handle(Source());
intptr_t source_length;
if (source.IsNull()) {
Smi& value = Smi::Handle();
const Array& debug_positions_array = Array::Handle(debug_positions());
value ^= debug_positions_array.At(debug_positions_array.Length() - 1);
source_length = value.Value();
} else {
source_length = source.Length();
}
kernel::KernelLineStartsReader line_starts_reader(
line_starts_data, Thread::Current()->zone());
line_starts_reader.TokenRangeAtLine(source_length, line_number,
first_token_index, last_token_index);
#endif // !defined(DART_PRECOMPILED_RUNTIME)
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
LookupSourceAndLineStarts(zone);
if (line_starts() == TypedData::null()) {
// Scripts in the AOT snapshot do not have a line starts array.
// Neither do some scripts coming from bytecode.
*first_token_index = TokenPosition::kNoSource;
*last_token_index = TokenPosition::kNoSource;
return;
}
UNREACHABLE();
#if !defined(DART_PRECOMPILED_RUNTIME)
const String& source = String::Handle(zone, Source());
intptr_t source_length;
if (source.IsNull()) {
Smi& value = Smi::Handle(zone);
const Array& debug_positions_array = Array::Handle(zone, debug_positions());
value ^= debug_positions_array.At(debug_positions_array.Length() - 1);
source_length = value.Value();
} else {
source_length = source.Length();
}
const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
kernel::KernelLineStartsReader line_starts_reader(line_starts_data,
Thread::Current()->zone());
line_starts_reader.TokenRangeAtLine(source_length, line_number,
first_token_index, last_token_index);
#endif // !defined(DART_PRECOMPILED_RUNTIME)
}
RawString* Script::GetLine(intptr_t line_number, Heap::Space space) const {

View file

@ -874,10 +874,7 @@ class Class : public Object {
TokenPosition token_pos() const { return raw_ptr()->token_pos_; }
void set_token_pos(TokenPosition value) const;
TokenPosition end_token_pos() const {
ASSERT(is_declaration_loaded());
return raw_ptr()->end_token_pos_;
}
TokenPosition end_token_pos() const { return raw_ptr()->end_token_pos_; }
void set_end_token_pos(TokenPosition value) const;
int32_t SourceFingerprint() const;
@ -3790,6 +3787,7 @@ class Script : public Object {
RawString* Source() const;
bool IsPartOfDartColonLibrary() const;
void LookupSourceAndLineStarts(Zone* zone) const;
RawGrowableObjectArray* GenerateLineNumberArray() const;
RawScript::Kind kind() const {
return static_cast<RawScript::Kind>(raw_ptr()->kind_);