v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
source-position-table.cc
Go to the documentation of this file.
1// Copyright 2016 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
8#include "src/base/logging.h"
12#include "src/objects/objects.h"
13
14namespace v8 {
15namespace internal {
16
17// We'll use a simple encoding scheme to record the source positions.
18// Conceptually, each position consists of:
19// - code_offset: An integer index into the BytecodeArray or code.
20// - source_position: An integer index into the source string.
21// - position type: Each position is either a statement or an expression.
22//
23// The basic idea for the encoding is to use a variable-length integer coding,
24// where each byte contains 7 bits of payload data, and 1 'more' bit that
25// determines whether additional bytes follow. Additionally:
26// - we record the difference from the previous position,
27// - we just stuff one bit for the type into the code offset,
28// - we write least-significant bits first,
29// - we use zig-zag encoding to encode both positive and negative numbers.
30
31namespace {
32
33// Each byte is encoded as MoreBit | ValueBits.
34using MoreBit = base::BitField8<bool, 7, 1>;
35using ValueBits = base::BitField8<unsigned, 0, 7>;
36
37// Helper: Add the offsets from 'other' to 'value'. Also set is_statement.
38void AddAndSetEntry(PositionTableEntry* value,
39 const PositionTableEntry& other) {
40 value->code_offset += other.code_offset;
41 DCHECK_IMPLIES(value->code_offset != kFunctionEntryBytecodeOffset,
42 value->code_offset >= 0);
43 value->source_position += other.source_position;
44 DCHECK_LE(0, value->source_position);
45 value->is_statement = other.is_statement;
46}
47
48// Helper: Subtract the offsets from 'other' from 'value'.
49void SubtractFromEntry(PositionTableEntry* value,
50 const PositionTableEntry& other) {
51 value->code_offset -= other.code_offset;
52 value->source_position -= other.source_position;
53}
54
55// Helper: Encode an integer.
56template <typename T>
57void EncodeInt(ZoneVector<uint8_t>* bytes, T value) {
58 using unsigned_type = std::make_unsigned_t<T>;
59 // Zig-zag encoding.
60 static constexpr int kShift = sizeof(T) * kBitsPerByte - 1;
61 value = ((static_cast<unsigned_type>(value) << 1) ^ (value >> kShift));
62 DCHECK_GE(value, 0);
63 unsigned_type encoded = static_cast<unsigned_type>(value);
64 bool more;
65 do {
66 more = encoded > ValueBits::kMax;
67 uint8_t current =
68 MoreBit::encode(more) | ValueBits::encode(encoded & ValueBits::kMask);
69 bytes->push_back(current);
70 encoded >>= ValueBits::kSize;
71 } while (more);
72}
73
74// Encode a PositionTableEntry.
75void EncodeEntry(ZoneVector<uint8_t>* bytes, const PositionTableEntry& entry) {
76 // We only accept ascending code offsets.
77 DCHECK_LE(0, entry.code_offset);
78 // All but the first entry must be *strictly* ascending (no two entries for
79 // the same position).
80 // TODO(11496): This DCHECK fails tests.
81 // DCHECK_IMPLIES(!bytes->empty(), entry.code_offset > 0);
82 // Since code_offset is not negative, we use sign to encode is_statement.
83 EncodeInt(bytes,
84 entry.is_statement ? entry.code_offset : -entry.code_offset - 1);
85 EncodeInt(bytes, entry.source_position);
86}
87
88// Helper: Decode an integer.
89template <typename T>
90T DecodeInt(base::Vector<const uint8_t> bytes, int* index) {
91 uint8_t current;
92 int shift = 0;
93 T decoded = 0;
94 bool more;
95 do {
96 current = bytes[(*index)++];
97 decoded |= static_cast<std::make_unsigned_t<T>>(ValueBits::decode(current))
98 << shift;
99 more = MoreBit::decode(current);
100 shift += ValueBits::kSize;
101 } while (more);
102 DCHECK_GE(decoded, 0);
103 decoded = (decoded >> 1) ^ (-(decoded & 1));
104 return decoded;
105}
106
107void DecodeEntry(base::Vector<const uint8_t> bytes, int* index,
108 PositionTableEntry* entry) {
109 int tmp = DecodeInt<int>(bytes, index);
110 if (tmp >= 0) {
111 entry->is_statement = true;
112 entry->code_offset = tmp;
113 } else {
114 entry->is_statement = false;
115 entry->code_offset = -(tmp + 1);
116 }
117 entry->source_position = DecodeInt<int64_t>(bytes, index);
118}
119
120base::Vector<const uint8_t> VectorFromByteArray(
121 Tagged<TrustedByteArray> byte_array) {
122 return base::Vector<const uint8_t>(byte_array->begin(), byte_array->length());
123}
124
125#ifdef ENABLE_SLOW_DCHECKS
126void CheckTableEquals(const ZoneVector<PositionTableEntry>& raw_entries,
127 SourcePositionTableIterator* encoded) {
128 // Brute force testing: Record all positions and decode
129 // the entire table to verify they are identical.
130 auto raw = raw_entries.begin();
131 for (; !encoded->done(); encoded->Advance(), raw++) {
132 DCHECK(raw != raw_entries.end());
133 DCHECK_EQ(encoded->code_offset(), raw->code_offset);
134 DCHECK_EQ(encoded->source_position().raw(), raw->source_position);
135 DCHECK_EQ(encoded->is_statement(), raw->is_statement);
136 }
137 DCHECK(raw == raw_entries.end());
138}
139#endif
140
141} // namespace
142
145 : mode_(mode),
146 bytes_(zone),
147#ifdef ENABLE_SLOW_DCHECKS
148 raw_entries_(zone),
149#endif
150 previous_() {
151}
152
154 SourcePosition source_position,
155 bool is_statement) {
156 if (Omit()) return;
157 DCHECK(source_position.IsKnown());
158 int offset = static_cast<int>(code_offset);
159 AddEntry({offset, source_position.raw(), is_statement});
160}
161
163 const PositionTableEntry& entry) {
164 PositionTableEntry tmp(entry);
165 SubtractFromEntry(&tmp, previous_);
166 EncodeEntry(&bytes_, tmp);
167 previous_ = entry;
168#ifdef ENABLE_SLOW_DCHECKS
169 raw_entries_.push_back(entry);
170#endif
171}
172
173template <typename IsolateT>
175 IsolateT* isolate) {
176 if (bytes_.empty()) return isolate->factory()->empty_trusted_byte_array();
177 DCHECK(!Omit());
178
180 isolate->factory()->NewTrustedByteArray(static_cast<int>(bytes_.size()));
181 MemCopy(table->begin(), bytes_.data(), bytes_.size());
182
183#ifdef ENABLE_SLOW_DCHECKS
184 // Brute force testing: Record all positions and decode
185 // the entire table to verify they are identical.
189 CheckTableEquals(raw_entries_, &it);
190 // No additional source positions after creating the table.
192#endif
193 return table;
194}
195
198 Isolate* isolate);
201 LocalIsolate* isolate);
202
206 DCHECK(!Omit());
207
209
210#ifdef ENABLE_SLOW_DCHECKS
211 // Brute force testing: Record all positions and decode
212 // the entire table to verify they are identical.
214 table.as_vector(), SourcePositionTableIterator::kAll,
216 CheckTableEquals(raw_entries_, &it);
217 // No additional source positions after creating the table.
219#endif
220 return table;
221}
222
230
232 Tagged<TrustedByteArray> byte_array, IterationFilter iteration_filter,
233 FunctionEntryFilter function_entry_filter)
234 : raw_table_(VectorFromByteArray(byte_array)),
235 iteration_filter_(iteration_filter),
236 function_entry_filter_(function_entry_filter) {
237 Initialize();
238}
239
241 Handle<TrustedByteArray> byte_array, IterationFilter iteration_filter,
242 FunctionEntryFilter function_entry_filter)
243 : table_(byte_array),
244 iteration_filter_(iteration_filter),
245 function_entry_filter_(function_entry_filter) {
246 Initialize();
247#ifdef DEBUG
248 // We can enable allocation because we keep the table in a handle.
249 no_gc.Release();
250#endif // DEBUG
251}
252
254 base::Vector<const uint8_t> bytes, IterationFilter iteration_filter,
255 FunctionEntryFilter function_entry_filter)
256 : raw_table_(bytes),
257 iteration_filter_(iteration_filter),
258 function_entry_filter_(function_entry_filter) {
259 Initialize();
260#ifdef DEBUG
261 // We can enable allocation because the underlying vector does not move.
262 no_gc.Release();
263#endif // DEBUG
264}
265
268 table_.is_null() ? raw_table_ : VectorFromByteArray(*table_);
269 DCHECK(!done());
270 DCHECK(index_ >= 0 && index_ <= bytes.length());
271 bool filter_satisfied = false;
272 while (!done() && !filter_satisfied) {
273 if (index_ >= bytes.length()) {
274 index_ = kDone;
275 } else {
277 DecodeEntry(bytes, &index_, &tmp);
278 AddAndSetEntry(&current_, tmp);
280 filter_satisfied =
281 (iteration_filter_ == kAll) ||
284 }
285 }
286}
287
288} // namespace internal
289} // namespace v8
#define T
SourcePositionTableBuilder(Zone *zone, RecordingMode mode=RECORD_SOURCE_POSITIONS)
base::OwnedVector< uint8_t > ToSourcePositionTableVector()
Handle< TrustedByteArray > ToSourcePositionTable(IsolateT *isolate)
void AddPosition(size_t code_offset, SourcePosition source_position, bool is_statement)
void AddEntry(const PositionTableEntry &entry)
SourcePositionTableIterator(Handle< TrustedByteArray > byte_array, IterationFilter iteration_filter=kJavaScriptOnly, FunctionEntryFilter function_entry_filter=kSkipFunctionEntry)
RecordWriteMode const mode_
LineAndColumn current
#define EXPORT_TEMPLATE_DEFINE(export)
int32_t offset
Register tmp
BitField< T, shift, size, uint8_t > BitField8
Definition bit-field.h:90
OwnedVector< T > OwnedCopyOf(const T *data, size_t size)
Definition vector.h:383
constexpr int kFunctionEntryBytecodeOffset
Definition globals.h:854
constexpr int kBitsPerByte
Definition globals.h:682
Tagged(T object) -> Tagged< T >
return value
Definition map-inl.h:893
void MemCopy(void *dest, const void *src, size_t size)
Definition memcopy.h:124
SourcePositionTable *const table_
Definition pipeline.cc:227
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_EXPORT_PRIVATE
Definition macros.h:460
#define V8_INLINE
Definition v8config.h:500