V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
v8-debugger-script.cc
1 // Copyright 2014 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/v8-debugger-script.h"
6 
7 #include "src/inspector/inspected-context.h"
8 #include "src/inspector/string-util.h"
9 #include "src/inspector/v8-inspector-impl.h"
10 #include "src/inspector/wasm-translation.h"
11 #include "src/v8memory.h"
12 
13 namespace v8_inspector {
14 
15 namespace {
16 
17 const char kGlobalDebuggerScriptHandleLabel[] = "DevTools debugger";
18 
19 // Hash algorithm for substrings is described in "Über die Komplexität der
20 // Multiplikation in
21 // eingeschränkten Branchingprogrammmodellen" by Woelfe.
22 // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
23 String16 calculateHash(v8::Isolate* isolate, v8::Local<v8::String> source) {
24  static uint64_t prime[] = {0x3FB75161, 0xAB1F4E4F, 0x82675BC5, 0xCD924D35,
25  0x81ABE279};
26  static uint64_t random[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,
27  0xC3D2E1F0};
28  static uint32_t randomOdd[] = {0xB4663807, 0xCC322BF5, 0xD4F91BBD, 0xA7BEA11D,
29  0x8F462907};
30 
31  uint64_t hashes[] = {0, 0, 0, 0, 0};
32  uint64_t zi[] = {1, 1, 1, 1, 1};
33 
34  const size_t hashesSize = arraysize(hashes);
35 
36  size_t current = 0;
37 
38  std::unique_ptr<UChar[]> buffer(new UChar[source->Length()]);
39  int written = source->Write(
40  isolate, reinterpret_cast<uint16_t*>(buffer.get()), 0, source->Length());
41 
42  const uint32_t* data = nullptr;
43  size_t sizeInBytes = sizeof(UChar) * written;
44  data = reinterpret_cast<const uint32_t*>(buffer.get());
45  for (size_t i = 0; i < sizeInBytes / 4; ++i) {
46  uint32_t d = v8::internal::ReadUnalignedUInt32(
47  reinterpret_cast<v8::internal::Address>(data + i));
48 #if V8_TARGET_LITTLE_ENDIAN
49  uint32_t v = d;
50 #else
51  uint32_t v = (d << 16) | (d >> 16);
52 #endif
53  uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
54  hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
55  zi[current] = (zi[current] * random[current]) % prime[current];
56  current = current == hashesSize - 1 ? 0 : current + 1;
57  }
58  if (sizeInBytes % 4) {
59  uint32_t v = 0;
60  const uint8_t* data_8b = reinterpret_cast<const uint8_t*>(data);
61  for (size_t i = sizeInBytes - sizeInBytes % 4; i < sizeInBytes; ++i) {
62  v <<= 8;
63 #if V8_TARGET_LITTLE_ENDIAN
64  v |= data_8b[i];
65 #else
66  if (i % 2) {
67  v |= data_8b[i - 1];
68  } else {
69  v |= data_8b[i + 1];
70  }
71 #endif
72  }
73  uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
74  hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
75  zi[current] = (zi[current] * random[current]) % prime[current];
76  current = current == hashesSize - 1 ? 0 : current + 1;
77  }
78 
79  for (size_t i = 0; i < hashesSize; ++i)
80  hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i];
81 
82  String16Builder hash;
83  for (size_t i = 0; i < hashesSize; ++i)
84  hash.appendUnsignedAsHex(static_cast<uint32_t>(hashes[i]));
85  return hash.toString();
86 }
87 
88 void TranslateProtocolLocationToV8Location(WasmTranslation* wasmTranslation,
90  const String16& scriptId,
91  const String16& expectedV8ScriptId) {
92  if (loc->IsEmpty()) return;
93  int lineNumber = loc->GetLineNumber();
94  int columnNumber = loc->GetColumnNumber();
95  String16 translatedScriptId = scriptId;
96  wasmTranslation->TranslateProtocolLocationToWasmScriptLocation(
97  &translatedScriptId, &lineNumber, &columnNumber);
98  DCHECK_EQ(expectedV8ScriptId.utf8(), translatedScriptId.utf8());
99  *loc = v8::debug::Location(lineNumber, columnNumber);
100 }
101 
102 void TranslateV8LocationToProtocolLocation(
103  WasmTranslation* wasmTranslation, v8::debug::Location* loc,
104  const String16& scriptId, const String16& expectedProtocolScriptId) {
105  int lineNumber = loc->GetLineNumber();
106  int columnNumber = loc->GetColumnNumber();
107  String16 translatedScriptId = scriptId;
108  wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
109  &translatedScriptId, &lineNumber, &columnNumber);
110  DCHECK_EQ(expectedProtocolScriptId.utf8(), translatedScriptId.utf8());
111  *loc = v8::debug::Location(lineNumber, columnNumber);
112 }
113 
114 class ActualScript : public V8DebuggerScript {
115  friend class V8DebuggerScript;
116 
117  public:
118  ActualScript(v8::Isolate* isolate, v8::Local<v8::debug::Script> script,
119  bool isLiveEdit, V8InspectorClient* client)
120  : V8DebuggerScript(isolate, String16::fromInteger(script->Id()),
121  GetScriptURL(isolate, script, client)),
122  m_isLiveEdit(isLiveEdit) {
123  Initialize(script);
124  }
125 
126  bool isLiveEdit() const override { return m_isLiveEdit; }
127  bool isModule() const override { return m_isModule; }
128 
129  String16 source(size_t pos, size_t len) const override {
130  v8::HandleScope scope(m_isolate);
131  v8::Local<v8::String> v8Source;
132  if (!script()->Source().ToLocal(&v8Source)) return String16();
133  if (pos >= static_cast<size_t>(v8Source->Length())) return String16();
134  size_t substringLength =
135  std::min(len, static_cast<size_t>(v8Source->Length()) - pos);
136  std::unique_ptr<UChar[]> buffer(new UChar[substringLength]);
137  v8Source->Write(m_isolate, reinterpret_cast<uint16_t*>(buffer.get()),
138  static_cast<int>(pos), static_cast<int>(substringLength));
139  return String16(buffer.get(), substringLength);
140  }
141  int startLine() const override { return m_startLine; }
142  int startColumn() const override { return m_startColumn; }
143  int endLine() const override { return m_endLine; }
144  int endColumn() const override { return m_endColumn; }
145  bool isSourceLoadedLazily() const override { return false; }
146  int length() const override {
147  v8::HandleScope scope(m_isolate);
148  v8::Local<v8::String> v8Source;
149  if (!script()->Source().ToLocal(&v8Source)) return 0;
150  return v8Source->Length();
151  }
152 
153  const String16& sourceMappingURL() const override {
154  return m_sourceMappingURL;
155  }
156 
157  void setSourceMappingURL(const String16& sourceMappingURL) override {
158  m_sourceMappingURL = sourceMappingURL;
159  }
160 
161  void setSource(const String16& newSource, bool preview,
162  v8::debug::LiveEditResult* result) override {
163  v8::EscapableHandleScope scope(m_isolate);
164  v8::Local<v8::String> v8Source = toV8String(m_isolate, newSource);
165  if (!m_script.Get(m_isolate)->SetScriptSource(v8Source, preview, result)) {
166  result->message = scope.Escape(result->message);
167  return;
168  }
169  if (preview) return;
170  m_hash = String16();
171  Initialize(scope.Escape(result->script));
172  }
173 
174  bool getPossibleBreakpoints(
175  const v8::debug::Location& start, const v8::debug::Location& end,
176  bool restrictToFunction,
177  std::vector<v8::debug::BreakLocation>* locations) override {
178  v8::HandleScope scope(m_isolate);
179  v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
180  std::vector<v8::debug::BreakLocation> allLocations;
181  if (!script->GetPossibleBreakpoints(start, end, restrictToFunction,
182  &allLocations)) {
183  return false;
184  }
185  if (!allLocations.size()) return true;
186  v8::debug::BreakLocation current = allLocations[0];
187  for (size_t i = 1; i < allLocations.size(); ++i) {
188  if (allLocations[i].GetLineNumber() == current.GetLineNumber() &&
189  allLocations[i].GetColumnNumber() == current.GetColumnNumber()) {
190  if (allLocations[i].type() != v8::debug::kCommonBreakLocation) {
191  DCHECK(allLocations[i].type() == v8::debug::kCallBreakLocation ||
192  allLocations[i].type() == v8::debug::kReturnBreakLocation);
193  // debugger can returns more then one break location at the same
194  // source location, e.g. foo() - in this case there are two break
195  // locations before foo: for statement and for function call, we can
196  // merge them for inspector and report only one with call type.
197  current = allLocations[i];
198  }
199  } else {
200  // we assume that returned break locations are sorted.
201  DCHECK(
202  allLocations[i].GetLineNumber() > current.GetLineNumber() ||
203  (allLocations[i].GetColumnNumber() >= current.GetColumnNumber() &&
204  allLocations[i].GetLineNumber() == current.GetLineNumber()));
205  locations->push_back(current);
206  current = allLocations[i];
207  }
208  }
209  locations->push_back(current);
210  return true;
211  }
212 
213  void resetBlackboxedStateCache() override {
214  v8::HandleScope scope(m_isolate);
215  v8::debug::ResetBlackboxedStateCache(m_isolate, m_script.Get(m_isolate));
216  }
217 
218  int offset(int lineNumber, int columnNumber) const override {
219  v8::HandleScope scope(m_isolate);
220  return m_script.Get(m_isolate)->GetSourceOffset(
221  v8::debug::Location(lineNumber, columnNumber));
222  }
223 
224  v8::debug::Location location(int offset) const override {
225  v8::HandleScope scope(m_isolate);
226  return m_script.Get(m_isolate)->GetSourceLocation(offset);
227  }
228 
229  bool setBreakpoint(const String16& condition, v8::debug::Location* location,
230  int* id) const override {
231  v8::HandleScope scope(m_isolate);
232  return script()->SetBreakpoint(toV8String(m_isolate, condition), location,
233  id);
234  }
235 
236  const String16& hash() const override {
237  if (m_hash.isEmpty()) {
238  v8::HandleScope scope(m_isolate);
239  v8::Local<v8::String> v8Source;
240  if (script()->Source().ToLocal(&v8Source)) {
241  m_hash = calculateHash(m_isolate, v8Source);
242  }
243  }
244  DCHECK(!m_hash.isEmpty());
245  return m_hash;
246  }
247 
248  private:
249  String16 GetScriptURL(v8::Isolate* isolate,
251  V8InspectorClient* client) {
252  v8::Local<v8::String> sourceURL;
253  if (script->SourceURL().ToLocal(&sourceURL) && sourceURL->Length() > 0)
254  return toProtocolString(isolate, sourceURL);
255  v8::Local<v8::String> v8Name;
256  if (script->Name().ToLocal(&v8Name) && v8Name->Length() > 0) {
257  String16 name = toProtocolString(isolate, v8Name);
258  std::unique_ptr<StringBuffer> url =
259  client->resourceNameToUrl(toStringView(name));
260  return url ? toString16(url->string()) : name;
261  }
262  return String16();
263  }
264 
265  v8::Local<v8::debug::Script> script() const override {
266  return m_script.Get(m_isolate);
267  }
268 
269  void Initialize(v8::Local<v8::debug::Script> script) {
271  m_hasSourceURLComment =
272  script->SourceURL().ToLocal(&tmp) && tmp->Length() > 0;
273  if (script->SourceMappingURL().ToLocal(&tmp))
274  m_sourceMappingURL = toProtocolString(m_isolate, tmp);
275  m_startLine = script->LineOffset();
276  m_startColumn = script->ColumnOffset();
277  std::vector<int> lineEnds = script->LineEnds();
278  CHECK(lineEnds.size());
279  int source_length = lineEnds[lineEnds.size() - 1];
280  if (lineEnds.size()) {
281  m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
282  if (lineEnds.size() > 1) {
283  m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
284  } else {
285  m_endColumn = source_length + m_startColumn;
286  }
287  } else {
288  m_endLine = m_startLine;
289  m_endColumn = m_startColumn;
290  }
291 
292  USE(script->ContextId().To(&m_executionContextId));
293 
294  m_isModule = script->IsModule();
295 
296  m_script.Reset(m_isolate, script);
297  m_script.AnnotateStrongRetainer(kGlobalDebuggerScriptHandleLabel);
298  }
299 
300  String16 m_sourceMappingURL;
301  bool m_isLiveEdit = false;
302  bool m_isModule = false;
303  mutable String16 m_hash;
304  int m_startLine = 0;
305  int m_startColumn = 0;
306  int m_endLine = 0;
307  int m_endColumn = 0;
309 };
310 
311 class WasmVirtualScript : public V8DebuggerScript {
312  friend class V8DebuggerScript;
313 
314  public:
315  WasmVirtualScript(v8::Isolate* isolate, WasmTranslation* wasmTranslation,
316  v8::Local<v8::debug::WasmScript> script, String16 id,
317  String16 url, int functionIndex)
318  : V8DebuggerScript(isolate, std::move(id), std::move(url)),
319  m_script(isolate, script),
320  m_wasmTranslation(wasmTranslation),
321  m_functionIndex(functionIndex) {
322  m_script.AnnotateStrongRetainer(kGlobalDebuggerScriptHandleLabel);
323  m_executionContextId = script->ContextId().ToChecked();
324  }
325 
326  const String16& sourceMappingURL() const override { return emptyString(); }
327  bool isLiveEdit() const override { return false; }
328  bool isModule() const override { return false; }
329  void setSourceMappingURL(const String16&) override {}
330  void setSource(const String16&, bool, v8::debug::LiveEditResult*) override {
331  UNREACHABLE();
332  }
333  bool isSourceLoadedLazily() const override { return true; }
334  String16 source(size_t pos, size_t len) const override {
335  return m_wasmTranslation->GetSource(m_id, m_functionIndex)
336  .substring(pos, len);
337  }
338  int startLine() const override {
339  return m_wasmTranslation->GetStartLine(m_id, m_functionIndex);
340  }
341  int startColumn() const override {
342  return m_wasmTranslation->GetStartColumn(m_id, m_functionIndex);
343  }
344  int endLine() const override {
345  return m_wasmTranslation->GetEndLine(m_id, m_functionIndex);
346  }
347  int endColumn() const override {
348  return m_wasmTranslation->GetEndColumn(m_id, m_functionIndex);
349  }
350  int length() const override {
351  return static_cast<int>(source(0, UINT_MAX).length());
352  }
353 
354  bool getPossibleBreakpoints(
355  const v8::debug::Location& start, const v8::debug::Location& end,
356  bool restrictToFunction,
357  std::vector<v8::debug::BreakLocation>* locations) override {
358  v8::HandleScope scope(m_isolate);
359  v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
360  String16 v8ScriptId = String16::fromInteger(script->Id());
361 
362  v8::debug::Location translatedStart = start;
363  TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedStart,
364  scriptId(), v8ScriptId);
365 
366  v8::debug::Location translatedEnd = end;
367  if (translatedEnd.IsEmpty()) {
368  // Stop before the start of the next function.
369  translatedEnd =
370  v8::debug::Location(translatedStart.GetLineNumber() + 1, 0);
371  } else {
372  TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedEnd,
373  scriptId(), v8ScriptId);
374  }
375 
376  bool success = script->GetPossibleBreakpoints(
377  translatedStart, translatedEnd, restrictToFunction, locations);
378  for (v8::debug::BreakLocation& loc : *locations) {
379  TranslateV8LocationToProtocolLocation(m_wasmTranslation, &loc, v8ScriptId,
380  scriptId());
381  }
382  return success;
383  }
384 
385  void resetBlackboxedStateCache() override {}
386 
387  int offset(int lineNumber, int columnNumber) const override {
388  return kNoOffset;
389  }
390 
391  v8::debug::Location location(int offset) const override {
392  return v8::debug::Location();
393  }
394 
395  bool setBreakpoint(const String16& condition, v8::debug::Location* location,
396  int* id) const override {
397  v8::HandleScope scope(m_isolate);
398  v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
399  String16 v8ScriptId = String16::fromInteger(script->Id());
400 
401  TranslateProtocolLocationToV8Location(m_wasmTranslation, location,
402  scriptId(), v8ScriptId);
403  if (location->IsEmpty()) return false;
404  if (!script->SetBreakpoint(toV8String(m_isolate, condition), location, id))
405  return false;
406  TranslateV8LocationToProtocolLocation(m_wasmTranslation, location,
407  v8ScriptId, scriptId());
408  return true;
409  }
410 
411  const String16& hash() const override {
412  if (m_hash.isEmpty()) {
413  m_hash = m_wasmTranslation->GetHash(m_id, m_functionIndex);
414  }
415  return m_hash;
416  }
417 
418  private:
419  static const String16& emptyString() {
420  // On the heap and leaked so that no destructor needs to run at exit time.
421  static const String16* singleEmptyString = new String16;
422  return *singleEmptyString;
423  }
424 
425  v8::Local<v8::debug::Script> script() const override {
426  return m_script.Get(m_isolate);
427  }
428 
430  WasmTranslation* m_wasmTranslation;
431  int m_functionIndex;
432  mutable String16 m_hash;
433 };
434 
435 } // namespace
436 
437 std::unique_ptr<V8DebuggerScript> V8DebuggerScript::Create(
438  v8::Isolate* isolate, v8::Local<v8::debug::Script> scriptObj,
439  bool isLiveEdit, V8InspectorClient* client) {
440  return std::unique_ptr<ActualScript>(
441  new ActualScript(isolate, scriptObj, isLiveEdit, client));
442 }
443 
444 std::unique_ptr<V8DebuggerScript> V8DebuggerScript::CreateWasm(
445  v8::Isolate* isolate, WasmTranslation* wasmTranslation,
446  v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
447  String16 url, int functionIndex) {
448  return std::unique_ptr<WasmVirtualScript>(
449  new WasmVirtualScript(isolate, wasmTranslation, underlyingScript,
450  std::move(id), std::move(url), functionIndex));
451 }
452 
453 V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate, String16 id,
454  String16 url)
455  : m_id(std::move(id)), m_url(std::move(url)), m_isolate(isolate) {}
456 
457 V8DebuggerScript::~V8DebuggerScript() = default;
458 
459 void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
460  if (sourceURL.length() > 0) {
461  m_hasSourceURLComment = true;
462  m_url = sourceURL;
463  }
464 }
465 
466 bool V8DebuggerScript::setBreakpoint(const String16& condition,
467  v8::debug::Location* loc, int* id) const {
468  v8::HandleScope scope(m_isolate);
469  return script()->SetBreakpoint(toV8String(m_isolate, condition), loc, id);
470 }
471 } // namespace v8_inspector
int Length() const
Definition: api.cc:5175
Definition: v8.h:94
STL namespace.