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