• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&current_, 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