LCOV - code coverage report
Current view: top level - src/inspector - v8-debugger-script.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 145 151 96.0 %
Date: 2017-04-26 Functions: 32 36 88.9 %

          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

Generated by: LCOV version 1.10