V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
wasm-translation.cc
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/inspector/wasm-translation.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "src/debug/debug-interface.h"
11 #include "src/inspector/string-util.h"
12 #include "src/inspector/v8-debugger-agent-impl.h"
13 #include "src/inspector/v8-debugger-script.h"
14 #include "src/inspector/v8-debugger.h"
15 #include "src/inspector/v8-inspector-impl.h"
16 
17 namespace v8_inspector {
18 
19 using OffsetTable = v8::debug::WasmDisassembly::OffsetTable;
20 
22  String16 source;
23  int end_line = 0;
24  int end_column = 0;
25 
26  OffsetTable offset_table;
27  OffsetTable reverse_offset_table;
28 
29  WasmSourceInformation(String16 source, OffsetTable offset_table)
30  : source(std::move(source)), offset_table(std::move(offset_table)) {
31  int num_lines = 0;
32  int last_newline = -1;
33  size_t next_newline = this->source.find('\n', last_newline + 1);
34  while (next_newline != String16::kNotFound) {
35  last_newline = static_cast<int>(next_newline);
36  next_newline = this->source.find('\n', last_newline + 1);
37  ++num_lines;
38  }
39  end_line = num_lines;
40  end_column = static_cast<int>(this->source.length()) - last_newline - 1;
41 
42  reverse_offset_table = this->offset_table;
43  // Order by line, column, then byte offset.
44  auto cmp = [](OffsetTable::value_type el1, OffsetTable::value_type el2) {
45  if (el1.line != el2.line) return el1.line < el2.line;
46  if (el1.column != el2.column) return el1.column < el2.column;
47  return el1.byte_offset < el2.byte_offset;
48  };
49  std::sort(reverse_offset_table.begin(), reverse_offset_table.end(), cmp);
50  }
51 
52  WasmSourceInformation() = default;
53 };
54 
56  public:
57  struct TransLocation {
58  WasmTranslation* translation;
59  String16 script_id;
60  int line;
61  int column;
62  TransLocation(WasmTranslation* translation, String16 script_id, int line,
63  int column)
64  : translation(translation),
65  script_id(std::move(script_id)),
66  line(line),
67  column(column) {}
68  };
69 
70  TranslatorImpl(v8::Isolate* isolate, v8::Local<v8::debug::WasmScript> script)
71  : script_(isolate, script) {}
72 
73  void Init(v8::Isolate* isolate, WasmTranslation* translation,
74  V8DebuggerAgentImpl* agent) {
75  // Register fake scripts for each function in this wasm module/script.
76  v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
77  int num_functions = script->NumFunctions();
78  int num_imported_functions = script->NumImportedFunctions();
79  DCHECK_LE(0, num_imported_functions);
80  DCHECK_LE(0, num_functions);
81  DCHECK_GE(num_functions, num_imported_functions);
82  String16 script_id = String16::fromInteger(script->Id());
83  for (int func_idx = num_imported_functions; func_idx < num_functions;
84  ++func_idx) {
85  AddFakeScript(isolate, script_id, func_idx, translation, agent);
86  }
87  }
88 
89  void Translate(TransLocation* loc) {
90  const OffsetTable& offset_table = GetOffsetTable(loc);
91  DCHECK(!offset_table.empty());
92  uint32_t byte_offset = static_cast<uint32_t>(loc->column);
93 
94  // Binary search for the given offset.
95  unsigned left = 0; // inclusive
96  unsigned right = static_cast<unsigned>(offset_table.size()); // exclusive
97  while (right - left > 1) {
98  unsigned mid = (left + right) / 2;
99  if (offset_table[mid].byte_offset <= byte_offset) {
100  left = mid;
101  } else {
102  right = mid;
103  }
104  }
105 
106  loc->script_id = GetFakeScriptId(loc);
107  if (offset_table[left].byte_offset == byte_offset) {
108  loc->line = offset_table[left].line;
109  loc->column = offset_table[left].column;
110  } else {
111  loc->line = 0;
112  loc->column = 0;
113  }
114  }
115 
116  static bool LessThan(const v8::debug::WasmDisassemblyOffsetTableEntry& entry,
117  const TransLocation& loc) {
118  return entry.line < loc.line ||
119  (entry.line == loc.line && entry.column < loc.column);
120  }
121 
122  void TranslateBack(TransLocation* loc) {
123  v8::Isolate* isolate = loc->translation->isolate_;
124  int func_index = GetFunctionIndexFromFakeScriptId(loc->script_id);
125  const OffsetTable& reverse_table = GetReverseTable(isolate, func_index);
126  if (reverse_table.empty()) return;
127 
128  // Binary search for the given line and column.
129  auto element = std::lower_bound(reverse_table.begin(), reverse_table.end(),
130  *loc, LessThan);
131 
132  int found_byte_offset = 0;
133  // We want an entry on the same line if possible.
134  if (element == reverse_table.end()) {
135  // We did not find an element, so this points after the function.
136  std::pair<int, int> func_range =
137  script_.Get(isolate)->GetFunctionRange(func_index);
138  DCHECK_LE(func_range.first, func_range.second);
139  found_byte_offset = func_range.second - func_range.first;
140  } else if (element->line == loc->line || element == reverse_table.begin()) {
141  found_byte_offset = element->byte_offset;
142  } else {
143  auto prev = element - 1;
144  DCHECK(prev->line == loc->line);
145  found_byte_offset = prev->byte_offset;
146  }
147 
148  loc->script_id = String16::fromInteger(script_.Get(isolate)->Id());
149  loc->line = func_index;
150  loc->column = found_byte_offset;
151  }
152 
153  const WasmSourceInformation& GetSourceInformation(v8::Isolate* isolate,
154  int index) {
155  auto it = source_informations_.find(index);
156  if (it != source_informations_.end()) return it->second;
157  v8::HandleScope scope(isolate);
158  v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
159  v8::debug::WasmDisassembly disassembly = script->DisassembleFunction(index);
160 
161  auto inserted = source_informations_.insert(std::make_pair(
162  index, WasmSourceInformation({disassembly.disassembly.data(),
163  disassembly.disassembly.length()},
164  std::move(disassembly.offset_table))));
165  DCHECK(inserted.second);
166  return inserted.first->second;
167  }
168 
169  const String16 GetHash(v8::Isolate* isolate, int index) {
170  v8::HandleScope scope(isolate);
171  v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
172  uint32_t hash = script->GetFunctionHash(index);
173  String16Builder builder;
174  builder.appendUnsignedAsHex(hash);
175  return builder.toString();
176  }
177 
178  int GetContextId(v8::Isolate* isolate) {
179  v8::HandleScope scope(isolate);
180  v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
181  return script->ContextId().FromMaybe(0);
182  }
183 
184  private:
185  String16 GetFakeScriptUrl(v8::Isolate* isolate, int func_index) {
186  v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
187  String16 script_name =
188  toProtocolString(isolate, script->Name().ToLocalChecked());
189  int numFunctions = script->NumFunctions();
190  int numImported = script->NumImportedFunctions();
191  String16Builder builder;
192  builder.appendAll("wasm://wasm/", script_name, '/');
193  if (numFunctions - numImported > 300) {
194  size_t digits = String16::fromInteger(numFunctions - 1).length();
195  String16 thisCategory = String16::fromInteger((func_index / 100) * 100);
196  DCHECK_LE(thisCategory.length(), digits);
197  for (size_t i = thisCategory.length(); i < digits; ++i)
198  builder.append('0');
199  builder.appendAll(thisCategory, '/');
200  }
201  builder.appendAll(script_name, '-');
202  builder.appendNumber(func_index);
203  return builder.toString();
204  }
205 
206  String16 GetFakeScriptId(const String16& script_id, int func_index) {
207  return String16::concat(script_id, '-', String16::fromInteger(func_index));
208  }
209  String16 GetFakeScriptId(const TransLocation* loc) {
210  return GetFakeScriptId(loc->script_id, loc->line);
211  }
212 
213  void AddFakeScript(v8::Isolate* isolate, const String16& underlyingScriptId,
214  int func_idx, WasmTranslation* translation,
215  V8DebuggerAgentImpl* agent) {
216  String16 fake_script_id = GetFakeScriptId(underlyingScriptId, func_idx);
217  String16 fake_script_url = GetFakeScriptUrl(isolate, func_idx);
218 
219  std::unique_ptr<V8DebuggerScript> fake_script =
220  V8DebuggerScript::CreateWasm(isolate, translation, script_.Get(isolate),
221  fake_script_id, std::move(fake_script_url),
222  func_idx);
223 
224  translation->AddFakeScript(fake_script->scriptId(), this);
225  agent->didParseSource(std::move(fake_script), true);
226  }
227 
228  int GetFunctionIndexFromFakeScriptId(const String16& fake_script_id) {
229  size_t last_dash_pos = fake_script_id.reverseFind('-');
230  DCHECK_GT(fake_script_id.length(), last_dash_pos);
231  bool ok = true;
232  int func_index = fake_script_id.substring(last_dash_pos + 1).toInteger(&ok);
233  DCHECK(ok);
234  return func_index;
235  }
236 
237  const OffsetTable& GetOffsetTable(const TransLocation* loc) {
238  int func_index = loc->line;
239  return GetSourceInformation(loc->translation->isolate_, func_index)
240  .offset_table;
241  }
242 
243  const OffsetTable& GetReverseTable(v8::Isolate* isolate, int func_index) {
244  return GetSourceInformation(isolate, func_index).reverse_offset_table;
245  }
246 
248 
249  // We assume to only disassemble a subset of the functions, so store them in a
250  // map instead of an array.
251  std::unordered_map<int, WasmSourceInformation> source_informations_;
252 };
253 
254 WasmTranslation::WasmTranslation(v8::Isolate* isolate) : isolate_(isolate) {}
255 
256 WasmTranslation::~WasmTranslation() { Clear(); }
257 
258 void WasmTranslation::AddScript(v8::Local<v8::debug::WasmScript> script,
259  V8DebuggerAgentImpl* agent) {
260  std::unique_ptr<TranslatorImpl> impl;
261  impl.reset(new TranslatorImpl(isolate_, script));
262  DCHECK(impl);
263  auto inserted =
264  wasm_translators_.insert(std::make_pair(script->Id(), std::move(impl)));
265  // Check that no mapping for this script id existed before.
266  DCHECK(inserted.second);
267  // impl has been moved, use the returned iterator to call Init.
268  inserted.first->second->Init(isolate_, this, agent);
269 }
270 
271 void WasmTranslation::Clear() {
272  wasm_translators_.clear();
273  fake_scripts_.clear();
274 }
275 
276 void WasmTranslation::Clear(v8::Isolate* isolate,
277  const std::vector<int>& contextIdsToClear) {
278  for (auto iter = fake_scripts_.begin(); iter != fake_scripts_.end();) {
279  auto contextId = iter->second->GetContextId(isolate);
280  auto it = std::find(std::begin(contextIdsToClear),
281  std::end(contextIdsToClear), contextId);
282  if (it != std::end(contextIdsToClear)) {
283  iter = fake_scripts_.erase(iter);
284  } else {
285  ++iter;
286  }
287  }
288 
289  for (auto iter = wasm_translators_.begin();
290  iter != wasm_translators_.end();) {
291  auto contextId = iter->second->GetContextId(isolate);
292  auto it = std::find(std::begin(contextIdsToClear),
293  std::end(contextIdsToClear), contextId);
294  if (it != std::end(contextIdsToClear)) {
295  iter = wasm_translators_.erase(iter);
296  } else {
297  ++iter;
298  }
299  }
300 }
301 
302 const String16& WasmTranslation::GetSource(const String16& script_id,
303  int func_index) {
304  auto it = fake_scripts_.find(script_id);
305  DCHECK_NE(it, fake_scripts_.end());
306  return it->second->GetSourceInformation(isolate_, func_index).source;
307 }
308 
309 int WasmTranslation::GetEndLine(const String16& script_id, int func_index) {
310  auto it = fake_scripts_.find(script_id);
311  DCHECK_NE(it, fake_scripts_.end());
312  return it->second->GetSourceInformation(isolate_, func_index).end_line;
313 }
314 
315 int WasmTranslation::GetEndColumn(const String16& script_id, int func_index) {
316  auto it = fake_scripts_.find(script_id);
317  DCHECK_NE(it, fake_scripts_.end());
318  return it->second->GetSourceInformation(isolate_, func_index).end_column;
319 }
320 
321 String16 WasmTranslation::GetHash(const String16& script_id, int func_index) {
322  auto it = fake_scripts_.find(script_id);
323  DCHECK_NE(it, fake_scripts_.end());
324  return it->second->GetHash(isolate_, func_index);
325 }
326 
327 // Translation "forward" (to artificial scripts).
328 bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation(
329  String16* script_id, int* line_number, int* column_number) {
330  DCHECK(script_id && line_number && column_number);
331  bool ok = true;
332  int script_id_int = script_id->toInteger(&ok);
333  if (!ok) return false;
334 
335  auto it = wasm_translators_.find(script_id_int);
336  if (it == wasm_translators_.end()) return false;
337  TranslatorImpl* translator = it->second.get();
338 
339  TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
340  *line_number, *column_number);
341  translator->Translate(&trans_loc);
342 
343  *script_id = std::move(trans_loc.script_id);
344  *line_number = trans_loc.line;
345  *column_number = trans_loc.column;
346 
347  return true;
348 }
349 
350 // Translation "backward" (from artificial to real scripts).
351 bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation(
352  String16* script_id, int* line_number, int* column_number) {
353  auto it = fake_scripts_.find(*script_id);
354  if (it == fake_scripts_.end()) return false;
355  TranslatorImpl* translator = it->second;
356 
357  TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
358  *line_number, *column_number);
359  translator->TranslateBack(&trans_loc);
360 
361  *script_id = std::move(trans_loc.script_id);
362  *line_number = trans_loc.line;
363  *column_number = trans_loc.column;
364 
365  return true;
366 }
367 
368 void WasmTranslation::AddFakeScript(const String16& scriptId,
369  TranslatorImpl* translator) {
370  DCHECK_EQ(0, fake_scripts_.count(scriptId));
371  fake_scripts_.insert(std::make_pair(scriptId, translator));
372 }
373 } // namespace v8_inspector
Definition: v8.h:85