Line data Source code
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/wasm-translation.h"
10 :
11 : namespace v8_inspector {
12 :
13 : namespace {
14 :
15 : const char hexDigits[17] = "0123456789ABCDEF";
16 :
17 129850 : void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
18 1168650 : for (size_t i = 0; i < 8; ++i) {
19 1038800 : UChar c = hexDigits[number & 0xF];
20 1038800 : destination->append(c);
21 1038800 : number >>= 4;
22 : }
23 129850 : }
24 :
25 : // Hash algorithm for substrings is described in "Über die Komplexität der
26 : // Multiplikation in
27 : // eingeschränkten Branchingprogrammmodellen" by Woelfe.
28 : // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
29 25970 : String16 calculateHash(const String16& str) {
30 : static uint64_t prime[] = {0x3FB75161, 0xAB1F4E4F, 0x82675BC5, 0xCD924D35,
31 : 0x81ABE279};
32 : static uint64_t random[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,
33 : 0xC3D2E1F0};
34 : static uint32_t randomOdd[] = {0xB4663807, 0xCC322BF5, 0xD4F91BBD, 0xA7BEA11D,
35 : 0x8F462907};
36 :
37 25970 : uint64_t hashes[] = {0, 0, 0, 0, 0};
38 25970 : uint64_t zi[] = {1, 1, 1, 1, 1};
39 :
40 : const size_t hashesSize = arraysize(hashes);
41 :
42 : size_t current = 0;
43 : const uint32_t* data = nullptr;
44 25970 : size_t sizeInBytes = sizeof(UChar) * str.length();
45 : data = reinterpret_cast<const uint32_t*>(str.characters16());
46 18348038 : for (size_t i = 0; i < sizeInBytes / 4; i += 4) {
47 : #if V8_TARGET_LITTLE_ENDIAN
48 18322068 : uint32_t v = data[i];
49 : #else
50 : uint32_t v = (data[i] << 16) | (data[i] >> 16);
51 : #endif
52 18322068 : uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
53 18322068 : hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
54 18322068 : zi[current] = (zi[current] * random[current]) % prime[current];
55 18322068 : current = current == hashesSize - 1 ? 0 : current + 1;
56 : }
57 25970 : if (sizeInBytes % 4) {
58 : uint32_t v = 0;
59 44796 : for (size_t i = sizeInBytes - sizeInBytes % 4; i < sizeInBytes; ++i) {
60 29864 : v <<= 8;
61 : #if V8_TARGET_LITTLE_ENDIAN
62 29864 : v |= reinterpret_cast<const uint8_t*>(data)[i];
63 : #else
64 : if (i % 2) {
65 : v |= reinterpret_cast<const uint8_t*>(data)[i - 1];
66 : } else {
67 : v |= reinterpret_cast<const uint8_t*>(data)[i + 1];
68 : }
69 : #endif
70 : }
71 14932 : uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
72 14932 : hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
73 14932 : zi[current] = (zi[current] * random[current]) % prime[current];
74 : current = current == hashesSize - 1 ? 0 : current + 1;
75 : }
76 :
77 129850 : for (size_t i = 0; i < hashesSize; ++i)
78 129850 : hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i];
79 :
80 25970 : String16Builder hash;
81 25970 : for (size_t i = 0; i < hashesSize; ++i) appendUnsignedAsHex(hashes[i], &hash);
82 51940 : return hash.toString();
83 : }
84 :
85 120 : void TranslateProtocolLocationToV8Location(WasmTranslation* wasmTranslation,
86 : v8::debug::Location* loc,
87 : const String16& scriptId,
88 : const String16& expectedV8ScriptId) {
89 120 : if (loc->IsEmpty()) return;
90 120 : int lineNumber = loc->GetLineNumber();
91 120 : int columnNumber = loc->GetColumnNumber();
92 120 : String16 translatedScriptId = scriptId;
93 : wasmTranslation->TranslateProtocolLocationToWasmScriptLocation(
94 120 : &translatedScriptId, &lineNumber, &columnNumber);
95 : DCHECK_EQ(expectedV8ScriptId.utf8(), translatedScriptId.utf8());
96 120 : *loc = v8::debug::Location(lineNumber, columnNumber);
97 : }
98 :
99 160 : void TranslateV8LocationToProtocolLocation(
100 : WasmTranslation* wasmTranslation, v8::debug::Location* loc,
101 : const String16& scriptId, const String16& expectedProtocolScriptId) {
102 160 : int lineNumber = loc->GetLineNumber();
103 160 : int columnNumber = loc->GetColumnNumber();
104 160 : String16 translatedScriptId = scriptId;
105 : wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
106 160 : &translatedScriptId, &lineNumber, &columnNumber);
107 : DCHECK_EQ(expectedProtocolScriptId.utf8(), translatedScriptId.utf8());
108 160 : *loc = v8::debug::Location(lineNumber, columnNumber);
109 160 : }
110 :
111 77661 : class ActualScript : public V8DebuggerScript {
112 : friend class V8DebuggerScript;
113 :
114 : public:
115 25887 : ActualScript(v8::Isolate* isolate, v8::Local<v8::debug::Script> script,
116 : bool isLiveEdit)
117 : : V8DebuggerScript(isolate, String16::fromInteger(script->Id()),
118 : GetNameOrSourceUrl(script)),
119 77661 : m_isLiveEdit(isLiveEdit) {
120 : v8::Local<v8::String> tmp;
121 53090 : if (script->SourceURL().ToLocal(&tmp)) m_sourceURL = toProtocolString(tmp);
122 51774 : if (script->SourceMappingURL().ToLocal(&tmp))
123 7466 : m_sourceMappingURL = toProtocolString(tmp);
124 25887 : m_startLine = script->LineOffset();
125 25887 : m_startColumn = script->ColumnOffset();
126 25887 : std::vector<int> lineEnds = script->LineEnds();
127 51774 : CHECK(lineEnds.size());
128 51774 : int source_length = lineEnds[lineEnds.size() - 1];
129 25887 : if (lineEnds.size()) {
130 25887 : m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
131 25887 : if (lineEnds.size() > 1) {
132 20428 : m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
133 : } else {
134 15673 : m_endColumn = source_length + m_startColumn;
135 : }
136 : } else {
137 0 : m_endLine = m_startLine;
138 0 : m_endColumn = m_startColumn;
139 : }
140 :
141 51774 : USE(script->ContextId().To(&m_executionContextId));
142 :
143 51774 : if (script->Source().ToLocal(&tmp)) {
144 51774 : m_source = toProtocolString(tmp);
145 : }
146 :
147 25887 : m_isModule = script->IsModule();
148 :
149 25887 : m_script.Reset(m_isolate, script);
150 25887 : }
151 :
152 25887 : bool isLiveEdit() const override { return m_isLiveEdit; }
153 25927 : bool isModule() const override { return m_isModule; }
154 :
155 25887 : const String16& sourceMappingURL() const override {
156 25887 : return m_sourceMappingURL;
157 : }
158 :
159 132 : void setSourceMappingURL(const String16& sourceMappingURL) override {
160 132 : m_sourceMappingURL = sourceMappingURL;
161 132 : }
162 :
163 35 : void setSource(const String16& newSource, bool preview,
164 : bool* stackChanged) override {
165 : DCHECK(!isModule());
166 35 : v8::HandleScope scope(m_isolate);
167 35 : v8::Local<v8::String> v8Source = toV8String(m_isolate, newSource);
168 70 : if (!m_script.Get(m_isolate)->SetScriptSource(v8Source, preview,
169 70 : stackChanged)) {
170 10 : return;
171 : }
172 25 : if (preview) return;
173 25 : m_source = newSource;
174 50 : m_hash = String16();
175 : }
176 :
177 220 : bool getPossibleBreakpoints(
178 : const v8::debug::Location& start, const v8::debug::Location& end,
179 : bool restrictToFunction,
180 : std::vector<v8::debug::BreakLocation>* locations) override {
181 220 : v8::HandleScope scope(m_isolate);
182 220 : v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
183 : std::vector<v8::debug::BreakLocation> allLocations;
184 220 : if (!script->GetPossibleBreakpoints(start, end, restrictToFunction,
185 220 : &allLocations)) {
186 : return false;
187 : }
188 430 : if (!allLocations.size()) return true;
189 190 : v8::debug::BreakLocation current = allLocations[0];
190 6820 : for (size_t i = 1; i < allLocations.size(); ++i) {
191 5300 : if (allLocations[i].GetLineNumber() == current.GetLineNumber() &&
192 4160 : allLocations[i].GetColumnNumber() == current.GetColumnNumber()) {
193 930 : if (allLocations[i].type() != v8::debug::kCommonBreakLocation) {
194 : DCHECK(allLocations[i].type() == v8::debug::kCallBreakLocation ||
195 : allLocations[i].type() == v8::debug::kReturnBreakLocation);
196 : // debugger can returns more then one break location at the same
197 : // source location, e.g. foo() - in this case there are two break
198 : // locations before foo: for statement and for function call, we can
199 : // merge them for inspector and report only one with call type.
200 395 : current = allLocations[i];
201 : }
202 : } else {
203 : // we assume that returned break locations are sorted.
204 : DCHECK(
205 : allLocations[i].GetLineNumber() > current.GetLineNumber() ||
206 : (allLocations[i].GetColumnNumber() >= current.GetColumnNumber() &&
207 : allLocations[i].GetLineNumber() == current.GetLineNumber()));
208 2755 : locations->push_back(current);
209 5510 : current = allLocations[i];
210 : }
211 : }
212 190 : locations->push_back(current);
213 410 : return true;
214 : }
215 :
216 52043 : void resetBlackboxedStateCache() override {
217 52043 : v8::HandleScope scope(m_isolate);
218 104086 : v8::debug::ResetBlackboxedStateCache(m_isolate, m_script.Get(m_isolate));
219 52043 : }
220 :
221 240 : int offset(int lineNumber, int columnNumber) const override {
222 240 : v8::HandleScope scope(m_isolate);
223 240 : return m_script.Get(m_isolate)->GetSourceOffset(
224 480 : v8::debug::Location(lineNumber, columnNumber));
225 : }
226 :
227 50 : v8::debug::Location location(int offset) const override {
228 50 : v8::HandleScope scope(m_isolate);
229 100 : return m_script.Get(m_isolate)->GetSourceLocation(offset);
230 : }
231 :
232 1899 : bool setBreakpoint(const String16& condition, v8::debug::Location* location,
233 : int* id) const override {
234 1899 : v8::HandleScope scope(m_isolate);
235 1899 : return script()->SetBreakpoint(toV8String(m_isolate, condition), location,
236 5697 : id);
237 : }
238 :
239 : private:
240 25887 : String16 GetNameOrSourceUrl(v8::Local<v8::debug::Script> script) {
241 : v8::Local<v8::String> name;
242 65643 : if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name))
243 12097 : return toProtocolString(name);
244 13790 : return String16();
245 : }
246 :
247 1899 : v8::Local<v8::debug::Script> script() const override {
248 3798 : return m_script.Get(m_isolate);
249 : }
250 :
251 : String16 m_sourceMappingURL;
252 : bool m_isLiveEdit = false;
253 : bool m_isModule = false;
254 : v8::Global<v8::debug::Script> m_script;
255 : };
256 :
257 249 : class WasmVirtualScript : public V8DebuggerScript {
258 : friend class V8DebuggerScript;
259 :
260 : public:
261 83 : WasmVirtualScript(v8::Isolate* isolate, WasmTranslation* wasmTranslation,
262 : v8::Local<v8::debug::WasmScript> script, String16 id,
263 : String16 url, String16 source)
264 : : V8DebuggerScript(isolate, std::move(id), std::move(url)),
265 : m_script(isolate, script),
266 332 : m_wasmTranslation(wasmTranslation) {
267 : int num_lines = 0;
268 : int last_newline = -1;
269 : size_t next_newline = source.find('\n', last_newline + 1);
270 609 : while (next_newline != String16::kNotFound) {
271 443 : last_newline = static_cast<int>(next_newline);
272 443 : next_newline = source.find('\n', last_newline + 1);
273 443 : ++num_lines;
274 : }
275 83 : m_endLine = num_lines;
276 83 : m_endColumn = static_cast<int>(source.length()) - last_newline - 1;
277 83 : m_source = std::move(source);
278 166 : m_executionContextId = script->ContextId().ToChecked();
279 83 : }
280 :
281 83 : const String16& sourceMappingURL() const override { return emptyString(); }
282 83 : bool isLiveEdit() const override { return false; }
283 83 : bool isModule() const override { return false; }
284 0 : void setSourceMappingURL(const String16&) override {}
285 0 : void setSource(const String16&, bool, bool*) override { UNREACHABLE(); }
286 :
287 30 : bool getPossibleBreakpoints(
288 : const v8::debug::Location& start, const v8::debug::Location& end,
289 : bool restrictToFunction,
290 : std::vector<v8::debug::BreakLocation>* locations) override {
291 30 : v8::HandleScope scope(m_isolate);
292 30 : v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
293 30 : String16 v8ScriptId = String16::fromInteger(script->Id());
294 :
295 30 : v8::debug::Location translatedStart = start;
296 : TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedStart,
297 30 : scriptId(), v8ScriptId);
298 :
299 30 : v8::debug::Location translatedEnd = end;
300 30 : if (translatedEnd.IsEmpty()) {
301 : // Stop before the start of the next function.
302 : translatedEnd =
303 10 : v8::debug::Location(translatedStart.GetLineNumber() + 1, 0);
304 : } else {
305 : TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedEnd,
306 20 : scriptId(), v8ScriptId);
307 : }
308 :
309 : bool success = script->GetPossibleBreakpoints(
310 30 : translatedStart, translatedEnd, restrictToFunction, locations);
311 150 : for (v8::debug::BreakLocation& loc : *locations) {
312 : TranslateV8LocationToProtocolLocation(m_wasmTranslation, &loc, v8ScriptId,
313 90 : scriptId());
314 : }
315 30 : return success;
316 : }
317 :
318 166 : void resetBlackboxedStateCache() override {}
319 :
320 5 : int offset(int lineNumber, int columnNumber) const override {
321 5 : return kNoOffset;
322 : }
323 :
324 0 : v8::debug::Location location(int offset) const override {
325 0 : return v8::debug::Location();
326 : }
327 :
328 70 : bool setBreakpoint(const String16& condition, v8::debug::Location* location,
329 : int* id) const override {
330 70 : v8::HandleScope scope(m_isolate);
331 70 : v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
332 70 : String16 v8ScriptId = String16::fromInteger(script->Id());
333 :
334 : TranslateProtocolLocationToV8Location(m_wasmTranslation, location,
335 70 : scriptId(), v8ScriptId);
336 70 : if (location->IsEmpty()) return false;
337 70 : if (!script->SetBreakpoint(toV8String(m_isolate, condition), location, id))
338 : return false;
339 : TranslateV8LocationToProtocolLocation(m_wasmTranslation, location,
340 70 : v8ScriptId, scriptId());
341 140 : return true;
342 : }
343 :
344 : private:
345 83 : static const String16& emptyString() {
346 83 : static const String16 singleEmptyString;
347 83 : return singleEmptyString;
348 : }
349 :
350 0 : v8::Local<v8::debug::Script> script() const override {
351 0 : return m_script.Get(m_isolate);
352 : }
353 :
354 : v8::Global<v8::debug::WasmScript> m_script;
355 : WasmTranslation* m_wasmTranslation;
356 : };
357 :
358 : } // namespace
359 :
360 25887 : std::unique_ptr<V8DebuggerScript> V8DebuggerScript::Create(
361 : v8::Isolate* isolate, v8::Local<v8::debug::Script> scriptObj,
362 : bool isLiveEdit) {
363 : return std::unique_ptr<ActualScript>(
364 51774 : new ActualScript(isolate, scriptObj, isLiveEdit));
365 : }
366 :
367 83 : std::unique_ptr<V8DebuggerScript> V8DebuggerScript::CreateWasm(
368 : v8::Isolate* isolate, WasmTranslation* wasmTranslation,
369 : v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
370 : String16 url, String16 source) {
371 : return std::unique_ptr<WasmVirtualScript>(
372 : new WasmVirtualScript(isolate, wasmTranslation, underlyingScript,
373 332 : std::move(id), std::move(url), std::move(source)));
374 : }
375 :
376 25970 : V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate, String16 id,
377 : String16 url)
378 25970 : : m_id(std::move(id)), m_url(std::move(url)), m_isolate(isolate) {}
379 :
380 51940 : V8DebuggerScript::~V8DebuggerScript() {}
381 :
382 152359 : const String16& V8DebuggerScript::sourceURL() const {
383 152359 : return m_sourceURL.isEmpty() ? m_url : m_sourceURL;
384 : }
385 :
386 26020 : const String16& V8DebuggerScript::hash() const {
387 51990 : if (m_hash.isEmpty()) m_hash = calculateHash(source());
388 : DCHECK(!m_hash.isEmpty());
389 26020 : return m_hash;
390 : }
391 :
392 132 : void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
393 132 : m_sourceURL = sourceURL;
394 132 : }
395 :
396 0 : bool V8DebuggerScript::setBreakpoint(const String16& condition,
397 : v8::debug::Location* loc, int* id) const {
398 0 : v8::HandleScope scope(m_isolate);
399 0 : return script()->SetBreakpoint(toV8String(m_isolate, condition), loc, id);
400 : }
401 :
402 : } // namespace v8_inspector
|