[inspector] Split V8DebuggerScript implementation for wasm

Make some methods on V8DebuggerScript virtual and provide the
implementations ActualScript for scripts which are backed by scripts on
V8's side, and WasmVirtualScript for wasm scripts.

The added test case ensures that we at least don't crash on the attempt
to get breakable locations for wasm "scripts", which we did previously.
Returning a reasonable result for wasm will be implemented in a
follow-up commit.

R=yangguo@chromium.org, jgruber@chromium.org
BUG=chromium:667767,chromium:613110

Review-Url: https://codereview.chromium.org/2532433003
Cr-Commit-Position: refs/heads/master@{#41527}
This commit is contained in:
clemensh 2016-12-06 06:26:15 -08:00 committed by Commit bot
parent 9c9c8d7bb5
commit a9017cb018
4 changed files with 200 additions and 118 deletions

View File

@ -8,9 +8,11 @@
namespace v8_inspector {
static const char hexDigits[17] = "0123456789ABCDEF";
namespace {
static void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
const char hexDigits[17] = "0123456789ABCDEF";
void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
for (size_t i = 0; i < 8; ++i) {
UChar c = hexDigits[number & 0xF];
destination->append(c);
@ -22,7 +24,7 @@ static void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
// Multiplikation in
// eingeschränkten Branchingprogrammmodellen" by Woelfe.
// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
static String16 calculateHash(const String16& str) {
String16 calculateHash(const String16& str) {
static uint64_t prime[] = {0x3FB75161, 0xAB1F4E4F, 0x82675BC5, 0xCD924D35,
0x81ABE279};
static uint64_t random[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,
@ -66,92 +68,188 @@ static String16 calculateHash(const String16& str) {
return hash.toString();
}
V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate,
v8::Local<v8::debug::Script> script,
bool isLiveEdit) {
m_isolate = script->GetIsolate();
m_id = String16::fromInteger(script->Id());
v8::Local<v8::String> tmp;
if (script->Name().ToLocal(&tmp)) m_url = toProtocolString(tmp);
if (script->SourceURL().ToLocal(&tmp)) {
m_sourceURL = toProtocolString(tmp);
if (m_url.isEmpty()) m_url = 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;
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;
}
} else {
m_endColumn = source_length + m_startColumn;
m_endLine = m_startLine;
m_endColumn = m_startColumn;
}
} else {
m_endLine = m_startLine;
m_endColumn = m_startColumn;
if (script->ContextData().ToLocal(&tmp)) {
String16 contextData = toProtocolString(tmp);
size_t firstComma = contextData.find(",", 0);
size_t secondComma = firstComma != String16::kNotFound
? contextData.find(",", firstComma + 1)
: String16::kNotFound;
if (secondComma != String16::kNotFound) {
String16 executionContextId =
contextData.substring(firstComma + 1, secondComma - firstComma - 1);
bool isOk = false;
m_executionContextId = executionContextId.toInteger(&isOk);
if (!isOk) m_executionContextId = 0;
m_executionContextAuxData = contextData.substring(secondComma + 1);
}
}
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;
}
}
m_script.Reset(m_isolate, script);
}
if (script->ContextData().ToLocal(&tmp)) {
String16 contextData = toProtocolString(tmp);
size_t firstComma = contextData.find(",", 0);
size_t secondComma = firstComma != String16::kNotFound
? contextData.find(",", firstComma + 1)
: String16::kNotFound;
if (secondComma != String16::kNotFound) {
String16 executionContextId =
contextData.substring(firstComma + 1, secondComma - firstComma - 1);
bool isOk = false;
m_executionContextId = executionContextId.toInteger(&isOk);
if (!isOk) m_executionContextId = 0;
m_executionContextAuxData = contextData.substring(secondComma + 1);
}
bool isLiveEdit() const override { return m_isLiveEdit; }
const String16& executionContextAuxData() const override {
return m_executionContextAuxData;
}
m_isLiveEdit = isLiveEdit;
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;
}
const String16& sourceMappingURL() const override {
return m_sourceMappingURL;
}
m_script.Reset(m_isolate, script);
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);
}
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();
}
String16 m_sourceMappingURL;
v8::Global<v8::String> m_sourceObj;
String16 m_executionContextAuxData;
bool m_isLiveEdit = false;
v8::Global<v8::debug::Script> m_script;
};
class WasmVirtualScript : public V8DebuggerScript {
friend class V8DebuggerScript;
public:
WasmVirtualScript(v8::Isolate* isolate,
v8::Local<v8::debug::WasmScript> script, String16 id,
String16 url, String16 source)
: V8DebuggerScript(isolate, std::move(id), std::move(url)),
m_script(isolate, script) {
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;
}
m_endLine = num_lines;
m_endColumn = static_cast<int>(source.length()) - last_newline - 1;
m_source = std::move(source);
}
const String16& sourceMappingURL() const override { return emptyString(); }
const String16& executionContextAuxData() const override {
return emptyString();
}
bool isLiveEdit() const override { return false; }
void setSourceMappingURL(const String16&) override {}
bool getPossibleBreakpoints(
const v8::debug::Location& start, const v8::debug::Location& end,
std::vector<v8::debug::Location>* locations) override {
// TODO(clemensh): Returning false produces the protocol error "Internal
// error". Implement and fix expected output of
// wasm-get-breakable-locations.js.
return false;
}
private:
static const String16& emptyString() {
static const String16 singleEmptyString;
return singleEmptyString;
}
v8::Global<v8::debug::WasmScript> m_script;
};
} // 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));
}
V8DebuggerScript::V8DebuggerScript(String16 id, String16 url, String16 source)
: m_id(std::move(id)), m_url(std::move(url)), m_source(std::move(source)) {
int num_lines = 0;
int last_newline = -1;
size_t next_newline = m_source.find('\n', last_newline + 1);
while (next_newline != String16::kNotFound) {
last_newline = static_cast<int>(next_newline);
next_newline = m_source.find('\n', last_newline + 1);
++num_lines;
}
m_endLine = num_lines;
m_endColumn = static_cast<int>(m_source.length()) - last_newline - 1;
std::unique_ptr<V8DebuggerScript> V8DebuggerScript::CreateWasm(
v8::Isolate* isolate, v8::Local<v8::debug::WasmScript> underlyingScript,
String16 id, String16 url, String16 source) {
return std::unique_ptr<WasmVirtualScript>(
new WasmVirtualScript(isolate, underlyingScript, std::move(id),
std::move(url), std::move(source)));
}
V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate, String16 id,
String16 url)
: m_id(std::move(id)), m_url(std::move(url)), m_isolate(isolate) {}
V8DebuggerScript::~V8DebuggerScript() {}
const String16& V8DebuggerScript::sourceURL() const {
return m_sourceURL.isEmpty() ? m_url : m_sourceURL;
}
String16 V8DebuggerScript::source(v8::Isolate* isolate) const {
if (m_sourceObj.IsEmpty()) return m_source;
return toProtocolString(m_sourceObj.Get(isolate));
}
const String16& V8DebuggerScript::hash(v8::Isolate* isolate) const {
if (m_hash.isEmpty()) m_hash = calculateHash(source(isolate));
DCHECK(!m_hash.isEmpty());
@ -162,22 +260,4 @@ void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
m_sourceURL = sourceURL;
}
void V8DebuggerScript::setSourceMappingURL(const String16& sourceMappingURL) {
m_sourceMappingURL = sourceMappingURL;
}
void V8DebuggerScript::setSource(v8::Local<v8::String> source) {
m_source = String16();
m_sourceObj.Reset(m_isolate, source);
m_hash = String16();
}
bool V8DebuggerScript::getPossibleBreakpoints(
const v8::debug::Location& start, const v8::debug::Location& end,
std::vector<v8::debug::Location>* locations) {
v8::HandleScope scope(m_isolate);
v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
return script->GetPossibleBreakpoints(start, end, locations);
}
} // namespace v8_inspector

View File

@ -32,6 +32,7 @@
#include "src/base/macros.h"
#include "src/inspector/string-16.h"
#include "src/inspector/string-util.h"
#include "include/v8.h"
#include "src/debug/debug-interface.h"
@ -40,42 +41,46 @@ namespace v8_inspector {
class V8DebuggerScript {
public:
V8DebuggerScript(v8::Isolate* isolate, v8::Local<v8::debug::Script> script,
bool isLiveEdit);
V8DebuggerScript(String16 id, String16 url, String16 source);
~V8DebuggerScript();
static std::unique_ptr<V8DebuggerScript> Create(
v8::Isolate* isolate, v8::Local<v8::debug::Script> script,
bool isLiveEdit);
static std::unique_ptr<V8DebuggerScript> CreateWasm(
v8::Isolate* isolate, v8::Local<v8::debug::WasmScript> underlyingScript,
String16 id, String16 url, String16 source);
virtual ~V8DebuggerScript();
const String16& scriptId() const { return m_id; }
const String16& url() const { return m_url; }
bool hasSourceURL() const { return !m_sourceURL.isEmpty(); }
const String16& sourceURL() const;
const String16& sourceMappingURL() const { return m_sourceMappingURL; }
String16 source(v8::Isolate*) const;
virtual const String16& sourceMappingURL() const = 0;
virtual String16 source(v8::Isolate*) const { return m_source; }
const String16& hash(v8::Isolate*) const;
int startLine() const { return m_startLine; }
int startColumn() const { return m_startColumn; }
int endLine() const { return m_endLine; }
int endColumn() const { return m_endColumn; }
int executionContextId() const { return m_executionContextId; }
const String16& executionContextAuxData() const {
return m_executionContextAuxData;
}
bool isLiveEdit() const { return m_isLiveEdit; }
virtual const String16& executionContextAuxData() const = 0;
virtual bool isLiveEdit() const = 0;
void setSourceURL(const String16&);
void setSourceMappingURL(const String16&);
void setSource(v8::Local<v8::String>);
virtual void setSourceMappingURL(const String16&) = 0;
virtual void setSource(v8::Local<v8::String> source) {
m_source = toProtocolString(source);
}
bool getPossibleBreakpoints(const v8::debug::Location& start,
const v8::debug::Location& end,
std::vector<v8::debug::Location>* locations);
virtual bool getPossibleBreakpoints(
const v8::debug::Location& start, const v8::debug::Location& end,
std::vector<v8::debug::Location>* locations) = 0;
protected:
V8DebuggerScript(v8::Isolate*, String16 id, String16 url);
private:
String16 m_id;
String16 m_url;
String16 m_sourceURL;
String16 m_sourceMappingURL;
v8::Global<v8::String> m_sourceObj;
String16 m_source;
mutable String16 m_hash;
int m_startLine = 0;
@ -83,12 +88,10 @@ class V8DebuggerScript {
int m_endLine = 0;
int m_endColumn = 0;
int m_executionContextId = 0;
String16 m_executionContextAuxData;
bool m_isLiveEdit = false;
v8::Isolate* m_isolate;
v8::Global<v8::debug::Script> m_script;
private:
DISALLOW_COPY_AND_ASSIGN(V8DebuggerScript);
};

View File

@ -127,8 +127,7 @@ void V8Debugger::getCompiledScripts(
if (!script->ContextData().ToLocal(&v8ContextData)) continue;
String16 contextData = toProtocolString(v8ContextData);
if (contextData.find(contextPrefix) != 0) continue;
result.push_back(std::unique_ptr<V8DebuggerScript>(
new V8DebuggerScript(m_isolate, script, false)));
result.push_back(V8DebuggerScript::Create(m_isolate, script, false));
}
}
@ -590,8 +589,7 @@ void V8Debugger::handleV8DebugEvent(
m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent);
} else if (m_ignoreScriptParsedEventsCounter == 0) {
agent->didParseSource(
std::unique_ptr<V8DebuggerScript>(
new V8DebuggerScript(m_isolate, script, inLiveEditScope)),
V8DebuggerScript::Create(m_isolate, script, inLiveEditScope),
event == v8::AfterCompile);
}
} else if (event == v8::Exception) {

View File

@ -167,17 +167,18 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
String16 fake_script_id = GetFakeScriptId(underlyingScriptId, func_idx);
String16 fake_script_url = GetFakeScriptUrl(isolate, func_idx);
v8::Local<debug::WasmScript> script = script_.Get(isolate);
// TODO(clemensh): Generate disassembly lazily when queried by the frontend.
debug::WasmDisassembly disassembly =
script_.Get(isolate)->DisassembleFunction(func_idx);
debug::WasmDisassembly disassembly = script->DisassembleFunction(func_idx);
DCHECK_EQ(0, offset_tables_.count(func_idx));
offset_tables_.insert(
std::make_pair(func_idx, std::move(disassembly.offset_table)));
String16 source(disassembly.disassembly.data(),
disassembly.disassembly.length());
std::unique_ptr<V8DebuggerScript> fake_script(new V8DebuggerScript(
fake_script_id, std::move(fake_script_url), source));
std::unique_ptr<V8DebuggerScript> fake_script =
V8DebuggerScript::CreateWasm(isolate, script, fake_script_id,
std::move(fake_script_url), source);
translation->AddFakeScript(fake_script->scriptId(), this);
agent->didParseSource(std::move(fake_script), true);