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/protocol-platform.h"
8 #include "src/inspector/string-util.h"
9 
10 namespace v8_inspector {
11 
12 static const char hexDigits[17] = "0123456789ABCDEF";
13 
appendUnsignedAsHex(uint64_t number,String16Builder * destination)14 static void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
15   for (size_t i = 0; i < 8; ++i) {
16     UChar c = hexDigits[number & 0xF];
17     destination->append(c);
18     number >>= 4;
19   }
20 }
21 
22 // Hash algorithm for substrings is described in "Über die Komplexität der
23 // Multiplikation in
24 // eingeschränkten Branchingprogrammmodellen" by Woelfe.
25 // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
calculateHash(const String16 & str)26 static String16 calculateHash(const String16& str) {
27   static uint64_t prime[] = {0x3FB75161, 0xAB1F4E4F, 0x82675BC5, 0xCD924D35,
28                              0x81ABE279};
29   static uint64_t random[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,
30                               0xC3D2E1F0};
31   static uint32_t randomOdd[] = {0xB4663807, 0xCC322BF5, 0xD4F91BBD, 0xA7BEA11D,
32                                  0x8F462907};
33 
34   uint64_t hashes[] = {0, 0, 0, 0, 0};
35   uint64_t zi[] = {1, 1, 1, 1, 1};
36 
37   const size_t hashesSize = arraysize(hashes);
38 
39   size_t current = 0;
40   const uint32_t* data = nullptr;
41   size_t sizeInBytes = sizeof(UChar) * str.length();
42   data = reinterpret_cast<const uint32_t*>(str.characters16());
43   for (size_t i = 0; i < sizeInBytes / 4; i += 4) {
44     uint32_t v = data[i];
45     uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
46     hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
47     zi[current] = (zi[current] * random[current]) % prime[current];
48     current = current == hashesSize - 1 ? 0 : current + 1;
49   }
50   if (sizeInBytes % 4) {
51     uint32_t v = 0;
52     for (size_t i = sizeInBytes - sizeInBytes % 4; i < sizeInBytes; ++i) {
53       v <<= 8;
54       v |= reinterpret_cast<const uint8_t*>(data)[i];
55     }
56     uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
57     hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
58     zi[current] = (zi[current] * random[current]) % prime[current];
59     current = current == hashesSize - 1 ? 0 : current + 1;
60   }
61 
62   for (size_t i = 0; i < hashesSize; ++i)
63     hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i];
64 
65   String16Builder hash;
66   for (size_t i = 0; i < hashesSize; ++i) appendUnsignedAsHex(hashes[i], &hash);
67   return hash.toString();
68 }
69 
V8DebuggerScript(v8::Isolate * isolate,v8::Local<v8::DebugInterface::Script> script,bool isLiveEdit)70 V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate,
71                                    v8::Local<v8::DebugInterface::Script> script,
72                                    bool isLiveEdit) {
73   m_isolate = script->GetIsolate();
74   m_id = String16::fromInteger(script->Id());
75   v8::Local<v8::String> tmp;
76   if (script->Name().ToLocal(&tmp)) m_url = toProtocolString(tmp);
77   if (script->SourceURL().ToLocal(&tmp)) {
78     m_sourceURL = toProtocolString(tmp);
79     if (m_url.isEmpty()) m_url = toProtocolString(tmp);
80   }
81   if (script->SourceMappingURL().ToLocal(&tmp))
82     m_sourceMappingURL = toProtocolString(tmp);
83   m_startLine = script->LineOffset();
84   m_startColumn = script->ColumnOffset();
85   std::vector<int> lineEnds = script->LineEnds();
86   CHECK(lineEnds.size());
87   int source_length = lineEnds[lineEnds.size() - 1];
88   if (lineEnds.size()) {
89     m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
90     if (lineEnds.size() > 1) {
91       m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
92     } else {
93       m_endColumn = source_length + m_startColumn;
94     }
95   } else {
96     m_endLine = m_startLine;
97     m_endColumn = m_startColumn;
98   }
99 
100   if (script->ContextData().ToLocal(&tmp)) {
101     String16 contextData = toProtocolString(tmp);
102     size_t firstComma = contextData.find(",", 0);
103     size_t secondComma = firstComma != String16::kNotFound
104                              ? contextData.find(",", firstComma + 1)
105                              : String16::kNotFound;
106     if (secondComma != String16::kNotFound) {
107       String16 executionContextId =
108           contextData.substring(firstComma + 1, secondComma - firstComma - 1);
109       bool isOk = false;
110       m_executionContextId = executionContextId.toInteger(&isOk);
111       if (!isOk) m_executionContextId = 0;
112       m_executionContextAuxData = contextData.substring(secondComma + 1);
113     }
114   }
115 
116   m_isLiveEdit = isLiveEdit;
117 
118   if (script->Source().ToLocal(&tmp)) {
119     m_source.Reset(m_isolate, tmp);
120     String16 source = toProtocolString(tmp);
121     m_hash = calculateHash(source);
122     // V8 will not count last line if script source ends with \n.
123     if (source.length() > 1 && source[source.length() - 1] == '\n') {
124       m_endLine++;
125       m_endColumn = 0;
126     }
127   }
128 
129   m_script.Reset(m_isolate, script);
130 }
131 
~V8DebuggerScript()132 V8DebuggerScript::~V8DebuggerScript() {}
133 
sourceURL() const134 const String16& V8DebuggerScript::sourceURL() const {
135   return m_sourceURL.isEmpty() ? m_url : m_sourceURL;
136 }
137 
source(v8::Isolate * isolate) const138 v8::Local<v8::String> V8DebuggerScript::source(v8::Isolate* isolate) const {
139   return m_source.Get(isolate);
140 }
141 
setSourceURL(const String16 & sourceURL)142 void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
143   m_sourceURL = sourceURL;
144 }
145 
setSourceMappingURL(const String16 & sourceMappingURL)146 void V8DebuggerScript::setSourceMappingURL(const String16& sourceMappingURL) {
147   m_sourceMappingURL = sourceMappingURL;
148 }
149 
setSource(v8::Local<v8::String> source)150 void V8DebuggerScript::setSource(v8::Local<v8::String> source) {
151   m_source.Reset(m_isolate, source);
152   m_hash = calculateHash(toProtocolString(source));
153 }
154 
getPossibleBreakpoints(const v8::DebugInterface::Location & start,const v8::DebugInterface::Location & end,std::vector<v8::DebugInterface::Location> * locations)155 bool V8DebuggerScript::getPossibleBreakpoints(
156     const v8::DebugInterface::Location& start,
157     const v8::DebugInterface::Location& end,
158     std::vector<v8::DebugInterface::Location>* locations) {
159   v8::HandleScope scope(m_isolate);
160   v8::Local<v8::DebugInterface::Script> script = m_script.Get(m_isolate);
161   return script->GetPossibleBreakpoints(start, end, locations);
162 }
163 
164 }  // namespace v8_inspector
165