LCOV - code coverage report
Current view: top level - src/inspector - v8-debugger-script.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 176 187 94.1 %
Date: 2017-10-20 Functions: 37 43 86.0 %

          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      129850 : void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
      18     1168650 :   for (size_t i = 0; i < 8; ++i) {
      19     1038800 :     UChar c = hexDigits[number & 0xF];
      20     1038800 :     destination->append(c);
      21     1038800 :     number >>= 4;
      22             :   }
      23      129850 : }
      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       25970 : 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       25970 :   uint64_t hashes[] = {0, 0, 0, 0, 0};
      38       25970 :   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       25970 :   size_t sizeInBytes = sizeof(UChar) * str.length();
      45             :   data = reinterpret_cast<const uint32_t*>(str.characters16());
      46    18348038 :   for (size_t i = 0; i < sizeInBytes / 4; i += 4) {
      47             : #if V8_TARGET_LITTLE_ENDIAN
      48    18322068 :     uint32_t v = data[i];
      49             : #else
      50             :     uint32_t v = (data[i] << 16) | (data[i] >> 16);
      51             : #endif
      52    18322068 :     uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
      53    18322068 :     hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
      54    18322068 :     zi[current] = (zi[current] * random[current]) % prime[current];
      55    18322068 :     current = current == hashesSize - 1 ? 0 : current + 1;
      56             :   }
      57       25970 :   if (sizeInBytes % 4) {
      58             :     uint32_t v = 0;
      59       44796 :     for (size_t i = sizeInBytes - sizeInBytes % 4; i < sizeInBytes; ++i) {
      60       29864 :       v <<= 8;
      61             : #if V8_TARGET_LITTLE_ENDIAN
      62       29864 :       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       14932 :     uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
      72       14932 :     hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
      73       14932 :     zi[current] = (zi[current] * random[current]) % prime[current];
      74             :     current = current == hashesSize - 1 ? 0 : current + 1;
      75             :   }
      76             : 
      77      129850 :   for (size_t i = 0; i < hashesSize; ++i)
      78      129850 :     hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i];
      79             : 
      80       25970 :   String16Builder hash;
      81       25970 :   for (size_t i = 0; i < hashesSize; ++i) appendUnsignedAsHex(hashes[i], &hash);
      82       51940 :   return hash.toString();
      83             : }
      84             : 
      85         120 : void TranslateProtocolLocationToV8Location(WasmTranslation* wasmTranslation,
      86             :                                            v8::debug::Location* loc,
      87             :                                            const String16& scriptId,
      88             :                                            const String16& expectedV8ScriptId) {
      89         120 :   if (loc->IsEmpty()) return;
      90         120 :   int lineNumber = loc->GetLineNumber();
      91         120 :   int columnNumber = loc->GetColumnNumber();
      92         120 :   String16 translatedScriptId = scriptId;
      93             :   wasmTranslation->TranslateProtocolLocationToWasmScriptLocation(
      94         120 :       &translatedScriptId, &lineNumber, &columnNumber);
      95             :   DCHECK_EQ(expectedV8ScriptId.utf8(), translatedScriptId.utf8());
      96         120 :   *loc = v8::debug::Location(lineNumber, columnNumber);
      97             : }
      98             : 
      99         160 : void TranslateV8LocationToProtocolLocation(
     100             :     WasmTranslation* wasmTranslation, v8::debug::Location* loc,
     101             :     const String16& scriptId, const String16& expectedProtocolScriptId) {
     102         160 :   int lineNumber = loc->GetLineNumber();
     103         160 :   int columnNumber = loc->GetColumnNumber();
     104         160 :   String16 translatedScriptId = scriptId;
     105             :   wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
     106         160 :       &translatedScriptId, &lineNumber, &columnNumber);
     107             :   DCHECK_EQ(expectedProtocolScriptId.utf8(), translatedScriptId.utf8());
     108         160 :   *loc = v8::debug::Location(lineNumber, columnNumber);
     109         160 : }
     110             : 
     111       77661 : class ActualScript : public V8DebuggerScript {
     112             :   friend class V8DebuggerScript;
     113             : 
     114             :  public:
     115       25887 :   ActualScript(v8::Isolate* isolate, v8::Local<v8::debug::Script> script,
     116             :                bool isLiveEdit)
     117             :       : V8DebuggerScript(isolate, String16::fromInteger(script->Id()),
     118             :                          GetNameOrSourceUrl(script)),
     119       77661 :         m_isLiveEdit(isLiveEdit) {
     120             :     v8::Local<v8::String> tmp;
     121       53090 :     if (script->SourceURL().ToLocal(&tmp)) m_sourceURL = toProtocolString(tmp);
     122       51774 :     if (script->SourceMappingURL().ToLocal(&tmp))
     123        7466 :       m_sourceMappingURL = toProtocolString(tmp);
     124       25887 :     m_startLine = script->LineOffset();
     125       25887 :     m_startColumn = script->ColumnOffset();
     126       25887 :     std::vector<int> lineEnds = script->LineEnds();
     127       51774 :     CHECK(lineEnds.size());
     128       51774 :     int source_length = lineEnds[lineEnds.size() - 1];
     129       25887 :     if (lineEnds.size()) {
     130       25887 :       m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
     131       25887 :       if (lineEnds.size() > 1) {
     132       20428 :         m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
     133             :       } else {
     134       15673 :         m_endColumn = source_length + m_startColumn;
     135             :       }
     136             :     } else {
     137           0 :       m_endLine = m_startLine;
     138           0 :       m_endColumn = m_startColumn;
     139             :     }
     140             : 
     141       51774 :     USE(script->ContextId().To(&m_executionContextId));
     142             : 
     143       51774 :     if (script->Source().ToLocal(&tmp)) {
     144       51774 :       m_source = toProtocolString(tmp);
     145             :     }
     146             : 
     147       25887 :     m_isModule = script->IsModule();
     148             : 
     149       25887 :     m_script.Reset(m_isolate, script);
     150       25887 :   }
     151             : 
     152       25887 :   bool isLiveEdit() const override { return m_isLiveEdit; }
     153       25927 :   bool isModule() const override { return m_isModule; }
     154             : 
     155       25887 :   const String16& sourceMappingURL() const override {
     156       25887 :     return m_sourceMappingURL;
     157             :   }
     158             : 
     159         132 :   void setSourceMappingURL(const String16& sourceMappingURL) override {
     160         132 :     m_sourceMappingURL = sourceMappingURL;
     161         132 :   }
     162             : 
     163          35 :   void setSource(const String16& newSource, bool preview,
     164             :                  bool* stackChanged) override {
     165             :     DCHECK(!isModule());
     166          35 :     v8::HandleScope scope(m_isolate);
     167          35 :     v8::Local<v8::String> v8Source = toV8String(m_isolate, newSource);
     168          70 :     if (!m_script.Get(m_isolate)->SetScriptSource(v8Source, preview,
     169          70 :                                                   stackChanged)) {
     170          10 :       return;
     171             :     }
     172          25 :     if (preview) return;
     173          25 :     m_source = newSource;
     174          50 :     m_hash = String16();
     175             :   }
     176             : 
     177         220 :   bool getPossibleBreakpoints(
     178             :       const v8::debug::Location& start, const v8::debug::Location& end,
     179             :       bool restrictToFunction,
     180             :       std::vector<v8::debug::BreakLocation>* locations) override {
     181         220 :     v8::HandleScope scope(m_isolate);
     182         220 :     v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
     183             :     std::vector<v8::debug::BreakLocation> allLocations;
     184         220 :     if (!script->GetPossibleBreakpoints(start, end, restrictToFunction,
     185         220 :                                         &allLocations)) {
     186             :       return false;
     187             :     }
     188         430 :     if (!allLocations.size()) return true;
     189         190 :     v8::debug::BreakLocation current = allLocations[0];
     190        6820 :     for (size_t i = 1; i < allLocations.size(); ++i) {
     191        5300 :       if (allLocations[i].GetLineNumber() == current.GetLineNumber() &&
     192        4160 :           allLocations[i].GetColumnNumber() == current.GetColumnNumber()) {
     193         930 :         if (allLocations[i].type() != v8::debug::kCommonBreakLocation) {
     194             :           DCHECK(allLocations[i].type() == v8::debug::kCallBreakLocation ||
     195             :                  allLocations[i].type() == v8::debug::kReturnBreakLocation);
     196             :           // debugger can returns more then one break location at the same
     197             :           // source location, e.g. foo() - in this case there are two break
     198             :           // locations before foo: for statement and for function call, we can
     199             :           // merge them for inspector and report only one with call type.
     200         395 :           current = allLocations[i];
     201             :         }
     202             :       } else {
     203             :         // we assume that returned break locations are sorted.
     204             :         DCHECK(
     205             :             allLocations[i].GetLineNumber() > current.GetLineNumber() ||
     206             :             (allLocations[i].GetColumnNumber() >= current.GetColumnNumber() &&
     207             :              allLocations[i].GetLineNumber() == current.GetLineNumber()));
     208        2755 :         locations->push_back(current);
     209        5510 :         current = allLocations[i];
     210             :       }
     211             :     }
     212         190 :     locations->push_back(current);
     213         410 :     return true;
     214             :   }
     215             : 
     216       52043 :   void resetBlackboxedStateCache() override {
     217       52043 :     v8::HandleScope scope(m_isolate);
     218      104086 :     v8::debug::ResetBlackboxedStateCache(m_isolate, m_script.Get(m_isolate));
     219       52043 :   }
     220             : 
     221         240 :   int offset(int lineNumber, int columnNumber) const override {
     222         240 :     v8::HandleScope scope(m_isolate);
     223         240 :     return m_script.Get(m_isolate)->GetSourceOffset(
     224         480 :         v8::debug::Location(lineNumber, columnNumber));
     225             :   }
     226             : 
     227          50 :   v8::debug::Location location(int offset) const override {
     228          50 :     v8::HandleScope scope(m_isolate);
     229         100 :     return m_script.Get(m_isolate)->GetSourceLocation(offset);
     230             :   }
     231             : 
     232        1899 :   bool setBreakpoint(const String16& condition, v8::debug::Location* location,
     233             :                      int* id) const override {
     234        1899 :     v8::HandleScope scope(m_isolate);
     235        1899 :     return script()->SetBreakpoint(toV8String(m_isolate, condition), location,
     236        5697 :                                    id);
     237             :   }
     238             : 
     239             :  private:
     240       25887 :   String16 GetNameOrSourceUrl(v8::Local<v8::debug::Script> script) {
     241             :     v8::Local<v8::String> name;
     242       65643 :     if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name))
     243       12097 :       return toProtocolString(name);
     244       13790 :     return String16();
     245             :   }
     246             : 
     247        1899 :   v8::Local<v8::debug::Script> script() const override {
     248        3798 :     return m_script.Get(m_isolate);
     249             :   }
     250             : 
     251             :   String16 m_sourceMappingURL;
     252             :   bool m_isLiveEdit = false;
     253             :   bool m_isModule = false;
     254             :   v8::Global<v8::debug::Script> m_script;
     255             : };
     256             : 
     257         249 : class WasmVirtualScript : public V8DebuggerScript {
     258             :   friend class V8DebuggerScript;
     259             : 
     260             :  public:
     261          83 :   WasmVirtualScript(v8::Isolate* isolate, WasmTranslation* wasmTranslation,
     262             :                     v8::Local<v8::debug::WasmScript> script, String16 id,
     263             :                     String16 url, String16 source)
     264             :       : V8DebuggerScript(isolate, std::move(id), std::move(url)),
     265             :         m_script(isolate, script),
     266         332 :         m_wasmTranslation(wasmTranslation) {
     267             :     int num_lines = 0;
     268             :     int last_newline = -1;
     269             :     size_t next_newline = source.find('\n', last_newline + 1);
     270         609 :     while (next_newline != String16::kNotFound) {
     271         443 :       last_newline = static_cast<int>(next_newline);
     272         443 :       next_newline = source.find('\n', last_newline + 1);
     273         443 :       ++num_lines;
     274             :     }
     275          83 :     m_endLine = num_lines;
     276          83 :     m_endColumn = static_cast<int>(source.length()) - last_newline - 1;
     277          83 :     m_source = std::move(source);
     278         166 :     m_executionContextId = script->ContextId().ToChecked();
     279          83 :   }
     280             : 
     281          83 :   const String16& sourceMappingURL() const override { return emptyString(); }
     282          83 :   bool isLiveEdit() const override { return false; }
     283          83 :   bool isModule() const override { return false; }
     284           0 :   void setSourceMappingURL(const String16&) override {}
     285           0 :   void setSource(const String16&, bool, bool*) override { UNREACHABLE(); }
     286             : 
     287          30 :   bool getPossibleBreakpoints(
     288             :       const v8::debug::Location& start, const v8::debug::Location& end,
     289             :       bool restrictToFunction,
     290             :       std::vector<v8::debug::BreakLocation>* locations) override {
     291          30 :     v8::HandleScope scope(m_isolate);
     292          30 :     v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
     293          30 :     String16 v8ScriptId = String16::fromInteger(script->Id());
     294             : 
     295          30 :     v8::debug::Location translatedStart = start;
     296             :     TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedStart,
     297          30 :                                           scriptId(), v8ScriptId);
     298             : 
     299          30 :     v8::debug::Location translatedEnd = end;
     300          30 :     if (translatedEnd.IsEmpty()) {
     301             :       // Stop before the start of the next function.
     302             :       translatedEnd =
     303          10 :           v8::debug::Location(translatedStart.GetLineNumber() + 1, 0);
     304             :     } else {
     305             :       TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedEnd,
     306          20 :                                             scriptId(), v8ScriptId);
     307             :     }
     308             : 
     309             :     bool success = script->GetPossibleBreakpoints(
     310          30 :         translatedStart, translatedEnd, restrictToFunction, locations);
     311         150 :     for (v8::debug::BreakLocation& loc : *locations) {
     312             :       TranslateV8LocationToProtocolLocation(m_wasmTranslation, &loc, v8ScriptId,
     313          90 :                                             scriptId());
     314             :     }
     315          30 :     return success;
     316             :   }
     317             : 
     318         166 :   void resetBlackboxedStateCache() override {}
     319             : 
     320           5 :   int offset(int lineNumber, int columnNumber) const override {
     321           5 :     return kNoOffset;
     322             :   }
     323             : 
     324           0 :   v8::debug::Location location(int offset) const override {
     325           0 :     return v8::debug::Location();
     326             :   }
     327             : 
     328          70 :   bool setBreakpoint(const String16& condition, v8::debug::Location* location,
     329             :                      int* id) const override {
     330          70 :     v8::HandleScope scope(m_isolate);
     331          70 :     v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
     332          70 :     String16 v8ScriptId = String16::fromInteger(script->Id());
     333             : 
     334             :     TranslateProtocolLocationToV8Location(m_wasmTranslation, location,
     335          70 :                                           scriptId(), v8ScriptId);
     336          70 :     if (location->IsEmpty()) return false;
     337          70 :     if (!script->SetBreakpoint(toV8String(m_isolate, condition), location, id))
     338             :       return false;
     339             :     TranslateV8LocationToProtocolLocation(m_wasmTranslation, location,
     340          70 :                                           v8ScriptId, scriptId());
     341         140 :     return true;
     342             :   }
     343             : 
     344             :  private:
     345          83 :   static const String16& emptyString() {
     346          83 :     static const String16 singleEmptyString;
     347          83 :     return singleEmptyString;
     348             :   }
     349             : 
     350           0 :   v8::Local<v8::debug::Script> script() const override {
     351           0 :     return m_script.Get(m_isolate);
     352             :   }
     353             : 
     354             :   v8::Global<v8::debug::WasmScript> m_script;
     355             :   WasmTranslation* m_wasmTranslation;
     356             : };
     357             : 
     358             : }  // namespace
     359             : 
     360       25887 : std::unique_ptr<V8DebuggerScript> V8DebuggerScript::Create(
     361             :     v8::Isolate* isolate, v8::Local<v8::debug::Script> scriptObj,
     362             :     bool isLiveEdit) {
     363             :   return std::unique_ptr<ActualScript>(
     364       51774 :       new ActualScript(isolate, scriptObj, isLiveEdit));
     365             : }
     366             : 
     367          83 : std::unique_ptr<V8DebuggerScript> V8DebuggerScript::CreateWasm(
     368             :     v8::Isolate* isolate, WasmTranslation* wasmTranslation,
     369             :     v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
     370             :     String16 url, String16 source) {
     371             :   return std::unique_ptr<WasmVirtualScript>(
     372             :       new WasmVirtualScript(isolate, wasmTranslation, underlyingScript,
     373         332 :                             std::move(id), std::move(url), std::move(source)));
     374             : }
     375             : 
     376       25970 : V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate, String16 id,
     377             :                                    String16 url)
     378       25970 :     : m_id(std::move(id)), m_url(std::move(url)), m_isolate(isolate) {}
     379             : 
     380       51940 : V8DebuggerScript::~V8DebuggerScript() {}
     381             : 
     382      152359 : const String16& V8DebuggerScript::sourceURL() const {
     383      152359 :   return m_sourceURL.isEmpty() ? m_url : m_sourceURL;
     384             : }
     385             : 
     386       26020 : const String16& V8DebuggerScript::hash() const {
     387       51990 :   if (m_hash.isEmpty()) m_hash = calculateHash(source());
     388             :   DCHECK(!m_hash.isEmpty());
     389       26020 :   return m_hash;
     390             : }
     391             : 
     392         132 : void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
     393         132 :   m_sourceURL = sourceURL;
     394         132 : }
     395             : 
     396           0 : bool V8DebuggerScript::setBreakpoint(const String16& condition,
     397             :                                      v8::debug::Location* loc, int* id) const {
     398           0 :   v8::HandleScope scope(m_isolate);
     399           0 :   return script()->SetBreakpoint(toV8String(m_isolate, condition), loc, id);
     400             : }
     401             : 
     402             : }  // namespace v8_inspector

Generated by: LCOV version 1.10