Skip to content

Commit f9860f9

Browse files
alexmarkovCommit Queue
authored andcommitted
[dyn_modules] Add source positions to bytecode
dart2bytecode can optionally add source position information (including line starts) to the generated bytecode (when '--bytecode-options=source-positions' flag is specified). If bytecode has source positions, they are now shown in stack traces involving interpreter frames. TEST=ci Change-Id: I1ae3326bac21201040be32c712514e71e96f51e2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/433760 Reviewed-by: Slava Egorov <[email protected]> Commit-Queue: Alexander Markov <[email protected]>
1 parent 307c3c6 commit f9860f9

File tree

12 files changed

+243
-168
lines changed

12 files changed

+243
-168
lines changed

pkg/dart2bytecode/lib/bytecode_generator.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,13 @@ class BytecodeGenerator extends RecursiveVisitor {
649649
int endPosition = TreeNode.noOffset;
650650
if (options.emitSourcePositions && member.fileOffset != TreeNode.noOffset) {
651651
flags |= FunctionDeclaration.hasSourcePositionsFlag;
652-
position = (member as dynamic).startFileOffset;
652+
if (member is Constructor) {
653+
position = member.startFileOffset;
654+
} else if (member is Procedure) {
655+
position = member.fileStartOffset;
656+
} else {
657+
throw 'Unexpected ${member.runtimeType} $member';
658+
}
653659
endPosition = member.fileEndOffset;
654660
}
655661
final Annotations annotations = getFunctionAnnotations(member);

pkg/test_runner/lib/src/compiler_configuration.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,6 +1615,7 @@ class BytecodeCompilerConfiguration extends CompilerConfiguration {
16151615
arguments.contains('--enable-asserts') ||
16161616
arguments.contains('--enable_asserts'))
16171617
'--enable-asserts',
1618+
if (!isProductMode) '--bytecode-options=source-positions',
16181619
];
16191620

16201621
return CompilationCommand(

runtime/vm/bytecode_reader.cc

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,10 @@ ObjectPtr BytecodeReaderHelper::ReadObjectContents(uint32_t header) {
10411041
}
10421042
case kScript: {
10431043
const String& uri = String::CheckedHandle(Z, ReadObject());
1044-
RELEASE_ASSERT((flags & kFlagHasSourceFile) == 0);
1044+
if ((flags & kFlagHasSourceFile) != 0) {
1045+
return ReadSourceFile(uri, bytecode_component_->GetSourceFilesOffset() +
1046+
reader_.ReadUInt());
1047+
}
10451048
return Script::New(uri, Object::null_string());
10461049
}
10471050
case kType: {
@@ -1466,6 +1469,72 @@ StringPtr BytecodeReaderHelper::ReadString(bool is_canonical) {
14661469
}
14671470
}
14681471

1472+
TypedDataPtr BytecodeReaderHelper::ReadLineStartsData(
1473+
intptr_t line_starts_offset) {
1474+
AlternativeReadingScope alt(&reader_, line_starts_offset);
1475+
1476+
const intptr_t num_line_starts = reader_.ReadUInt();
1477+
1478+
// Choose representation between Uint16 and Uint32 typed data.
1479+
intptr_t max_start = 0;
1480+
{
1481+
AlternativeReadingScope alt2(&reader_, reader_.offset());
1482+
for (intptr_t i = 0; i < num_line_starts; ++i) {
1483+
const intptr_t delta = reader_.ReadUInt();
1484+
max_start += delta;
1485+
}
1486+
}
1487+
1488+
const intptr_t cid = (max_start <= kMaxUint16) ? kTypedDataUint16ArrayCid
1489+
: kTypedDataUint32ArrayCid;
1490+
const TypedData& line_starts_data =
1491+
TypedData::Handle(Z, TypedData::New(cid, num_line_starts, Heap::kOld));
1492+
1493+
intptr_t current_start = 0;
1494+
for (intptr_t i = 0; i < num_line_starts; ++i) {
1495+
const intptr_t delta = reader_.ReadUInt();
1496+
current_start += delta;
1497+
if (cid == kTypedDataUint16ArrayCid) {
1498+
line_starts_data.SetUint16(i << 1, static_cast<uint16_t>(current_start));
1499+
} else {
1500+
line_starts_data.SetUint32(i << 2, current_start);
1501+
}
1502+
}
1503+
1504+
return line_starts_data.ptr();
1505+
}
1506+
1507+
ScriptPtr BytecodeReaderHelper::ReadSourceFile(const String& uri,
1508+
intptr_t offset) {
1509+
// SourceFile flags, must be in sync with SourceFile constants in
1510+
// pkg/dart2bytecode/lib/declarations.dart.
1511+
const int kHasLineStartsFlag = 1 << 0;
1512+
const int kHasSourceFlag = 1 << 1;
1513+
1514+
AlternativeReadingScope alt(&reader_, offset);
1515+
1516+
const intptr_t flags = reader_.ReadUInt();
1517+
const String& import_uri = String::CheckedHandle(Z, ReadObject());
1518+
1519+
TypedData& line_starts = TypedData::Handle(Z);
1520+
if ((flags & kHasLineStartsFlag) != 0) {
1521+
const intptr_t line_starts_offset =
1522+
bytecode_component_->GetLineStartsOffset() + reader_.ReadUInt();
1523+
line_starts = ReadLineStartsData(line_starts_offset);
1524+
}
1525+
1526+
String& source = String::Handle(Z);
1527+
if ((flags & kHasSourceFlag) != 0) {
1528+
source = ReadString(/* is_canonical = */ false);
1529+
}
1530+
1531+
const Script& script =
1532+
Script::Handle(Z, Script::New(import_uri, uri, source));
1533+
script.set_line_starts(line_starts);
1534+
1535+
return script.ptr();
1536+
}
1537+
14691538
TypeArgumentsPtr BytecodeReaderHelper::ReadTypeArguments() {
14701539
const intptr_t length = reader_.ReadUInt();
14711540
TypeArguments& type_arguments =

runtime/vm/bytecode_reader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ class BytecodeReaderHelper : public ValueObject {
346346
ObjectPtr ReadConstObject(intptr_t tag);
347347
ObjectPtr ReadType(intptr_t tag, Nullability nullability);
348348
StringPtr ReadString(bool is_canonical = true);
349+
TypedDataPtr ReadLineStartsData(intptr_t line_starts_offset);
350+
ScriptPtr ReadSourceFile(const String& uri, intptr_t offset);
349351
TypeArgumentsPtr ReadTypeArguments();
350352
void SetupFieldAccessorFunction(const Class& klass,
351353
const Function& function,

runtime/vm/kernel.cc

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -18,87 +18,6 @@
1818
namespace dart {
1919
namespace kernel {
2020

21-
KernelLineStartsReader::KernelLineStartsReader(
22-
const dart::TypedData& line_starts_data,
23-
dart::Zone* zone)
24-
: line_starts_data_(line_starts_data) {
25-
TypedDataElementType type = line_starts_data_.ElementType();
26-
if (type == kUint16ArrayElement) {
27-
helper_ = new KernelUint16LineStartsHelper();
28-
} else if (type == kUint32ArrayElement) {
29-
helper_ = new KernelUint32LineStartsHelper();
30-
} else {
31-
UNREACHABLE();
32-
}
33-
}
34-
35-
uint32_t KernelLineStartsReader::MaxPosition() const {
36-
const intptr_t line_count = line_starts_data_.Length();
37-
if (line_count == 0) {
38-
return 0;
39-
}
40-
return helper_->At(line_starts_data_, line_count - 1);
41-
}
42-
43-
bool KernelLineStartsReader::LocationForPosition(intptr_t position,
44-
intptr_t* line,
45-
intptr_t* col) const {
46-
const intptr_t line_count = line_starts_data_.Length();
47-
if (position < 0 || static_cast<uint32_t>(position) > MaxPosition() ||
48-
line_count == 0) {
49-
return false;
50-
}
51-
52-
intptr_t lo = 0;
53-
intptr_t hi = line_count;
54-
while (hi > lo + 1) {
55-
const intptr_t mid = lo + (hi - lo) / 2;
56-
const intptr_t mid_position = helper_->At(line_starts_data_, mid);
57-
if (mid_position > position) {
58-
hi = mid;
59-
} else {
60-
lo = mid;
61-
}
62-
}
63-
*line = lo + 1;
64-
if (col != nullptr) {
65-
*col = position - helper_->At(line_starts_data_, lo) + 1;
66-
}
67-
68-
return true;
69-
}
70-
71-
bool KernelLineStartsReader::TokenRangeAtLine(
72-
intptr_t line_number,
73-
TokenPosition* first_token_index,
74-
TokenPosition* last_token_index) const {
75-
const intptr_t line_count = line_starts_data_.Length();
76-
if (line_number <= 0 || line_number > line_count) {
77-
return false;
78-
}
79-
*first_token_index = dart::TokenPosition::Deserialize(
80-
helper_->At(line_starts_data_, line_number - 1));
81-
if (line_number == line_count) {
82-
*last_token_index = *first_token_index;
83-
} else {
84-
*last_token_index = dart::TokenPosition::Deserialize(
85-
helper_->At(line_starts_data_, line_number) - 1);
86-
}
87-
return true;
88-
}
89-
90-
uint32_t KernelLineStartsReader::KernelUint16LineStartsHelper::At(
91-
const dart::TypedData& data,
92-
intptr_t index) const {
93-
return data.GetUint16(index << 1);
94-
}
95-
96-
uint32_t KernelLineStartsReader::KernelUint32LineStartsHelper::At(
97-
const dart::TypedData& data,
98-
intptr_t index) const {
99-
return data.GetUint32(index << 2);
100-
}
101-
10221
class KernelTokenPositionCollector : public KernelReaderHelper {
10322
public:
10423
KernelTokenPositionCollector(

runtime/vm/kernel.h

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -127,70 +127,6 @@ class Program {
127127
DISALLOW_COPY_AND_ASSIGN(Program);
128128
};
129129

130-
class KernelLineStartsReader {
131-
public:
132-
KernelLineStartsReader(const dart::TypedData& line_starts_data,
133-
dart::Zone* zone);
134-
135-
~KernelLineStartsReader() { delete helper_; }
136-
137-
uint32_t At(intptr_t index) const {
138-
return helper_->At(line_starts_data_, index);
139-
}
140-
141-
uint32_t MaxPosition() const;
142-
143-
// Returns whether the given offset corresponds to a valid source offset
144-
// If it does, then *line and *column (if column is not nullptr) are set
145-
// to the line and column the token starts at.
146-
DART_WARN_UNUSED_RESULT bool LocationForPosition(
147-
intptr_t position,
148-
intptr_t* line,
149-
intptr_t* col = nullptr) const;
150-
151-
// Returns whether any tokens were found for the given line. When found,
152-
// *first_token_index and *last_token_index are set to the first and
153-
// last token on the line, respectively.
154-
DART_WARN_UNUSED_RESULT bool TokenRangeAtLine(
155-
intptr_t line_number,
156-
dart::TokenPosition* first_token_index,
157-
dart::TokenPosition* last_token_index) const;
158-
159-
private:
160-
class KernelLineStartsHelper {
161-
public:
162-
KernelLineStartsHelper() {}
163-
virtual ~KernelLineStartsHelper() {}
164-
virtual uint32_t At(const dart::TypedData& data, intptr_t index) const = 0;
165-
166-
private:
167-
DISALLOW_COPY_AND_ASSIGN(KernelLineStartsHelper);
168-
};
169-
170-
class KernelUint16LineStartsHelper : public KernelLineStartsHelper {
171-
public:
172-
KernelUint16LineStartsHelper() {}
173-
virtual uint32_t At(const dart::TypedData& data, intptr_t index) const;
174-
175-
private:
176-
DISALLOW_COPY_AND_ASSIGN(KernelUint16LineStartsHelper);
177-
};
178-
179-
class KernelUint32LineStartsHelper : public KernelLineStartsHelper {
180-
public:
181-
KernelUint32LineStartsHelper() {}
182-
virtual uint32_t At(const dart::TypedData& data, intptr_t index) const;
183-
184-
private:
185-
DISALLOW_COPY_AND_ASSIGN(KernelUint32LineStartsHelper);
186-
};
187-
188-
const dart::TypedData& line_starts_data_;
189-
KernelLineStartsHelper* helper_;
190-
191-
DISALLOW_COPY_AND_ASSIGN(KernelLineStartsReader);
192-
};
193-
194130
ObjectPtr EvaluateStaticConstFieldInitializer(const Field& field);
195131
ObjectPtr EvaluateMetadata(const Library& library,
196132
intptr_t kernel_offset,

runtime/vm/line_starts_reader.cc

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
6+
7+
#include "vm/line_starts_reader.h"
8+
#include "vm/object.h"
9+
#include "vm/token_position.h"
10+
11+
namespace dart {
12+
13+
LineStartsReader::LineStartsReader(const dart::TypedData& line_starts_data)
14+
: line_starts_data_(line_starts_data),
15+
element_type_(line_starts_data.ElementType()) {
16+
RELEASE_ASSERT((element_type_ == kUint16ArrayElement) ||
17+
(element_type_ == kUint32ArrayElement));
18+
}
19+
20+
uint32_t LineStartsReader::MaxPosition() const {
21+
const intptr_t line_count = line_starts_data_.Length();
22+
if (line_count == 0) {
23+
return 0;
24+
}
25+
return At(line_count - 1);
26+
}
27+
28+
bool LineStartsReader::LocationForPosition(intptr_t position,
29+
intptr_t* line,
30+
intptr_t* col) const {
31+
const intptr_t line_count = line_starts_data_.Length();
32+
if (position < 0 || static_cast<uint32_t>(position) > MaxPosition() ||
33+
line_count == 0) {
34+
return false;
35+
}
36+
37+
intptr_t lo = 0;
38+
intptr_t hi = line_count;
39+
while (hi > lo + 1) {
40+
const intptr_t mid = lo + (hi - lo) / 2;
41+
const intptr_t mid_position = At(mid);
42+
if (mid_position > position) {
43+
hi = mid;
44+
} else {
45+
lo = mid;
46+
}
47+
}
48+
*line = lo + 1;
49+
if (col != nullptr) {
50+
*col = position - At(lo) + 1;
51+
}
52+
53+
return true;
54+
}
55+
56+
bool LineStartsReader::TokenRangeAtLine(intptr_t line_number,
57+
TokenPosition* first_token_index,
58+
TokenPosition* last_token_index) const {
59+
const intptr_t line_count = line_starts_data_.Length();
60+
if (line_number <= 0 || line_number > line_count) {
61+
return false;
62+
}
63+
*first_token_index = TokenPosition::Deserialize(At(line_number - 1));
64+
if (line_number == line_count) {
65+
*last_token_index = *first_token_index;
66+
} else {
67+
*last_token_index = TokenPosition::Deserialize(At(line_number) - 1);
68+
}
69+
return true;
70+
}
71+
72+
} // namespace dart
73+
74+
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)

0 commit comments

Comments
 (0)