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 183380 : void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
18 1650420 : for (size_t i = 0; i < 8; ++i) {
19 1467040 : UChar c = hexDigits[number & 0xF];
20 1467040 : destination->append(c);
21 1467040 : number >>= 4;
22 : }
23 183380 : }
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 36676 : 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 36676 : uint64_t hashes[] = {0, 0, 0, 0, 0};
38 36676 : 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 36676 : size_t sizeInBytes = sizeof(UChar) * str.length();
45 : data = reinterpret_cast<const uint32_t*>(str.characters16());
46 25100396 : for (size_t i = 0; i < sizeInBytes / 4; i += 4) {
47 : #if V8_TARGET_LITTLE_ENDIAN
48 25063720 : uint32_t v = data[i];
49 : #else
50 : uint32_t v = (data[i] << 16) | (data[i] >> 16);
51 : #endif
52 25063720 : uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
53 25063720 : hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
54 25063720 : zi[current] = (zi[current] * random[current]) % prime[current];
55 25063720 : current = current == hashesSize - 1 ? 0 : current + 1;
56 : }
57 36676 : if (sizeInBytes % 4) {
58 : uint32_t v = 0;
59 52296 : for (size_t i = sizeInBytes - sizeInBytes % 4; i < sizeInBytes; ++i) {
60 34864 : v <<= 8;
61 : #if V8_TARGET_LITTLE_ENDIAN
62 34864 : 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 17432 : uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
72 17432 : hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
73 17432 : zi[current] = (zi[current] * random[current]) % prime[current];
74 : current = current == hashesSize - 1 ? 0 : current + 1;
75 : }
76 :
77 183380 : for (size_t i = 0; i < hashesSize; ++i)
78 183380 : hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i];
79 :
80 36676 : String16Builder hash;
81 36676 : for (size_t i = 0; i < hashesSize; ++i) appendUnsignedAsHex(hashes[i], &hash);
82 73352 : return hash.toString();
83 : }
84 :
85 60 : void TranslateProtocolLocationToV8Location(WasmTranslation* wasmTranslation,
86 : v8::debug::Location* loc,
87 : const String16& scriptId,
88 : const String16& expectedV8ScriptId) {
89 60 : if (loc->IsEmpty()) return;
90 60 : int lineNumber = loc->GetLineNumber();
91 60 : int columnNumber = loc->GetColumnNumber();
92 : String16 translatedScriptId = scriptId;
93 : wasmTranslation->TranslateProtocolLocationToWasmScriptLocation(
94 60 : &translatedScriptId, &lineNumber, &columnNumber);
95 : DCHECK_EQ(expectedV8ScriptId.utf8(), translatedScriptId.utf8());
96 60 : *loc = v8::debug::Location(lineNumber, columnNumber);
97 : }
98 :
99 108 : void TranslateV8LocationToProtocolLocation(
100 : WasmTranslation* wasmTranslation, v8::debug::Location* loc,
101 : const String16& scriptId, const String16& expectedProtocolScriptId) {
102 108 : int lineNumber = loc->GetLineNumber();
103 108 : int columnNumber = loc->GetColumnNumber();
104 : String16 translatedScriptId = scriptId;
105 : wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
106 108 : &translatedScriptId, &lineNumber, &columnNumber);
107 : DCHECK_EQ(expectedProtocolScriptId.utf8(), translatedScriptId.utf8());
108 108 : *loc = v8::debug::Location(lineNumber, columnNumber);
109 108 : }
110 :
111 109728 : class ActualScript : public V8DebuggerScript {
112 : friend class V8DebuggerScript;
113 :
114 : public:
115 36576 : ActualScript(v8::Isolate* isolate, v8::Local<v8::debug::Script> script,
116 : bool isLiveEdit)
117 : : V8DebuggerScript(isolate, String16::fromInteger(script->Id()),
118 : GetNameOrSourceUrl(script)),
119 146304 : m_isLiveEdit(isLiveEdit) {
120 : v8::Local<v8::String> tmp;
121 74345 : if (script->SourceURL().ToLocal(&tmp)) m_sourceURL = toProtocolString(tmp);
122 73152 : if (script->SourceMappingURL().ToLocal(&tmp))
123 6734 : m_sourceMappingURL = toProtocolString(tmp);
124 36576 : m_startLine = script->LineOffset();
125 36576 : m_startColumn = script->ColumnOffset();
126 36576 : std::vector<int> lineEnds = script->LineEnds();
127 73152 : CHECK(lineEnds.size());
128 73152 : int source_length = lineEnds[lineEnds.size() - 1];
129 36576 : if (lineEnds.size()) {
130 36576 : m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
131 36576 : if (lineEnds.size() > 1) {
132 30020 : m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
133 : } else {
134 21566 : m_endColumn = source_length + m_startColumn;
135 : }
136 : } else {
137 0 : m_endLine = m_startLine;
138 0 : m_endColumn = m_startColumn;
139 : }
140 :
141 73152 : USE(script->ContextId().To(&m_executionContextId));
142 :
143 73152 : if (script->Source().ToLocal(&tmp)) {
144 73152 : m_source = toProtocolString(tmp);
145 : }
146 :
147 36576 : m_isModule = script->IsModule();
148 :
149 36576 : m_script.Reset(m_isolate, script);
150 36576 : }
151 :
152 36576 : bool isLiveEdit() const override { return m_isLiveEdit; }
153 36600 : bool isModule() const override { return m_isModule; }
154 :
155 36576 : const String16& sourceMappingURL() const override {
156 36576 : return m_sourceMappingURL;
157 : }
158 :
159 184 : void setSourceMappingURL(const String16& sourceMappingURL) override {
160 : m_sourceMappingURL = sourceMappingURL;
161 184 : }
162 :
163 216 : bool getPossibleBreakpoints(
164 : const v8::debug::Location& start, const v8::debug::Location& end,
165 : bool restrictToFunction,
166 : std::vector<v8::debug::BreakLocation>* locations) override {
167 216 : v8::HandleScope scope(m_isolate);
168 216 : v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
169 : std::vector<v8::debug::BreakLocation> allLocations;
170 216 : if (!script->GetPossibleBreakpoints(start, end, restrictToFunction,
171 216 : &allLocations)) {
172 : return false;
173 : }
174 432 : if (!allLocations.size()) return true;
175 186 : v8::debug::BreakLocation current = allLocations[0];
176 6696 : for (size_t i = 1; i < allLocations.size(); ++i) {
177 5196 : if (allLocations[i].GetLineNumber() == current.GetLineNumber() &&
178 4068 : allLocations[i].GetColumnNumber() == current.GetColumnNumber()) {
179 1236 : if (allLocations[i].type() != v8::debug::kCommonBreakLocation) {
180 : DCHECK(allLocations[i].type() == v8::debug::kCallBreakLocation ||
181 : allLocations[i].type() == v8::debug::kReturnBreakLocation);
182 : // debugger can returns more then one break location at the same
183 : // source location, e.g. foo() - in this case there are two break
184 : // locations before foo: for statement and for function call, we can
185 : // merge them for inspector and report only one with call type.
186 504 : current = allLocations[i];
187 : }
188 : } else {
189 : // we assume that returned break locations are sorted.
190 : DCHECK(
191 : allLocations[i].GetLineNumber() > current.GetLineNumber() ||
192 : (allLocations[i].GetColumnNumber() >= current.GetColumnNumber() &&
193 : allLocations[i].GetLineNumber() == current.GetLineNumber()));
194 2544 : locations->push_back(current);
195 5088 : current = allLocations[i];
196 : }
197 : }
198 186 : locations->push_back(current);
199 402 : return true;
200 : }
201 :
202 73324 : void resetBlackboxedStateCache() override {
203 73324 : v8::HandleScope scope(m_isolate);
204 146648 : v8::debug::ResetBlackboxedStateCache(m_isolate, m_script.Get(m_isolate));
205 73324 : }
206 :
207 162 : int offset(int lineNumber, int columnNumber) const override {
208 162 : v8::HandleScope scope(m_isolate);
209 162 : return m_script.Get(m_isolate)->GetSourceOffset(
210 324 : v8::debug::Location(lineNumber, columnNumber));
211 : }
212 :
213 30 : v8::debug::Location location(int offset) const override {
214 30 : v8::HandleScope scope(m_isolate);
215 60 : return m_script.Get(m_isolate)->GetSourceLocation(offset);
216 : }
217 :
218 : private:
219 36576 : String16 GetNameOrSourceUrl(v8::Local<v8::debug::Script> script) {
220 : v8::Local<v8::String> name;
221 93965 : if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name))
222 15871 : return toProtocolString(name);
223 : return String16();
224 : }
225 :
226 : String16 m_sourceMappingURL;
227 : bool m_isLiveEdit = false;
228 : bool m_isModule = false;
229 : v8::Global<v8::debug::Script> m_script;
230 : };
231 :
232 300 : class WasmVirtualScript : public V8DebuggerScript {
233 : friend class V8DebuggerScript;
234 :
235 : public:
236 100 : WasmVirtualScript(v8::Isolate* isolate, WasmTranslation* wasmTranslation,
237 : v8::Local<v8::debug::WasmScript> script, String16 id,
238 : String16 url, String16 source)
239 : : V8DebuggerScript(isolate, std::move(id), std::move(url)),
240 : m_script(isolate, script),
241 200 : m_wasmTranslation(wasmTranslation) {
242 : int num_lines = 0;
243 : int last_newline = -1;
244 : size_t next_newline = source.find('\n', last_newline + 1);
245 676 : while (next_newline != String16::kNotFound) {
246 476 : last_newline = static_cast<int>(next_newline);
247 476 : next_newline = source.find('\n', last_newline + 1);
248 476 : ++num_lines;
249 : }
250 100 : m_endLine = num_lines;
251 100 : m_endColumn = static_cast<int>(source.length()) - last_newline - 1;
252 : m_source = std::move(source);
253 100 : }
254 :
255 100 : const String16& sourceMappingURL() const override { return emptyString(); }
256 100 : bool isLiveEdit() const override { return false; }
257 100 : bool isModule() const override { return false; }
258 0 : void setSourceMappingURL(const String16&) override {}
259 :
260 36 : bool getPossibleBreakpoints(
261 : const v8::debug::Location& start, const v8::debug::Location& end,
262 : bool restrictToFunction,
263 : std::vector<v8::debug::BreakLocation>* locations) override {
264 36 : v8::HandleScope scope(m_isolate);
265 36 : v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
266 36 : String16 v8ScriptId = String16::fromInteger(script->Id());
267 :
268 36 : v8::debug::Location translatedStart = start;
269 : TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedStart,
270 36 : scriptId(), v8ScriptId);
271 :
272 36 : v8::debug::Location translatedEnd = end;
273 36 : if (translatedEnd.IsEmpty()) {
274 : // Stop before the start of the next function.
275 : translatedEnd =
276 12 : v8::debug::Location(translatedStart.GetLineNumber() + 1, 0);
277 : } else {
278 : TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedEnd,
279 24 : scriptId(), v8ScriptId);
280 : }
281 :
282 : bool success = script->GetPossibleBreakpoints(
283 36 : translatedStart, translatedEnd, restrictToFunction, locations);
284 180 : for (v8::debug::BreakLocation& loc : *locations) {
285 : TranslateV8LocationToProtocolLocation(m_wasmTranslation, &loc, v8ScriptId,
286 108 : scriptId());
287 : }
288 36 : return success;
289 : }
290 :
291 200 : void resetBlackboxedStateCache() override {}
292 :
293 6 : int offset(int lineNumber, int columnNumber) const override {
294 6 : return kNoOffset;
295 : }
296 :
297 0 : v8::debug::Location location(int offset) const override {
298 0 : return v8::debug::Location();
299 : }
300 :
301 : private:
302 100 : static const String16& emptyString() {
303 142 : static const String16 singleEmptyString;
304 100 : return singleEmptyString;
305 : }
306 :
307 : v8::Global<v8::debug::WasmScript> m_script;
308 : WasmTranslation* m_wasmTranslation;
309 : };
310 :
311 : } // namespace
312 :
313 36576 : std::unique_ptr<V8DebuggerScript> V8DebuggerScript::Create(
314 : v8::Isolate* isolate, v8::Local<v8::debug::Script> scriptObj,
315 : bool isLiveEdit) {
316 : return std::unique_ptr<ActualScript>(
317 73152 : new ActualScript(isolate, scriptObj, isLiveEdit));
318 : }
319 :
320 100 : std::unique_ptr<V8DebuggerScript> V8DebuggerScript::CreateWasm(
321 : v8::Isolate* isolate, WasmTranslation* wasmTranslation,
322 : v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
323 : String16 url, String16 source) {
324 : return std::unique_ptr<WasmVirtualScript>(
325 : new WasmVirtualScript(isolate, wasmTranslation, underlyingScript,
326 500 : std::move(id), std::move(url), std::move(source)));
327 : }
328 :
329 0 : V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate, String16 id,
330 : String16 url)
331 73352 : : m_id(std::move(id)), m_url(std::move(url)), m_isolate(isolate) {}
332 :
333 73352 : V8DebuggerScript::~V8DebuggerScript() {}
334 :
335 37736 : const String16& V8DebuggerScript::sourceURL() const {
336 37736 : return m_sourceURL.isEmpty() ? m_url : m_sourceURL;
337 : }
338 :
339 36676 : const String16& V8DebuggerScript::hash() const {
340 73352 : if (m_hash.isEmpty()) m_hash = calculateHash(source());
341 : DCHECK(!m_hash.isEmpty());
342 36676 : return m_hash;
343 : }
344 :
345 184 : void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
346 : m_sourceURL = sourceURL;
347 184 : }
348 :
349 : } // namespace v8_inspector
|