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 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 Komplexitt der 23 // Multiplikation in 24 // eingeschrnkten Branchingprogrammmodellen" by Woelfe. 25 // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000 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 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 132 V8DebuggerScript::~V8DebuggerScript() {} 133 134 const String16& V8DebuggerScript::sourceURL() const { 135 return m_sourceURL.isEmpty() ? m_url : m_sourceURL; 136 } 137 138 v8::Local<v8::String> V8DebuggerScript::source(v8::Isolate* isolate) const { 139 return m_source.Get(isolate); 140 } 141 142 void V8DebuggerScript::setSourceURL(const String16& sourceURL) { 143 m_sourceURL = sourceURL; 144 } 145 146 void V8DebuggerScript::setSourceMappingURL(const String16& sourceMappingURL) { 147 m_sourceMappingURL = sourceMappingURL; 148 } 149 150 void V8DebuggerScript::setSource(v8::Local<v8::String> source) { 151 m_source.Reset(m_isolate, source); 152 m_hash = calculateHash(toProtocolString(source)); 153 } 154 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