Commit c6663ef1 authored by clemensh's avatar clemensh Committed by Commit bot

[inspector] Expose GetPossibleBreakpoints for wasm

and add an inspector test for it. Also test that a breakpoint can be
set on each reported position, and that it is also hit during
execution.
This CL also fixes two little things which were uncovered by the new
test.

R=yangguo@chromium.org, titzer@chromium.org, kozyatinskiy@chromium.org
BUG=v8:5822

Review-Url: https://codereview.chromium.org/2655653003
Cr-Commit-Position: refs/heads/master@{#42722}
parent fec83a2a
......@@ -9151,8 +9151,9 @@ bool debug::Script::GetPossibleBreakpoints(
CHECK(!start.IsEmpty());
i::Handle<i::Script> script = Utils::OpenHandle(this);
if (script->type() == i::Script::TYPE_WASM) {
// TODO(clemensh): Return the proper thing once we support wasm breakpoints.
return false;
i::Handle<i::WasmCompiledModule> compiled_module(
i::WasmCompiledModule::cast(script->wasm_compiled_module()));
return compiled_module->GetPossibleBreakpoints(start, end, locations);
}
i::Script::InitLineEnds(script);
......@@ -9248,8 +9249,26 @@ int debug::WasmScript::NumImportedFunctions() const {
return static_cast<int>(compiled_module->module()->num_imported_functions);
}
std::pair<int, int> debug::WasmScript::GetFunctionRange(
int function_index) const {
i::DisallowHeapAllocation no_gc;
i::Handle<i::Script> script = Utils::OpenHandle(this);
DCHECK_EQ(i::Script::TYPE_WASM, script->type());
i::WasmCompiledModule* compiled_module =
i::WasmCompiledModule::cast(script->wasm_compiled_module());
DCHECK_LE(0, function_index);
DCHECK_GT(compiled_module->module()->functions.size(), function_index);
i::wasm::WasmFunction& func =
compiled_module->module()->functions[function_index];
DCHECK_GE(i::kMaxInt, func.code_start_offset);
DCHECK_GE(i::kMaxInt, func.code_end_offset);
return std::make_pair(static_cast<int>(func.code_start_offset),
static_cast<int>(func.code_end_offset));
}
debug::WasmDisassembly debug::WasmScript::DisassembleFunction(
int function_index) const {
i::DisallowHeapAllocation no_gc;
i::Handle<i::Script> script = Utils::OpenHandle(this);
DCHECK_EQ(i::Script::TYPE_WASM, script->type());
i::WasmCompiledModule* compiled_module =
......
......@@ -139,6 +139,8 @@ class WasmScript : public Script {
int NumFunctions() const;
int NumImportedFunctions() const;
std::pair<int, int> GetFunctionRange(int function_index) const;
debug::WasmDisassembly DisassembleFunction(int function_index) const;
};
......
......@@ -473,6 +473,7 @@ V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId,
scriptIterator->second->endLine() < breakpoint.line_number)
return nullptr;
// Translate from protocol location to v8 location for the debugger.
ScriptBreakpoint translatedBreakpoint = breakpoint;
m_debugger->wasmTranslation()->TranslateProtocolLocationToWasmScriptLocation(
&translatedBreakpoint.script_id, &translatedBreakpoint.line_number,
......@@ -484,6 +485,10 @@ V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId,
translatedBreakpoint, &actualLineNumber, &actualColumnNumber);
if (debuggerBreakpointId.isEmpty()) return nullptr;
// Translate back from v8 location to protocol location for the return value.
m_debugger->wasmTranslation()->TranslateWasmScriptLocationToProtocolLocation(
&translatedBreakpoint.script_id, &actualLineNumber, &actualColumnNumber);
m_serverBreakpoints[debuggerBreakpointId] =
std::make_pair(breakpointId, source);
CHECK(!breakpointId.isEmpty());
......
......@@ -6,6 +6,7 @@
#include "src/inspector/inspected-context.h"
#include "src/inspector/string-util.h"
#include "src/inspector/wasm-translation.h"
namespace v8_inspector {
......@@ -69,6 +70,32 @@ String16 calculateHash(const String16& str) {
return hash.toString();
}
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);
}
class ActualScript : public V8DebuggerScript {
friend class V8DebuggerScript;
......@@ -171,11 +198,12 @@ class WasmVirtualScript : public V8DebuggerScript {
friend class V8DebuggerScript;
public:
WasmVirtualScript(v8::Isolate* isolate,
WasmVirtualScript(v8::Isolate* isolate, WasmTranslation* wasmTranslation,
v8::Local<v8::debug::WasmScript> script, String16 id,
String16 url, String16 source)
: V8DebuggerScript(isolate, std::move(id), std::move(url)),
m_script(isolate, script) {
m_script(isolate, script),
m_wasmTranslation(wasmTranslation) {
int num_lines = 0;
int last_newline = -1;
size_t next_newline = source.find('\n', last_newline + 1);
......@@ -196,10 +224,31 @@ class WasmVirtualScript : public V8DebuggerScript {
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;
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;
}
void resetBlackboxedStateCache() override {}
......@@ -211,6 +260,7 @@ class WasmVirtualScript : public V8DebuggerScript {
}
v8::Global<v8::debug::WasmScript> m_script;
WasmTranslation* m_wasmTranslation;
};
} // namespace
......@@ -223,11 +273,12 @@ std::unique_ptr<V8DebuggerScript> V8DebuggerScript::Create(
}
std::unique_ptr<V8DebuggerScript> V8DebuggerScript::CreateWasm(
v8::Isolate* isolate, v8::Local<v8::debug::WasmScript> underlyingScript,
String16 id, String16 url, String16 source) {
v8::Isolate* isolate, WasmTranslation* wasmTranslation,
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)));
new WasmVirtualScript(isolate, wasmTranslation, underlyingScript,
std::move(id), std::move(url), std::move(source)));
}
V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate, String16 id,
......
......@@ -39,14 +39,18 @@
namespace v8_inspector {
// Forward declaration.
class WasmTranslation;
class V8DebuggerScript {
public:
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);
v8::Isolate* isolate, WasmTranslation* wasmTranslation,
v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
String16 url, String16 source);
virtual ~V8DebuggerScript();
......
......@@ -21,11 +21,11 @@ using namespace v8;
class WasmTranslation::TranslatorImpl {
public:
struct TransLocation {
WasmTranslation *translation;
WasmTranslation* translation;
String16 script_id;
int line;
int column;
TransLocation(WasmTranslation *translation, String16 script_id, int line,
TransLocation(WasmTranslation* translation, String16 script_id, int line,
int column)
: translation(translation),
script_id(script_id),
......@@ -33,8 +33,9 @@ class WasmTranslation::TranslatorImpl {
column(column) {}
};
virtual void Translate(TransLocation *loc) = 0;
virtual void TranslateBack(TransLocation *loc) = 0;
virtual void Init(Isolate*, WasmTranslation*, V8DebuggerAgentImpl*) = 0;
virtual void Translate(TransLocation*) = 0;
virtual void TranslateBack(TransLocation*) = 0;
class RawTranslator;
class DisassemblingTranslator;
......@@ -43,8 +44,9 @@ class WasmTranslation::TranslatorImpl {
class WasmTranslation::TranslatorImpl::RawTranslator
: public WasmTranslation::TranslatorImpl {
public:
void Translate(TransLocation *loc) {}
void TranslateBack(TransLocation *loc) {}
void Init(Isolate*, WasmTranslation*, V8DebuggerAgentImpl*) {}
void Translate(TransLocation*) {}
void TranslateBack(TransLocation*) {}
};
class WasmTranslation::TranslatorImpl::DisassemblingTranslator
......@@ -52,11 +54,13 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
using OffsetTable = debug::WasmDisassembly::OffsetTable;
public:
DisassemblingTranslator(Isolate *isolate, Local<debug::WasmScript> script,
WasmTranslation *translation,
V8DebuggerAgentImpl *agent)
: script_(isolate, script) {
DisassemblingTranslator(Isolate* isolate, Local<debug::WasmScript> script)
: script_(isolate, script) {}
void Init(Isolate* isolate, WasmTranslation* translation,
V8DebuggerAgentImpl* agent) override {
// Register fake scripts for each function in this wasm module/script.
Handle<debug::WasmScript> script = script_.Get(isolate);
int num_functions = script->NumFunctions();
int num_imported_functions = script->NumImportedFunctions();
DCHECK_LE(0, num_imported_functions);
......@@ -69,8 +73,8 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
}
}
void Translate(TransLocation *loc) {
const OffsetTable &offset_table = GetOffsetTable(loc);
void Translate(TransLocation* loc) override {
const OffsetTable& offset_table = GetOffsetTable(loc);
DCHECK(!offset_table.empty());
uint32_t byte_offset = static_cast<uint32_t>(loc->column);
......@@ -96,18 +100,19 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
}
}
void TranslateBack(TransLocation *loc) {
void TranslateBack(TransLocation* loc) override {
int func_index = GetFunctionIndexFromFakeScriptId(loc->script_id);
const OffsetTable *reverse_table = GetReverseTable(func_index);
const OffsetTable* reverse_table = GetReverseTable(func_index);
if (!reverse_table) return;
DCHECK(!reverse_table->empty());
v8::Isolate* isolate = loc->translation->isolate_;
// Binary search for the given line and column.
unsigned left = 0; // inclusive
unsigned right = static_cast<unsigned>(reverse_table->size()); // exclusive
while (right - left > 1) {
unsigned mid = (left + right) / 2;
auto &entry = (*reverse_table)[mid];
auto& entry = (*reverse_table)[mid];
if (entry.line < loc->line ||
(entry.line == loc->line && entry.column <= loc->column)) {
left = mid;
......@@ -119,22 +124,31 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
int found_byte_offset = 0;
// If we found an exact match, use it. Otherwise check whether the next
// bigger entry is still in the same line. Report that one then.
// Otherwise we might have hit the special case of pointing after the last
// line, which is translated to the end of the function (one byte after the
// last function byte).
if ((*reverse_table)[left].line == loc->line &&
(*reverse_table)[left].column == loc->column) {
found_byte_offset = (*reverse_table)[left].byte_offset;
} else if (left + 1 < reverse_table->size() &&
(*reverse_table)[left + 1].line == loc->line) {
found_byte_offset = (*reverse_table)[left + 1].byte_offset;
} else if (left == reverse_table->size() - 1 &&
(*reverse_table)[left].line == loc->line - 1 &&
loc->column == 0) {
std::pair<int, int> func_range =
script_.Get(isolate)->GetFunctionRange(func_index);
DCHECK_LE(func_range.first, func_range.second);
found_byte_offset = func_range.second - func_range.first;
}
v8::Isolate *isolate = loc->translation->isolate_;
loc->script_id = String16::fromInteger(script_.Get(isolate)->Id());
loc->line = func_index;
loc->column = found_byte_offset;
}
private:
String16 GetFakeScriptUrl(v8::Isolate *isolate, int func_index) {
String16 GetFakeScriptUrl(v8::Isolate* isolate, int func_index) {
Local<debug::WasmScript> script = script_.Get(isolate);
String16 script_name = toProtocolString(script->Name().ToLocalChecked());
int numFunctions = script->NumFunctions();
......@@ -157,13 +171,13 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
String16 GetFakeScriptId(const String16 script_id, int func_index) {
return String16::concat(script_id, '-', String16::fromInteger(func_index));
}
String16 GetFakeScriptId(const TransLocation *loc) {
String16 GetFakeScriptId(const TransLocation* loc) {
return GetFakeScriptId(loc->script_id, loc->line);
}
void AddFakeScript(v8::Isolate *isolate, const String16 &underlyingScriptId,
int func_idx, WasmTranslation *translation,
V8DebuggerAgentImpl *agent) {
void AddFakeScript(v8::Isolate* isolate, const String16& underlyingScriptId,
int func_idx, WasmTranslation* translation,
V8DebuggerAgentImpl* agent) {
String16 fake_script_id = GetFakeScriptId(underlyingScriptId, func_idx);
String16 fake_script_url = GetFakeScriptUrl(isolate, func_idx);
......@@ -177,14 +191,15 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
String16 source(disassembly.disassembly.data(),
disassembly.disassembly.length());
std::unique_ptr<V8DebuggerScript> fake_script =
V8DebuggerScript::CreateWasm(isolate, script, fake_script_id,
std::move(fake_script_url), source);
V8DebuggerScript::CreateWasm(isolate, translation, script,
fake_script_id, std::move(fake_script_url),
source);
translation->AddFakeScript(fake_script->scriptId(), this);
agent->didParseSource(std::move(fake_script), true);
}
int GetFunctionIndexFromFakeScriptId(const String16 &fake_script_id) {
int GetFunctionIndexFromFakeScriptId(const String16& fake_script_id) {
size_t last_dash_pos = fake_script_id.reverseFind('-');
DCHECK_GT(fake_script_id.length(), last_dash_pos);
bool ok = true;
......@@ -193,7 +208,7 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
return func_index;
}
const OffsetTable &GetOffsetTable(const TransLocation *loc) {
const OffsetTable& GetOffsetTable(const TransLocation* loc) {
int func_index = loc->line;
auto it = offset_tables_.find(func_index);
// TODO(clemensh): Once we load disassembly lazily, the offset table
......@@ -202,7 +217,7 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
return it->second;
}
const OffsetTable *GetReverseTable(int func_index) {
const OffsetTable* GetReverseTable(int func_index) {
auto it = reverse_tables_.find(func_index);
if (it != reverse_tables_.end()) return &it->second;
......@@ -233,27 +248,29 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
std::unordered_map<int, const OffsetTable> reverse_tables_;
};
WasmTranslation::WasmTranslation(v8::Isolate *isolate)
WasmTranslation::WasmTranslation(v8::Isolate* isolate)
: isolate_(isolate), mode_(Disassemble) {}
WasmTranslation::~WasmTranslation() { Clear(); }
void WasmTranslation::AddScript(Local<debug::WasmScript> script,
V8DebuggerAgentImpl *agent) {
int script_id = script->Id();
DCHECK_EQ(0, wasm_translators_.count(script_id));
V8DebuggerAgentImpl* agent) {
std::unique_ptr<TranslatorImpl> impl;
switch (mode_) {
case Raw:
impl.reset(new TranslatorImpl::RawTranslator());
break;
case Disassemble:
impl.reset(new TranslatorImpl::DisassemblingTranslator(isolate_, script,
this, agent));
impl.reset(new TranslatorImpl::DisassemblingTranslator(isolate_, script));
break;
}
DCHECK(impl);
wasm_translators_.insert(std::make_pair(script_id, std::move(impl)));
auto inserted =
wasm_translators_.insert(std::make_pair(script->Id(), std::move(impl)));
// Check that no mapping for this script id existed before.
DCHECK(inserted.second);
// impl has been moved, use the returned iterator to call Init.
inserted.first->second->Init(isolate_, this, agent);
}
void WasmTranslation::Clear() {
......@@ -263,7 +280,7 @@ void WasmTranslation::Clear() {
// Translation "forward" (to artificial scripts).
bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation(
String16 *script_id, int *line_number, int *column_number) {
String16* script_id, int* line_number, int* column_number) {
DCHECK(script_id && line_number && column_number);
bool ok = true;
int script_id_int = script_id->toInteger(&ok);
......@@ -271,7 +288,7 @@ bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation(
auto it = wasm_translators_.find(script_id_int);
if (it == wasm_translators_.end()) return false;
TranslatorImpl *translator = it->second.get();
TranslatorImpl* translator = it->second.get();
TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
*line_number, *column_number);
......@@ -286,10 +303,10 @@ bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation(
// Translation "backward" (from artificial to real scripts).
bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation(
String16 *script_id, int *line_number, int *column_number) {
String16* script_id, int* line_number, int* column_number) {
auto it = fake_scripts_.find(*script_id);
if (it == fake_scripts_.end()) return false;
TranslatorImpl *translator = it->second;
TranslatorImpl* translator = it->second;
TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
*line_number, *column_number);
......@@ -302,8 +319,8 @@ bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation(
return true;
}
void WasmTranslation::AddFakeScript(const String16 &scriptId,
TranslatorImpl *translator) {
void WasmTranslation::AddFakeScript(const String16& scriptId,
TranslatorImpl* translator) {
DCHECK_EQ(0, fake_scripts_.count(scriptId));
fake_scripts_.insert(std::make_pair(scriptId, translator));
}
......@@ -1653,7 +1653,8 @@ namespace {
int ScriptLinePositionWithOffset(Handle<Script> script, int line, int offset) {
if (line < 0 || offset < 0) return -1;
if (line == 0) return ScriptLinePosition(script, line) + offset;
if (line == 0 || offset == 0)
return ScriptLinePosition(script, line) + offset;
Script::PositionInfo info;
if (!Script::GetPositionInfo(script, offset, &info, Script::NO_OFFSET)) {
......
......@@ -169,6 +169,10 @@ RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
CHECK(arg_buffer_obj->IsSmi());
uint8_t* arg_buffer = reinterpret_cast<uint8_t*>(*arg_buffer_obj);
// Set the current isolate's context, saving the previous one.
SaveContext save(isolate);
isolate->set_context(*instance->compiled_module()->native_context());
instance->debug_info()->RunInterpreter(func_index, arg_buffer);
return isolate->heap()->undefined_value();
}
......
Running testFunction...
Script nr 0 parsed. URL: v8://test/setup
Script nr 1 parsed. URL: v8://test/runTestFunction
Script nr 2 parsed. URL: wasm://wasm/wasm-824d794e/wasm-824d794e-0
This is a wasm script (nr 0).
Script nr 3 parsed. URL: wasm://wasm/wasm-824d794e/wasm-824d794e-1
This is a wasm script (nr 1).
Querying breakable locations for all wasm scripts now...
Requesting all breakable locations in wasm script 0
4 breakable location(s):
[0] 2:2 || >nop
[1] 3:2 || >i32.const 12
[2] 4:2 || >set_local 0
[3] 5:0 || >end
Requesting breakable locations in lines [0,3)
1 breakable location(s):
[0] 2:2 || >nop
Requesting breakable locations in lines [4,6)
2 breakable location(s):
[0] 4:2 || >set_local 0
[1] 5:0 || >end
Requesting all breakable locations in wasm script 1
7 breakable location(s):
[0] 1:2 || >get_local 0
[1] 2:2 || >if
[2] 3:4 || >block
[3] 4:6 || >call 0
[4] 5:4 || >end
[5] 6:2 || >end
[6] 7:0 || >end
Requesting breakable locations in lines [0,3)
2 breakable location(s):
[0] 1:2 || >get_local 0
[1] 2:2 || >if
Requesting breakable locations in lines [4,6)
2 breakable location(s):
[0] 4:6 || >call 0
[1] 5:4 || >end
Setting a breakpoint on each breakable location...
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-0:2:2
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-0:3:2
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-0:4:2
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-0:5:0
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-1:1:2
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-1:2:2
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-1:3:4
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-1:4:6
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-1:5:4
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-1:6:2
Success!
Setting at wasm://wasm/wasm-824d794e/wasm-824d794e-1:7:0
Success!
Running wasm code...
Missing breakpoints: 11
Script nr 4 parsed. URL: v8://test/runWasm
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-1:1:2
Missing breakpoints: 10
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-1:2:2
Missing breakpoints: 9
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-1:3:4
Missing breakpoints: 8
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-1:4:6
Missing breakpoints: 7
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-0:2:2
Missing breakpoints: 6
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-0:3:2
Missing breakpoints: 5
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-0:4:2
Missing breakpoints: 4
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-0:5:0
Missing breakpoints: 3
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-1:5:4
Missing breakpoints: 2
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-1:6:2
Missing breakpoints: 1
Stopped at wasm://wasm/wasm-824d794e/wasm-824d794e-1:7:0
Missing breakpoints: 0
Finished!
// Copyright 2016 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.
// Flags: --expose-wasm
load('test/mjsunit/wasm/wasm-constants.js');
load('test/mjsunit/wasm/wasm-module-builder.js');
var builder = new WasmModuleBuilder();
// clang-format off
var func_idx = builder.addFunction('helper', kSig_v_v)
.addLocals({i32_count: 1})
.addBody([
kExprNop,
kExprI32Const, 12,
kExprSetLocal, 0,
]).index;
builder.addFunction('main', kSig_v_i)
.addBody([
kExprGetLocal, 0,
kExprIf, kWasmStmt,
kExprBlock, kWasmStmt,
kExprCallFunction, func_idx,
kExprEnd,
kExprEnd
]).exportAs('main');
// clang-format on
var module_bytes = builder.toArray();
function testFunction(bytes) {
var buffer = new ArrayBuffer(bytes.length);
var view = new Uint8Array(buffer);
for (var i = 0; i < bytes.length; i++) {
view[i] = bytes[i] | 0;
}
var module = new WebAssembly.Module(buffer);
// Set global variable.
instance = new WebAssembly.Instance(module);
}
var evalWithUrl = (code, url) => Protocol.Runtime.evaluate(
{'expression': code + '\n//# sourceURL=v8://test/' + url});
var setupCode = testFunction.toString() + ';\nvar module_bytes = ' +
JSON.stringify(module_bytes) + ';\nvar instance;';
Protocol.Debugger.enable();
Protocol.Debugger.onScriptParsed(handleScriptParsed);
InspectorTest.log('Running testFunction...');
evalWithUrl(setupCode, 'setup')
.then(() => evalWithUrl('testFunction(module_bytes)', 'runTestFunction'))
.then(getBreakableLocationsForAllWasmScripts)
.then(setAllBreakableLocations)
.then(() => InspectorTest.log('Running wasm code...'))
.then(() => (evalWithUrl('instance.exports.main(1)', 'runWasm'), 0))
.then(waitForAllPauses)
.then(() => InspectorTest.log('Finished!'))
.then(InspectorTest.completeTest);
var allBreakableLocations = [];
var urls = {};
var numScripts = 0;
var wasmScripts = [];
function handleScriptParsed(messageObject) {
var scriptId = messageObject.params.scriptId;
var url = messageObject.params.url;
urls[scriptId] = url;
InspectorTest.log('Script nr ' + numScripts + ' parsed. URL: ' + url);
++numScripts;
if (url.startsWith('wasm://')) {
InspectorTest.log('This is a wasm script (nr ' + wasmScripts.length + ').');
wasmScripts.push(scriptId);
}
}
function printFailure(message) {
if (!message.result) {
InspectorTest.logMessage(message);
}
return message;
}
function printBreakableLocations(message, expectedScriptId, source) {
var lines = source.split('\n');
var locations = message.result.locations;
InspectorTest.log(locations.length + ' breakable location(s):');
for (var i = 0; i < locations.length; ++i) {
if (locations[i].scriptId != expectedScriptId) {
InspectorTest.log(
'SCRIPT ID MISMATCH!! ' + locations[i].scriptId + ' != ' +
expectedScriptId);
}
var line = '<illegal line number>';
if (locations[i].lineNumber < lines.length) {
line = lines[locations[i].lineNumber];
if (locations[i].columnNumber < line.length) {
line = line.substr(0, locations[i].columnNumber) + '>' +
line.substr(locations[i].columnNumber);
}
}
InspectorTest.log(
'[' + i + '] ' + locations[i].lineNumber + ':' +
locations[i].columnNumber + ' || ' + line);
}
}
function checkGetBreakableLocations(wasmScriptNr) {
InspectorTest.log(
'Requesting all breakable locations in wasm script ' + wasmScriptNr);
var scriptId = wasmScripts[wasmScriptNr];
var source;
return Protocol.Debugger.getScriptSource({scriptId: scriptId})
.then(msg => source = msg.result.scriptSource)
.then(
() => Protocol.Debugger.getPossibleBreakpoints(
{start: {lineNumber: 0, columnNumber: 0, scriptId: scriptId}}))
.then(printFailure)
.then(msg => (allBreakableLocations.push(...msg.result.locations), msg))
.then(msg => printBreakableLocations(msg, scriptId, source))
.then(
() => InspectorTest.log(
'Requesting breakable locations in lines [0,3)'))
.then(() => Protocol.Debugger.getPossibleBreakpoints({
start: {lineNumber: 0, columnNumber: 0, scriptId: scriptId},
end: {lineNumber: 3, columnNumber: 0, scriptId: scriptId}
}))
.then(printFailure)
.then(msg => printBreakableLocations(msg, scriptId, source))
.then(
() => InspectorTest.log(
'Requesting breakable locations in lines [4,6)'))
.then(() => Protocol.Debugger.getPossibleBreakpoints({
start: {lineNumber: 4, columnNumber: 0, scriptId: scriptId},
end: {lineNumber: 6, columnNumber: 0, scriptId: scriptId}
}))
.then(printFailure)
.then(msg => printBreakableLocations(msg, scriptId, source));
}
function getBreakableLocationsForAllWasmScripts() {
InspectorTest.log('Querying breakable locations for all wasm scripts now...');
var promise = Promise.resolve();
for (var wasmScriptNr = 0; wasmScriptNr < wasmScripts.length;
++wasmScriptNr) {
promise = promise.then(checkGetBreakableLocations.bind(null, wasmScriptNr));
}
return promise;
}
function locationMatches(loc1, loc2) {
return loc1.scriptId == loc2.scriptId && loc1.lineNumber == loc2.lineNumber &&
loc1.columnNumber == loc2.columnNumber;
}
function locationStr(loc) {
return urls[loc.scriptId] + ':' + loc.lineNumber + ':' + loc.columnNumber;
}
function setBreakpoint(loc) {
InspectorTest.log('Setting at ' + locationStr(loc));
function check(msg) {
if (locationMatches(loc, msg.result.actualLocation)) {
InspectorTest.log("Success!");
} else {
InspectorTest.log("Mismatch!");
InspectorTest.logMessage(msg);
}
}
return Protocol.Debugger.setBreakpoint({'location': loc})
.then(printFailure)
.then(check);
}
function setAllBreakableLocations() {
InspectorTest.log('Setting a breakpoint on each breakable location...');
var promise = Promise.resolve();
for (var loc of allBreakableLocations) {
promise = promise.then(setBreakpoint.bind(null, loc));
}
return promise;
}
function removePausedLocation(msg) {
var topLocation = msg.params.callFrames[0].location;
InspectorTest.log('Stopped at ' + locationStr(topLocation));
for (var i = 0; i < allBreakableLocations.length; ++i) {
if (locationMatches(topLocation, allBreakableLocations[i])) {
allBreakableLocations.splice(i, 1);
--i;
}
}
}
function waitForAllPauses() {
InspectorTest.log('Missing breakpoints: ' + allBreakableLocations.length);
if (allBreakableLocations.length == 0) return;
return Protocol.Debugger.oncePaused()
.then(removePausedLocation)
.then(Protocol.Debugger.resume())
.then(waitForAllPauses);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment