v8-debugger-script.cc 11.1 KB
Newer Older
1 2 3 4
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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 12

namespace v8_inspector {

13
namespace {
14

15 16 17
const char hexDigits[17] = "0123456789ABCDEF";

void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
18 19 20 21 22 23 24 25 26 27 28
  for (size_t i = 0; i < 8; ++i) {
    UChar c = hexDigits[number & 0xF];
    destination->append(c);
    number >>= 4;
  }
}

// Hash algorithm for substrings is described in "Über die Komplexität der
// Multiplikation in
// eingeschränkten Branchingprogrammmodellen" by Woelfe.
// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
29
String16 calculateHash(const String16& str) {
30 31 32 33 34 35 36 37 38 39
  static uint64_t prime[] = {0x3FB75161, 0xAB1F4E4F, 0x82675BC5, 0xCD924D35,
                             0x81ABE279};
  static uint64_t random[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,
                              0xC3D2E1F0};
  static uint32_t randomOdd[] = {0xB4663807, 0xCC322BF5, 0xD4F91BBD, 0xA7BEA11D,
                                 0x8F462907};

  uint64_t hashes[] = {0, 0, 0, 0, 0};
  uint64_t zi[] = {1, 1, 1, 1, 1};

40
  const size_t hashesSize = arraysize(hashes);
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

  size_t current = 0;
  const uint32_t* data = nullptr;
  size_t sizeInBytes = sizeof(UChar) * str.length();
  data = reinterpret_cast<const uint32_t*>(str.characters16());
  for (size_t i = 0; i < sizeInBytes / 4; i += 4) {
    uint32_t v = data[i];
    uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
    hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
    zi[current] = (zi[current] * random[current]) % prime[current];
    current = current == hashesSize - 1 ? 0 : current + 1;
  }
  if (sizeInBytes % 4) {
    uint32_t v = 0;
    for (size_t i = sizeInBytes - sizeInBytes % 4; i < sizeInBytes; ++i) {
      v <<= 8;
      v |= reinterpret_cast<const uint8_t*>(data)[i];
    }
    uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
    hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
    zi[current] = (zi[current] * random[current]) % prime[current];
    current = current == hashesSize - 1 ? 0 : current + 1;
  }

  for (size_t i = 0; i < hashesSize; ++i)
    hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i];

  String16Builder hash;
  for (size_t i = 0; i < hashesSize; ++i) appendUnsignedAsHex(hashes[i], &hash);
  return hash.toString();
}

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
void TranslateProtocolLocationToV8Location(WasmTranslation* wasmTranslation,
                                           v8::debug::Location* loc,
                                           const String16& scriptId,
                                           const String16& expectedV8ScriptId) {
  if (loc->IsEmpty()) return;
  int lineNumber = loc->GetLineNumber();
  int columnNumber = loc->GetColumnNumber();
  String16 translatedScriptId = scriptId;
  wasmTranslation->TranslateProtocolLocationToWasmScriptLocation(
      &translatedScriptId, &lineNumber, &columnNumber);
  DCHECK_EQ(expectedV8ScriptId.utf8(), translatedScriptId.utf8());
  *loc = v8::debug::Location(lineNumber, columnNumber);
}

void TranslateV8LocationToProtocolLocation(
    WasmTranslation* wasmTranslation, v8::debug::Location* loc,
    const String16& scriptId, const String16& expectedProtocolScriptId) {
  int lineNumber = loc->GetLineNumber();
  int columnNumber = loc->GetColumnNumber();
  String16 translatedScriptId = scriptId;
  wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
      &translatedScriptId, &lineNumber, &columnNumber);
  DCHECK_EQ(expectedProtocolScriptId.utf8(), translatedScriptId.utf8());
  *loc = v8::debug::Location(lineNumber, columnNumber);
}

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
class ActualScript : public V8DebuggerScript {
  friend class V8DebuggerScript;

 public:
  ActualScript(v8::Isolate* isolate, v8::Local<v8::debug::Script> script,
               bool isLiveEdit)
      : V8DebuggerScript(isolate, String16::fromInteger(script->Id()),
                         GetNameOrSourceUrl(script)),
        m_isLiveEdit(isLiveEdit) {
    v8::Local<v8::String> tmp;
    if (script->SourceURL().ToLocal(&tmp)) m_sourceURL = toProtocolString(tmp);
    if (script->SourceMappingURL().ToLocal(&tmp))
      m_sourceMappingURL = toProtocolString(tmp);
    m_startLine = script->LineOffset();
    m_startColumn = script->ColumnOffset();
    std::vector<int> lineEnds = script->LineEnds();
    CHECK(lineEnds.size());
    int source_length = lineEnds[lineEnds.size() - 1];
    if (lineEnds.size()) {
      m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
      if (lineEnds.size() > 1) {
        m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
      } else {
        m_endColumn = source_length + m_startColumn;
      }
124
    } else {
125 126 127 128
      m_endLine = m_startLine;
      m_endColumn = m_startColumn;
    }

129 130 131 132
    v8::Local<v8::Value> contextData;
    if (script->ContextData().ToLocal(&contextData) && contextData->IsInt32()) {
      m_executionContextId =
          static_cast<int>(contextData.As<v8::Int32>()->Value());
133
    }
134

135 136 137 138 139 140 141 142
    if (script->Source().ToLocal(&tmp)) {
      m_sourceObj.Reset(m_isolate, tmp);
      String16 source = toProtocolString(tmp);
      // V8 will not count last line if script source ends with \n.
      if (source.length() > 1 && source[source.length() - 1] == '\n') {
        m_endLine++;
        m_endColumn = 0;
      }
143
    }
144

145 146
    m_isModule = script->IsModule();

147
    m_script.Reset(m_isolate, script);
148
  }
149

150
  bool isLiveEdit() const override { return m_isLiveEdit; }
151
  bool isModule() const override { return m_isModule; }
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

  const String16& sourceMappingURL() const override {
    return m_sourceMappingURL;
  }

  String16 source(v8::Isolate* isolate) const override {
    if (!m_sourceObj.IsEmpty())
      return toProtocolString(m_sourceObj.Get(isolate));
    return V8DebuggerScript::source(isolate);
  }

  void setSourceMappingURL(const String16& sourceMappingURL) override {
    m_sourceMappingURL = sourceMappingURL;
  }

  void setSource(v8::Local<v8::String> source) override {
    m_source = String16();
    m_sourceObj.Reset(m_isolate, source);
    m_hash = String16();
  }

  bool getPossibleBreakpoints(
      const v8::debug::Location& start, const v8::debug::Location& end,
      std::vector<v8::debug::Location>* locations) override {
    v8::HandleScope scope(m_isolate);
    v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
    return script->GetPossibleBreakpoints(start, end, locations);
  }

181 182 183 184 185
  void resetBlackboxedStateCache() override {
    v8::HandleScope scope(m_isolate);
    v8::debug::ResetBlackboxedStateCache(m_isolate, m_script.Get(m_isolate));
  }

186 187 188 189 190 191 192
 private:
  String16 GetNameOrSourceUrl(v8::Local<v8::debug::Script> script) {
    v8::Local<v8::String> name;
    if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name))
      return toProtocolString(name);
    return String16();
  }
193

194 195 196
  String16 m_sourceMappingURL;
  v8::Global<v8::String> m_sourceObj;
  bool m_isLiveEdit = false;
197
  bool m_isModule = false;
198 199 200 201 202 203 204
  v8::Global<v8::debug::Script> m_script;
};

class WasmVirtualScript : public V8DebuggerScript {
  friend class V8DebuggerScript;

 public:
205
  WasmVirtualScript(v8::Isolate* isolate, WasmTranslation* wasmTranslation,
206 207 208
                    v8::Local<v8::debug::WasmScript> script, String16 id,
                    String16 url, String16 source)
      : V8DebuggerScript(isolate, std::move(id), std::move(url)),
209 210
        m_script(isolate, script),
        m_wasmTranslation(wasmTranslation) {
211 212 213 214 215 216 217
    int num_lines = 0;
    int last_newline = -1;
    size_t next_newline = source.find('\n', last_newline + 1);
    while (next_newline != String16::kNotFound) {
      last_newline = static_cast<int>(next_newline);
      next_newline = source.find('\n', last_newline + 1);
      ++num_lines;
218
    }
219 220 221
    m_endLine = num_lines;
    m_endColumn = static_cast<int>(source.length()) - last_newline - 1;
    m_source = std::move(source);
222
  }
223

224 225
  const String16& sourceMappingURL() const override { return emptyString(); }
  bool isLiveEdit() const override { return false; }
226
  bool isModule() const override { return false; }
227 228 229 230 231
  void setSourceMappingURL(const String16&) override {}

  bool getPossibleBreakpoints(
      const v8::debug::Location& start, const v8::debug::Location& end,
      std::vector<v8::debug::Location>* locations) override {
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
    v8::HandleScope scope(m_isolate);
    v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
    String16 v8ScriptId = String16::fromInteger(script->Id());

    v8::debug::Location translatedStart = start;
    TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedStart,
                                          scriptId(), v8ScriptId);

    v8::debug::Location translatedEnd = end;
    if (translatedEnd.IsEmpty()) {
      // Stop before the start of the next function.
      translatedEnd =
          v8::debug::Location(translatedStart.GetLineNumber() + 1, 0);
    } else {
      TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedEnd,
                                            scriptId(), v8ScriptId);
    }

    bool success = script->GetPossibleBreakpoints(translatedStart,
                                                  translatedEnd, locations);
    for (v8::debug::Location& loc : *locations) {
      TranslateV8LocationToProtocolLocation(m_wasmTranslation, &loc, v8ScriptId,
                                            scriptId());
    }
    return success;
257
  }
258

259 260
  void resetBlackboxedStateCache() override {}

261 262 263 264
 private:
  static const String16& emptyString() {
    static const String16 singleEmptyString;
    return singleEmptyString;
265
  }
266 267

  v8::Global<v8::debug::WasmScript> m_script;
268
  WasmTranslation* m_wasmTranslation;
269 270 271 272 273 274 275 276 277 278 279 280
};

}  // namespace

std::unique_ptr<V8DebuggerScript> V8DebuggerScript::Create(
    v8::Isolate* isolate, v8::Local<v8::debug::Script> scriptObj,
    bool isLiveEdit) {
  return std::unique_ptr<ActualScript>(
      new ActualScript(isolate, scriptObj, isLiveEdit));
}

std::unique_ptr<V8DebuggerScript> V8DebuggerScript::CreateWasm(
281 282 283
    v8::Isolate* isolate, WasmTranslation* wasmTranslation,
    v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
    String16 url, String16 source) {
284
  return std::unique_ptr<WasmVirtualScript>(
285 286
      new WasmVirtualScript(isolate, wasmTranslation, underlyingScript,
                            std::move(id), std::move(url), std::move(source)));
287 288
}

289 290 291 292
V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate, String16 id,
                                   String16 url)
    : m_id(std::move(id)), m_url(std::move(url)), m_isolate(isolate) {}

293 294 295 296 297 298
V8DebuggerScript::~V8DebuggerScript() {}

const String16& V8DebuggerScript::sourceURL() const {
  return m_sourceURL.isEmpty() ? m_url : m_sourceURL;
}

299 300 301 302
const String16& V8DebuggerScript::hash(v8::Isolate* isolate) const {
  if (m_hash.isEmpty()) m_hash = calculateHash(source(isolate));
  DCHECK(!m_hash.isEmpty());
  return m_hash;
303 304 305 306 307 308 309
}

void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
  m_sourceURL = sourceURL;
}

}  // namespace v8_inspector