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
5 #include "src/codegen/source-position-table.h"
6
7 #include "src/base/export-template.h"
8 #include "src/heap/local-factory-inl.h"
9 #include "src/objects/objects-inl.h"
10 #include "src/objects/objects.h"
11
12 namespace v8 {
13 namespace internal {
14
15 // We'll use a simple encoding scheme to record the source positions.
16 // Conceptually, each position consists of:
17 // - code_offset: An integer index into the BytecodeArray or code.
18 // - source_position: An integer index into the source string.
19 // - position type: Each position is either a statement or an expression.
20 //
21 // The basic idea for the encoding is to use a variable-length integer coding,
22 // where each byte contains 7 bits of payload data, and 1 'more' bit that
23 // determines whether additional bytes follow. Additionally:
24 // - we record the difference from the previous position,
25 // - we just stuff one bit for the type into the code offset,
26 // - we write least-significant bits first,
27 // - we use zig-zag encoding to encode both positive and negative numbers.
28
29 namespace {
30
31 // Each byte is encoded as MoreBit | ValueBits.
32 using MoreBit = base::BitField8<bool, 7, 1>;
33 using ValueBits = base::BitField8<unsigned, 0, 7>;
34
35 // Helper: Add the offsets from 'other' to 'value'. Also set is_statement.
AddAndSetEntry(PositionTableEntry * value,const PositionTableEntry & other)36 void AddAndSetEntry(PositionTableEntry* value,
37 const PositionTableEntry& other) {
38 value->code_offset += other.code_offset;
39 value->source_position += other.source_position;
40 value->is_statement = other.is_statement;
41 }
42
43 // Helper: Subtract the offsets from 'other' from 'value'.
SubtractFromEntry(PositionTableEntry * value,const PositionTableEntry & other)44 void SubtractFromEntry(PositionTableEntry* value,
45 const PositionTableEntry& other) {
46 value->code_offset -= other.code_offset;
47 value->source_position -= other.source_position;
48 }
49
50 // Helper: Encode an integer.
51 template <typename T>
EncodeInt(ZoneVector<byte> * bytes,T value)52 void EncodeInt(ZoneVector<byte>* bytes, T value) {
53 using unsigned_type = typename std::make_unsigned<T>::type;
54 // Zig-zag encoding.
55 static constexpr int kShift = sizeof(T) * kBitsPerByte - 1;
56 value = ((static_cast<unsigned_type>(value) << 1) ^ (value >> kShift));
57 DCHECK_GE(value, 0);
58 unsigned_type encoded = static_cast<unsigned_type>(value);
59 bool more;
60 do {
61 more = encoded > ValueBits::kMax;
62 byte current =
63 MoreBit::encode(more) | ValueBits::encode(encoded & ValueBits::kMask);
64 bytes->push_back(current);
65 encoded >>= ValueBits::kSize;
66 } while (more);
67 }
68
69 // Encode a PositionTableEntry.
EncodeEntry(ZoneVector<byte> * bytes,const PositionTableEntry & entry)70 void EncodeEntry(ZoneVector<byte>* bytes, const PositionTableEntry& entry) {
71 // We only accept ascending code offsets.
72 DCHECK_GE(entry.code_offset, 0);
73 // Since code_offset is not negative, we use sign to encode is_statement.
74 EncodeInt(bytes,
75 entry.is_statement ? entry.code_offset : -entry.code_offset - 1);
76 EncodeInt(bytes, entry.source_position);
77 }
78
79 // Helper: Decode an integer.
80 template <typename T>
DecodeInt(Vector<const byte> bytes,int * index)81 T DecodeInt(Vector<const byte> bytes, int* index) {
82 byte current;
83 int shift = 0;
84 T decoded = 0;
85 bool more;
86 do {
87 current = bytes[(*index)++];
88 decoded |= static_cast<typename std::make_unsigned<T>::type>(
89 ValueBits::decode(current))
90 << shift;
91 more = MoreBit::decode(current);
92 shift += ValueBits::kSize;
93 } while (more);
94 DCHECK_GE(decoded, 0);
95 decoded = (decoded >> 1) ^ (-(decoded & 1));
96 return decoded;
97 }
98
DecodeEntry(Vector<const byte> bytes,int * index,PositionTableEntry * entry)99 void DecodeEntry(Vector<const byte> bytes, int* index,
100 PositionTableEntry* entry) {
101 int tmp = DecodeInt<int>(bytes, index);
102 if (tmp >= 0) {
103 entry->is_statement = true;
104 entry->code_offset = tmp;
105 } else {
106 entry->is_statement = false;
107 entry->code_offset = -(tmp + 1);
108 }
109 entry->source_position = DecodeInt<int64_t>(bytes, index);
110 }
111
VectorFromByteArray(ByteArray byte_array)112 Vector<const byte> VectorFromByteArray(ByteArray byte_array) {
113 return Vector<const byte>(byte_array.GetDataStartAddress(),
114 byte_array.length());
115 }
116
117 #ifdef ENABLE_SLOW_DCHECKS
CheckTableEquals(const ZoneVector<PositionTableEntry> & raw_entries,SourcePositionTableIterator * encoded)118 void CheckTableEquals(const ZoneVector<PositionTableEntry>& raw_entries,
119 SourcePositionTableIterator* encoded) {
120 // Brute force testing: Record all positions and decode
121 // the entire table to verify they are identical.
122 auto raw = raw_entries.begin();
123 for (; !encoded->done(); encoded->Advance(), raw++) {
124 DCHECK(raw != raw_entries.end());
125 DCHECK_EQ(encoded->code_offset(), raw->code_offset);
126 DCHECK_EQ(encoded->source_position().raw(), raw->source_position);
127 DCHECK_EQ(encoded->is_statement(), raw->is_statement);
128 }
129 DCHECK(raw == raw_entries.end());
130 }
131 #endif
132
133 } // namespace
134
SourcePositionTableBuilder(Zone * zone,SourcePositionTableBuilder::RecordingMode mode)135 SourcePositionTableBuilder::SourcePositionTableBuilder(
136 Zone* zone, SourcePositionTableBuilder::RecordingMode mode)
137 : mode_(mode),
138 bytes_(zone),
139 #ifdef ENABLE_SLOW_DCHECKS
140 raw_entries_(zone),
141 #endif
142 previous_() {
143 }
144
AddPosition(size_t code_offset,SourcePosition source_position,bool is_statement)145 void SourcePositionTableBuilder::AddPosition(size_t code_offset,
146 SourcePosition source_position,
147 bool is_statement) {
148 if (Omit()) return;
149 DCHECK(source_position.IsKnown());
150 int offset = static_cast<int>(code_offset);
151 AddEntry({offset, source_position.raw(), is_statement});
152 }
153
AddEntry(const PositionTableEntry & entry)154 void SourcePositionTableBuilder::AddEntry(const PositionTableEntry& entry) {
155 PositionTableEntry tmp(entry);
156 SubtractFromEntry(&tmp, previous_);
157 EncodeEntry(&bytes_, tmp);
158 previous_ = entry;
159 #ifdef ENABLE_SLOW_DCHECKS
160 raw_entries_.push_back(entry);
161 #endif
162 }
163
164 template <typename LocalIsolate>
ToSourcePositionTable(LocalIsolate * isolate)165 Handle<ByteArray> SourcePositionTableBuilder::ToSourcePositionTable(
166 LocalIsolate* isolate) {
167 if (bytes_.empty()) return isolate->factory()->empty_byte_array();
168 DCHECK(!Omit());
169
170 Handle<ByteArray> table = isolate->factory()->NewByteArray(
171 static_cast<int>(bytes_.size()), AllocationType::kOld);
172 MemCopy(table->GetDataStartAddress(), bytes_.data(), bytes_.size());
173
174 #ifdef ENABLE_SLOW_DCHECKS
175 // Brute force testing: Record all positions and decode
176 // the entire table to verify they are identical.
177 SourcePositionTableIterator it(
178 *table, SourcePositionTableIterator::kAll,
179 SourcePositionTableIterator::kDontSkipFunctionEntry);
180 CheckTableEquals(raw_entries_, &it);
181 // No additional source positions after creating the table.
182 mode_ = OMIT_SOURCE_POSITIONS;
183 #endif
184 return table;
185 }
186
187 template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
188 Handle<ByteArray> SourcePositionTableBuilder::ToSourcePositionTable(
189 Isolate* isolate);
190 template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
191 Handle<ByteArray> SourcePositionTableBuilder::ToSourcePositionTable(
192 LocalIsolate* isolate);
193
ToSourcePositionTableVector()194 OwnedVector<byte> SourcePositionTableBuilder::ToSourcePositionTableVector() {
195 if (bytes_.empty()) return OwnedVector<byte>();
196 DCHECK(!Omit());
197
198 OwnedVector<byte> table = OwnedVector<byte>::Of(bytes_);
199
200 #ifdef ENABLE_SLOW_DCHECKS
201 // Brute force testing: Record all positions and decode
202 // the entire table to verify they are identical.
203 SourcePositionTableIterator it(
204 table.as_vector(), SourcePositionTableIterator::kAll,
205 SourcePositionTableIterator::kDontSkipFunctionEntry);
206 CheckTableEquals(raw_entries_, &it);
207 // No additional source positions after creating the table.
208 mode_ = OMIT_SOURCE_POSITIONS;
209 #endif
210 return table;
211 }
212
Initialize()213 void SourcePositionTableIterator::Initialize() {
214 Advance();
215 if (function_entry_filter_ == kSkipFunctionEntry &&
216 current_.code_offset == kFunctionEntryBytecodeOffset && !done()) {
217 Advance();
218 }
219 }
220
SourcePositionTableIterator(ByteArray byte_array,IterationFilter iteration_filter,FunctionEntryFilter function_entry_filter)221 SourcePositionTableIterator::SourcePositionTableIterator(
222 ByteArray byte_array, IterationFilter iteration_filter,
223 FunctionEntryFilter function_entry_filter)
224 : raw_table_(VectorFromByteArray(byte_array)),
225 iteration_filter_(iteration_filter),
226 function_entry_filter_(function_entry_filter) {
227 Initialize();
228 }
229
SourcePositionTableIterator(Handle<ByteArray> byte_array,IterationFilter iteration_filter,FunctionEntryFilter function_entry_filter)230 SourcePositionTableIterator::SourcePositionTableIterator(
231 Handle<ByteArray> byte_array, IterationFilter iteration_filter,
232 FunctionEntryFilter function_entry_filter)
233 : table_(byte_array),
234 iteration_filter_(iteration_filter),
235 function_entry_filter_(function_entry_filter) {
236 Initialize();
237 #ifdef DEBUG
238 // We can enable allocation because we keep the table in a handle.
239 no_gc.Release();
240 #endif // DEBUG
241 }
242
SourcePositionTableIterator(Vector<const byte> bytes,IterationFilter iteration_filter,FunctionEntryFilter function_entry_filter)243 SourcePositionTableIterator::SourcePositionTableIterator(
244 Vector<const byte> bytes, IterationFilter iteration_filter,
245 FunctionEntryFilter function_entry_filter)
246 : raw_table_(bytes),
247 iteration_filter_(iteration_filter),
248 function_entry_filter_(function_entry_filter) {
249 Initialize();
250 #ifdef DEBUG
251 // We can enable allocation because the underlying vector does not move.
252 no_gc.Release();
253 #endif // DEBUG
254 }
255
Advance()256 void SourcePositionTableIterator::Advance() {
257 Vector<const byte> bytes =
258 table_.is_null() ? raw_table_ : VectorFromByteArray(*table_);
259 DCHECK(!done());
260 DCHECK(index_ >= 0 && index_ <= bytes.length());
261 bool filter_satisfied = false;
262 while (!done() && !filter_satisfied) {
263 if (index_ >= bytes.length()) {
264 index_ = kDone;
265 } else {
266 PositionTableEntry tmp;
267 DecodeEntry(bytes, &index_, &tmp);
268 AddAndSetEntry(¤t_, tmp);
269 SourcePosition p = source_position();
270 filter_satisfied =
271 (iteration_filter_ == kAll) ||
272 (iteration_filter_ == kJavaScriptOnly && p.IsJavaScript()) ||
273 (iteration_filter_ == kExternalOnly && p.IsExternal());
274 }
275 }
276 }
277
278 } // namespace internal
279 } // namespace v8
280