[wasm] Allocate a single script per wasm module

Before, we allocated one script per function per instance, and each
script referenced the wasm instance and the function index. Now we only
allocate one script per compiled wasm module, so the script also only
references this WasmCompiledModule, which causes changes to many interfaces.

Instead of fixing the disassemble API only used via debug.js, I decided
to drop it for now. Some later CL will reintroduce it via
DebugInterface.

BUG=v8:5530,chromium:659715
R=yangguo@chromium.org, titzer@chromium.org
CC=jgruber@chromium.org

Review-Url: https://codereview.chromium.org/2493823003
Cr-Commit-Position: refs/heads/master@{#41004}
This commit is contained in:
clemensh 2016-11-15 09:05:13 -08:00 committed by Commit bot
parent 244a049bfc
commit 32077e01fb
20 changed files with 248 additions and 483 deletions

View File

@ -858,16 +858,6 @@ Debug.debuggerFlags = function() {
return debugger_flags;
};
Debug.getWasmFunctionOffsetTable = function(scriptId) {
var script = scriptById(scriptId);
return script ? %GetWasmFunctionOffsetTable(script) : UNDEFINED;
}
Debug.disassembleWasmFunction = function(scriptId) {
var script = scriptById(scriptId);
return script ? %DisassembleWasmFunction(script) : UNDEFINED;
}
Debug.MakeMirror = MakeMirror;
function MakeExecutionState(break_id) {

View File

@ -1517,11 +1517,7 @@ uint32_t WasmFrame::function_index() const {
Script* WasmFrame::script() const {
Handle<JSObject> instance(JSObject::cast(wasm_instance()), isolate());
if (wasm::WasmIsAsmJs(*instance, isolate())) {
return *wasm::GetAsmWasmScript(instance);
}
Handle<WasmDebugInfo> debug_info = wasm::GetDebugInfo(instance);
return WasmDebugInfo::GetFunctionScript(debug_info, function_index());
return *wasm::GetScript(instance);
}
int WasmFrame::position() const {

View File

@ -550,7 +550,7 @@ v8::Local<v8::Value> V8Debugger::callInternalGetterFunction(
.ToLocalChecked();
DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction());
return v8::Local<v8::Function>::Cast(getterValue)
->Call(m_isolate->GetCurrentContext(), object, 0, 0)
->Call(m_isolate->GetCurrentContext(), object, 0, nullptr)
.ToLocalChecked();
}

View File

@ -12,6 +12,7 @@
#include "src/keys.h"
#include "src/string-builder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
namespace v8 {
namespace internal {
@ -635,8 +636,15 @@ Handle<Object> WasmStackFrame::GetFunction() const {
}
Handle<Object> WasmStackFrame::GetFunctionName() {
return wasm::GetWasmFunctionNameOrNull(isolate_, wasm_instance_,
wasm_func_index_);
Handle<Object> name;
Handle<WasmCompiledModule> compiled_module(
Handle<WasmInstanceObject>::cast(wasm_instance_)->get_compiled_module(),
isolate_);
if (!WasmCompiledModule::GetFunctionName(compiled_module, wasm_func_index_)
.ToHandle(&name)) {
name = isolate_->factory()->null_value();
}
return name;
}
MaybeHandle<String> WasmStackFrame::ToString() {
@ -683,18 +691,15 @@ Handle<Object> AsmJsWasmStackFrame::GetFunction() const {
Handle<Object> AsmJsWasmStackFrame::GetFileName() {
Handle<Script> script =
wasm::GetAsmWasmScript(Handle<JSObject>::cast(wasm_instance_));
wasm::GetScript(Handle<JSObject>::cast(wasm_instance_));
DCHECK_EQ(Script::TYPE_NORMAL, script->type());
return handle(script->name(), isolate_);
}
Handle<Object> AsmJsWasmStackFrame::GetFunctionName() {
return wasm::GetWasmFunctionNameOrNull(isolate_, wasm_instance_,
wasm_func_index_);
}
Handle<Object> AsmJsWasmStackFrame::GetScriptNameOrSourceUrl() {
Handle<Script> script =
wasm::GetAsmWasmScript(Handle<JSObject>::cast(wasm_instance_));
wasm::GetScript(Handle<JSObject>::cast(wasm_instance_));
DCHECK_EQ(Script::TYPE_NORMAL, script->type());
return ScriptNameOrSourceUrl(script, isolate_);
}
@ -708,14 +713,16 @@ int AsmJsWasmStackFrame::GetPosition() const {
int AsmJsWasmStackFrame::GetLineNumber() {
DCHECK_LE(0, GetPosition());
Handle<Script> script =
wasm::GetAsmWasmScript(Handle<JSObject>::cast(wasm_instance_));
wasm::GetScript(Handle<JSObject>::cast(wasm_instance_));
DCHECK_EQ(Script::TYPE_NORMAL, script->type());
return Script::GetLineNumber(script, GetPosition()) + 1;
}
int AsmJsWasmStackFrame::GetColumnNumber() {
DCHECK_LE(0, GetPosition());
Handle<Script> script =
wasm::GetAsmWasmScript(Handle<JSObject>::cast(wasm_instance_));
wasm::GetScript(Handle<JSObject>::cast(wasm_instance_));
DCHECK_EQ(Script::TYPE_NORMAL, script->type());
return Script::GetColumnNumber(script, GetPosition()) + 1;
}

View File

@ -153,6 +153,7 @@ class WasmStackFrame : public StackFrameBase {
Isolate* isolate_;
// TODO(wasm): Use proper typing.
Handle<Object> wasm_instance_;
uint32_t wasm_func_index_;
Handle<AbstractCode> code_;
@ -172,7 +173,6 @@ class AsmJsWasmStackFrame : public WasmStackFrame {
Handle<Object> GetFunction() const override;
Handle<Object> GetFileName() override;
Handle<Object> GetFunctionName() override;
Handle<Object> GetScriptNameOrSourceUrl() override;
int GetPosition() const override;

View File

@ -5935,10 +5935,8 @@ ACCESSORS(Script, shared_function_infos, Object, kSharedFunctionInfosOffset)
SMI_ACCESSORS(Script, flags, kFlagsOffset)
ACCESSORS(Script, source_url, Object, kSourceUrlOffset)
ACCESSORS(Script, source_mapping_url, Object, kSourceMappingUrlOffset)
ACCESSORS_CHECKED(Script, wasm_instance, JSObject, kEvalFromSharedOffset,
ACCESSORS_CHECKED(Script, wasm_compiled_module, Object, kEvalFromSharedOffset,
this->type() == TYPE_WASM)
SMI_ACCESSORS_CHECKED(Script, wasm_function_index, kEvalFromPositionOffset,
this->type() == TYPE_WASM)
Script::CompilationType Script::compilation_type() {
return BooleanBit::get(flags(), kCompilationTypeBit) ?

View File

@ -62,6 +62,7 @@
#include "src/string-stream.h"
#include "src/utils.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/zone/zone.h"
#ifdef ENABLE_DISASSEMBLER
@ -13403,6 +13404,7 @@ int Script::GetEvalPosition() {
void Script::InitLineEnds(Handle<Script> script) {
Isolate* isolate = script->GetIsolate();
if (!script->line_ends()->IsUndefined(isolate)) return;
DCHECK_NE(Script::TYPE_WASM, script->type());
Object* src_obj = script->source();
if (!src_obj->IsString()) {
@ -13420,6 +13422,16 @@ void Script::InitLineEnds(Handle<Script> script) {
bool Script::GetPositionInfo(Handle<Script> script, int position,
PositionInfo* info, OffsetFlag offset_flag) {
// For wasm, we do not create an artificial line_ends array, but do the
// translation directly.
if (script->type() == Script::TYPE_WASM) {
Handle<WasmCompiledModule> compiled_module(
WasmCompiledModule::cast(script->wasm_compiled_module()));
DCHECK_LE(0, position);
return wasm::GetPositionInfo(compiled_module,
static_cast<uint32_t>(position), info);
}
InitLineEnds(script);
return script->GetPositionInfo(position, info, offset_flag);
}

View File

@ -7084,13 +7084,9 @@ class Script: public Struct {
// [source_mapping_url]: sourceMappingURL magic comment
DECL_ACCESSORS(source_mapping_url, Object)
// [wasm_instance]: the wasm instance this script belongs to.
// [wasm_compiled_module]: the compiled wasm module this script belongs to.
// This must only be called if the type of this script is TYPE_WASM.
DECL_ACCESSORS(wasm_instance, JSObject)
// [wasm_function_index]: the wasm function index this script belongs to.
// This must only be called if the type of this script is TYPE_WASM.
DECL_INT_ACCESSORS(wasm_function_index)
DECL_ACCESSORS(wasm_compiled_module, Object)
// [compilation_type]: how the the script was compiled. Encoded in the
// 'flags' field.

View File

@ -569,8 +569,20 @@ RUNTIME_FUNCTION(Runtime_GetFrameDetails) {
details->set(kFrameDetailsLocalCountIndex, Smi::kZero);
// Add the source position.
// For wasm, it is function-local, so translate it to a module-relative
// position, such that together with the script it uniquely identifies the
// position.
Handle<Object> positionValue;
if (position != kNoSourcePosition) {
details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
int translated_position = position;
if (!wasm::WasmIsAsmJs(*wasm_instance, isolate)) {
Handle<WasmCompiledModule> compiled_module(
wasm::GetCompiledModule(JSObject::cast(*wasm_instance)), isolate);
translated_position +=
wasm::GetFunctionCodeOffset(compiled_module, func_index);
}
details->set(kFrameDetailsSourcePositionIndex,
Smi::FromInt(translated_position));
}
// Add the constructor information.
@ -1897,36 +1909,5 @@ RUNTIME_FUNCTION(Runtime_DebugBreakInOptimizedCode) {
return NULL;
}
// TODO(5530): Remove once uses in debug.js are gone.
RUNTIME_FUNCTION(Runtime_GetWasmFunctionOffsetTable) {
DCHECK(args.length() == 1);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSValue, script_val, 0);
CHECK(script_val->value()->IsScript());
Handle<Script> script = Handle<Script>(Script::cast(script_val->value()));
Handle<WasmDebugInfo> debug_info =
wasm::GetDebugInfo(handle(script->wasm_instance(), isolate));
Handle<FixedArray> elements = WasmDebugInfo::GetFunctionOffsetTable(
debug_info, script->wasm_function_index());
return *isolate->factory()->NewJSArrayWithElements(elements);
}
// TODO(5530): Remove once uses in debug.js are gone.
RUNTIME_FUNCTION(Runtime_DisassembleWasmFunction) {
DCHECK(args.length() == 1);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSValue, script_val, 0);
CHECK(script_val->value()->IsScript());
Handle<Script> script = Handle<Script>(Script::cast(script_val->value()));
Handle<WasmDebugInfo> debug_info =
wasm::GetDebugInfo(handle(script->wasm_instance(), isolate));
return *WasmDebugInfo::DisassembleFunction(debug_info,
script->wasm_function_index());
}
} // namespace internal
} // namespace v8

View File

@ -199,9 +199,7 @@ namespace internal {
F(DebugNextMicrotaskId, 0, 1) \
F(DebugAsyncTaskEvent, 3, 1) \
F(DebugIsActive, 0, 1) \
F(DebugBreakInOptimizedCode, 0, 1) \
F(GetWasmFunctionOffsetTable, 1, 1) \
F(DisassembleWasmFunction, 1, 1)
F(DebugBreakInOptimizedCode, 0, 1)
#define FOR_EACH_INTRINSIC_ERROR(F) F(ErrorToString, 1, 1)

View File

@ -1163,9 +1163,8 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone,
return decoder.DecodeSingleFunction(module_env, function);
}
FunctionOffsetsResult DecodeWasmFunctionOffsets(
const byte* module_start, const byte* module_end,
uint32_t num_imported_functions) {
FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
const byte* module_end) {
// Find and decode the code section.
Vector<const byte> code_section =
FindSection(module_start, module_end, kCodeSectionCode);
@ -1179,12 +1178,9 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
uint32_t functions_count = decoder.consume_u32v("functions count");
// Reserve space for the entries, taking care of invalid input.
if (functions_count < static_cast<unsigned>(code_section.length()) / 2) {
table.reserve(num_imported_functions + functions_count);
table.reserve(functions_count);
}
// Add null entries for the imported functions.
table.resize(num_imported_functions);
int section_offset = static_cast<int>(code_section.start() - module_start);
DCHECK_LE(0, section_offset);
for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
@ -1200,20 +1196,16 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
}
AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start,
const byte* tables_end,
uint32_t num_imported_functions) {
const byte* tables_end) {
AsmJsOffsets table;
Decoder decoder(tables_start, tables_end);
uint32_t functions_count = decoder.consume_u32v("functions count");
// Reserve space for the entries, taking care of invalid input.
if (functions_count < static_cast<unsigned>(tables_end - tables_start)) {
table.reserve(num_imported_functions + functions_count);
table.reserve(functions_count);
}
// Add null entries for the imported functions.
table.resize(num_imported_functions);
for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
uint32_t size = decoder.consume_u32v("table size");
if (size == 0) {

View File

@ -44,9 +44,8 @@ V8_EXPORT_PRIVATE FunctionResult DecodeWasmFunction(Isolate* isolate,
// Extracts the function offset table from the wasm module bytes.
// Returns a vector with <offset, length> entries, or failure if the wasm bytes
// are detected as invalid. Note that this validation is not complete.
FunctionOffsetsResult DecodeWasmFunctionOffsets(
const byte* module_start, const byte* module_end,
uint32_t num_imported_functions);
FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
const byte* module_end);
V8_EXPORT_PRIVATE WasmInitExpr DecodeWasmInitExprForTesting(const byte* start,
const byte* end);
@ -57,8 +56,7 @@ V8_EXPORT_PRIVATE WasmInitExpr DecodeWasmInitExprForTesting(const byte* start,
// failure if the wasm bytes are detected as invalid. Note that this validation
// is not complete.
AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* module_start,
const byte* module_end,
uint32_t num_imported_functions);
const byte* module_end);
} // namespace wasm
} // namespace internal

View File

@ -18,97 +18,40 @@ namespace {
enum {
kWasmDebugInfoWasmObj,
kWasmDebugInfoWasmBytesHash,
kWasmDebugInfoFunctionByteOffsets,
kWasmDebugInfoFunctionScripts,
kWasmDebugInfoAsmJsOffsets,
kWasmDebugInfoNumEntries
};
ByteArray *GetOrCreateFunctionOffsetTable(Handle<WasmDebugInfo> debug_info) {
Object *offset_table = debug_info->get(kWasmDebugInfoFunctionByteOffsets);
Isolate *isolate = debug_info->GetIsolate();
if (!offset_table->IsUndefined(isolate)) return ByteArray::cast(offset_table);
FunctionOffsetsResult function_offsets;
{
Handle<JSObject> wasm_instance(debug_info->wasm_instance(), isolate);
uint32_t num_imported_functions =
static_cast<uint32_t>(wasm::GetNumImportedFunctions(wasm_instance));
Handle<SeqOneByteString> wasm_bytes = wasm::GetWasmBytes(wasm_instance);
DisallowHeapAllocation no_gc;
const byte *bytes_start = wasm_bytes->GetChars();
const byte *bytes_end = bytes_start + wasm_bytes->length();
function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end,
num_imported_functions);
}
DCHECK(function_offsets.ok());
size_t array_size = 2 * kIntSize * function_offsets.val.size();
CHECK_LE(array_size, static_cast<size_t>(kMaxInt));
ByteArray *arr =
*isolate->factory()->NewByteArray(static_cast<int>(array_size));
int idx = 0;
for (std::pair<int, int> p : function_offsets.val) {
arr->set_int(idx++, p.first);
arr->set_int(idx++, p.second);
}
DCHECK_EQ(arr->length(), idx * kIntSize);
debug_info->set(kWasmDebugInfoFunctionByteOffsets, arr);
return arr;
}
std::pair<int, int> GetFunctionOffsetAndLength(Handle<WasmDebugInfo> debug_info,
int func_index) {
ByteArray *arr = GetOrCreateFunctionOffsetTable(debug_info);
DCHECK(func_index >= 0 && func_index < arr->length() / kIntSize / 2);
int offset = arr->get_int(2 * func_index);
int length = arr->get_int(2 * func_index + 1);
// Assert that it's distinguishable from the "illegal function index" return.
DCHECK(offset > 0 && length > 0);
return {offset, length};
}
Vector<const uint8_t> GetFunctionBytes(Handle<WasmDebugInfo> debug_info,
int func_index) {
Handle<JSObject> wasm_instance(debug_info->wasm_instance());
Handle<SeqOneByteString> module_bytes = wasm::GetWasmBytes(wasm_instance);
std::pair<int, int> offset_and_length =
GetFunctionOffsetAndLength(debug_info, func_index);
return Vector<const uint8_t>(
module_bytes->GetChars() + offset_and_length.first,
offset_and_length.second);
}
FixedArray *GetOffsetTables(Handle<WasmDebugInfo> debug_info,
Isolate *isolate) {
// TODO(clemensh): Move asm.js offset tables to the compiled module.
FixedArray *GetAsmJsOffsetTables(Handle<WasmDebugInfo> debug_info,
Isolate *isolate) {
Object *offset_tables = debug_info->get(kWasmDebugInfoAsmJsOffsets);
if (!offset_tables->IsUndefined(isolate)) {
return FixedArray::cast(offset_tables);
}
Handle<JSObject> wasm_instance(debug_info->wasm_instance(), isolate);
Handle<WasmCompiledModule> compiled_module(GetCompiledModule(*wasm_instance),
isolate);
DCHECK(compiled_module->has_asm_js_offset_tables());
AsmJsOffsetsResult asm_offsets;
{
Handle<JSObject> wasm_instance(debug_info->wasm_instance(), isolate);
Handle<WasmCompiledModule> compiled_module =
handle(GetCompiledModule(*wasm_instance), isolate);
DCHECK(compiled_module->has_asm_js_offset_tables());
Handle<ByteArray> asm_offset_tables =
compiled_module->asm_js_offset_tables();
uint32_t num_imported_functions =
static_cast<uint32_t>(wasm::GetNumImportedFunctions(wasm_instance));
DisallowHeapAllocation no_gc;
const byte *bytes_start = asm_offset_tables->GetDataStartAddress();
const byte *bytes_end = bytes_start + asm_offset_tables->length();
asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end,
num_imported_functions);
asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end);
}
// Wasm bytes must be valid and must contain asm.js offset table.
DCHECK(asm_offsets.ok());
DCHECK_GE(static_cast<size_t>(kMaxInt), asm_offsets.val.size());
int num_functions = static_cast<int>(asm_offsets.val.size());
DCHECK_EQ(wasm::GetNumberOfFunctions(handle(debug_info->wasm_instance())),
num_functions);
DCHECK_EQ(
wasm::GetNumberOfFunctions(handle(debug_info->wasm_instance())),
static_cast<int>(num_functions +
compiled_module->module()->num_imported_functions));
Handle<FixedArray> all_tables =
isolate->factory()->NewFixedArray(num_functions);
debug_info->set(kWasmDebugInfoAsmJsOffsets, *all_tables);
@ -155,14 +98,9 @@ Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) {
bool WasmDebugInfo::IsDebugInfo(Object *object) {
if (!object->IsFixedArray()) return false;
FixedArray *arr = FixedArray::cast(object);
Isolate *isolate = arr->GetIsolate();
return arr->length() == kWasmDebugInfoNumEntries &&
IsWasmInstance(arr->get(kWasmDebugInfoWasmObj)) &&
arr->get(kWasmDebugInfoWasmBytesHash)->IsNumber() &&
(arr->get(kWasmDebugInfoFunctionByteOffsets)->IsUndefined(isolate) ||
arr->get(kWasmDebugInfoFunctionByteOffsets)->IsByteArray()) &&
(arr->get(kWasmDebugInfoFunctionScripts)->IsUndefined(isolate) ||
arr->get(kWasmDebugInfoFunctionScripts)->IsFixedArray());
arr->get(kWasmDebugInfoWasmBytesHash)->IsNumber();
}
WasmDebugInfo *WasmDebugInfo::cast(Object *object) {
@ -174,125 +112,17 @@ JSObject *WasmDebugInfo::wasm_instance() {
return JSObject::cast(get(kWasmDebugInfoWasmObj));
}
Script *WasmDebugInfo::GetFunctionScript(Handle<WasmDebugInfo> debug_info,
int func_index) {
Isolate *isolate = debug_info->GetIsolate();
Object *scripts_obj = debug_info->get(kWasmDebugInfoFunctionScripts);
Handle<FixedArray> scripts;
if (scripts_obj->IsUndefined(isolate)) {
Handle<JSObject> wasm_instance(debug_info->wasm_instance(), isolate);
int num_functions = wasm::GetNumberOfFunctions(wasm_instance);
scripts = isolate->factory()->NewFixedArray(num_functions, TENURED);
debug_info->set(kWasmDebugInfoFunctionScripts, *scripts);
} else {
scripts = handle(FixedArray::cast(scripts_obj), isolate);
}
DCHECK(func_index >= 0 && func_index < scripts->length());
Object *script_or_undef = scripts->get(func_index);
if (!script_or_undef->IsUndefined(isolate)) {
return Script::cast(script_or_undef);
}
Handle<Script> script =
isolate->factory()->NewScript(isolate->factory()->empty_string());
scripts->set(func_index, *script);
script->set_type(Script::TYPE_WASM);
script->set_wasm_instance(debug_info->wasm_instance());
script->set_wasm_function_index(func_index);
int hash = 0;
debug_info->get(kWasmDebugInfoWasmBytesHash)->ToInt32(&hash);
char buffer[32];
SNPrintF(ArrayVector(buffer), "wasm://%08x/%d", hash, func_index);
Handle<String> source_url =
isolate->factory()->NewStringFromAsciiChecked(buffer, TENURED);
script->set_source_url(*source_url);
int func_bytes_len =
GetFunctionOffsetAndLength(debug_info, func_index).second;
Handle<FixedArray> line_ends = isolate->factory()->NewFixedArray(1, TENURED);
line_ends->set(0, Smi::FromInt(func_bytes_len));
line_ends->set_map(isolate->heap()->fixed_cow_array_map());
script->set_line_ends(*line_ends);
// TODO(clemensh): Register with the debugger. Note that we cannot call into
// JS at this point since this function is called from within stack trace
// collection (which means we cannot call Debug::OnAfterCompile in its
// current form). See crbug.com/641065.
if (false) isolate->debug()->OnAfterCompile(script);
return *script;
}
Handle<String> WasmDebugInfo::DisassembleFunction(
Handle<WasmDebugInfo> debug_info, int func_index) {
std::ostringstream disassembly_os;
{
Vector<const uint8_t> bytes_vec = GetFunctionBytes(debug_info, func_index);
DisallowHeapAllocation no_gc;
AccountingAllocator allocator;
bool ok = PrintAst(
&allocator, FunctionBodyForTesting(bytes_vec.start(), bytes_vec.end()),
disassembly_os, nullptr);
DCHECK(ok);
USE(ok);
}
// Unfortunately, we have to copy the string here.
std::string code_str = disassembly_os.str();
CHECK_LE(code_str.length(), static_cast<size_t>(kMaxInt));
Factory *factory = debug_info->GetIsolate()->factory();
Vector<const char> code_vec(code_str.data(),
static_cast<int>(code_str.length()));
return factory->NewStringFromAscii(code_vec).ToHandleChecked();
}
Handle<FixedArray> WasmDebugInfo::GetFunctionOffsetTable(
Handle<WasmDebugInfo> debug_info, int func_index) {
class NullBuf : public std::streambuf {};
NullBuf null_buf;
std::ostream null_stream(&null_buf);
std::vector<std::tuple<uint32_t, int, int>> offset_table_vec;
{
Vector<const uint8_t> bytes_vec = GetFunctionBytes(debug_info, func_index);
DisallowHeapAllocation no_gc;
AccountingAllocator allocator;
bool ok = PrintAst(
&allocator, FunctionBodyForTesting(bytes_vec.start(), bytes_vec.end()),
null_stream, &offset_table_vec);
DCHECK(ok);
USE(ok);
}
size_t arr_size = 3 * offset_table_vec.size();
CHECK_LE(arr_size, static_cast<size_t>(kMaxInt));
Factory *factory = debug_info->GetIsolate()->factory();
Handle<FixedArray> offset_table =
factory->NewFixedArray(static_cast<int>(arr_size), TENURED);
int idx = 0;
for (std::tuple<uint32_t, int, int> elem : offset_table_vec) {
offset_table->set(idx++, Smi::FromInt(std::get<0>(elem)));
offset_table->set(idx++, Smi::FromInt(std::get<1>(elem)));
offset_table->set(idx++, Smi::FromInt(std::get<2>(elem)));
}
DCHECK_EQ(idx, offset_table->length());
return offset_table;
}
int WasmDebugInfo::GetAsmJsSourcePosition(Handle<WasmDebugInfo> debug_info,
int func_index, int byte_offset) {
Isolate *isolate = debug_info->GetIsolate();
FixedArray *offset_tables = GetOffsetTables(debug_info, isolate);
Handle<JSObject> instance(debug_info->wasm_instance(), isolate);
FixedArray *offset_tables = GetAsmJsOffsetTables(debug_info, isolate);
WasmCompiledModule *compiled_module = wasm::GetCompiledModule(*instance);
int num_imported_functions =
compiled_module->module()->num_imported_functions;
DCHECK_LE(num_imported_functions, func_index);
func_index -= num_imported_functions;
DCHECK_LT(func_index, offset_tables->length());
ByteArray *offset_table = ByteArray::cast(offset_tables->get(func_index));

View File

@ -51,6 +51,8 @@ MaybeHandle<String> ExtractStringFromModuleBytes(
uint32_t offset, uint32_t size) {
// TODO(wasm): cache strings from modules if it's a performance win.
Handle<SeqOneByteString> module_bytes = compiled_module->module_bytes();
DCHECK_GE(static_cast<size_t>(module_bytes->length()), offset);
DCHECK_GE(static_cast<size_t>(module_bytes->length() - offset), size);
Address raw = module_bytes->GetCharsAddress() + offset;
if (!unibrow::Utf8::Validate(reinterpret_cast<const byte*>(raw), size))
return {}; // UTF8 decoding error for name.
@ -583,6 +585,18 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
TRACE("}\n");
}
std::pair<int, int> GetFunctionOffsetAndLength(
Handle<WasmCompiledModule> compiled_module, int func_index) {
WasmModule* module = compiled_module->module();
if (func_index < 0 ||
static_cast<size_t>(func_index) > module->functions.size()) {
return {0, 0};
}
WasmFunction& func = module->functions[func_index];
return {static_cast<int>(func.code_start_offset),
static_cast<int>(func.code_end_offset - func.code_start_offset)};
}
} // namespace
const char* wasm::SectionName(WasmSectionCode code) {
@ -664,10 +678,38 @@ Object* wasm::GetOwningWasmInstance(Code* code) {
return cell->value();
}
int wasm::GetNumImportedFunctions(Handle<JSObject> object) {
return static_cast<int>(Handle<WasmInstanceObject>::cast(object)
->module()
->num_imported_functions);
int wasm::GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
int func_index) {
return GetFunctionOffsetAndLength(compiled_module, func_index).first;
}
bool wasm::GetPositionInfo(Handle<WasmCompiledModule> compiled_module,
uint32_t position, Script::PositionInfo* info) {
std::vector<WasmFunction>& functions = compiled_module->module()->functions;
// Binary search for a function containing the given position.
int left = 0; // inclusive
int right = static_cast<int>(functions.size()); // exclusive
if (right == 0) return false;
while (right - left > 1) {
int mid = left + (right - left) / 2;
if (functions[mid].code_start_offset <= position) {
left = mid;
} else {
right = mid;
}
}
// If the found entry does not contains the given position, return false.
WasmFunction& func = functions[left];
if (position < func.code_start_offset || position >= func.code_end_offset) {
return false;
}
info->line = left;
info->column = position - func.code_start_offset;
info->line_start = func.code_start_offset;
info->line_end = func.code_end_offset;
return true;
}
WasmModule::WasmModule(Zone* owned, const byte* module_start)
@ -1800,29 +1842,16 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
return builder.Build();
}
Handle<Object> wasm::GetWasmFunctionNameOrNull(Isolate* isolate,
Handle<Object> object,
uint32_t func_index) {
if (!object->IsUndefined(isolate)) {
auto instance = Handle<WasmInstanceObject>::cast(object);
WasmModule* module = instance->module();
WasmFunction& function = module->functions[func_index];
Handle<WasmCompiledModule> compiled_module(instance->get_compiled_module(),
isolate);
MaybeHandle<String> string = ExtractStringFromModuleBytes(
isolate, compiled_module, function.name_offset, function.name_length);
if (!string.is_null()) return string.ToHandleChecked();
}
return isolate->factory()->null_value();
}
Handle<String> wasm::GetWasmFunctionName(Isolate* isolate,
Handle<Object> instance,
Handle<Object> instance_or_undef,
uint32_t func_index) {
Handle<Object> name_or_null =
GetWasmFunctionNameOrNull(isolate, instance, func_index);
if (!name_or_null->IsNull(isolate)) {
return Handle<String>::cast(name_or_null);
if (!instance_or_undef->IsUndefined(isolate)) {
Handle<WasmCompiledModule> compiled_module(
Handle<WasmInstanceObject>::cast(instance_or_undef)
->get_compiled_module());
MaybeHandle<String> maybe_name =
WasmCompiledModule::GetFunctionName(compiled_module, func_index);
if (!maybe_name.is_null()) return maybe_name.ToHandleChecked();
}
return isolate->factory()->NewStringFromStaticChars("<WASM UNNAMED>");
}
@ -1835,17 +1864,21 @@ WasmCompiledModule* wasm::GetCompiledModule(Object* object) {
return WasmInstanceObject::cast(object)->get_compiled_module();
}
bool wasm::WasmIsAsmJs(Object* object, Isolate* isolate) {
return IsWasmInstance(object) &&
WasmInstanceObject::cast(object)
->get_compiled_module()
->has_asm_js_script();
bool wasm::WasmIsAsmJs(Object* instance, Isolate* isolate) {
if (instance->IsUndefined(isolate)) return false;
DCHECK(IsWasmInstance(instance));
WasmCompiledModule* compiled_module =
GetCompiledModule(JSObject::cast(instance));
DCHECK_EQ(compiled_module->has_asm_js_offset_tables(),
compiled_module->script()->type() == Script::TYPE_NORMAL);
return compiled_module->has_asm_js_offset_tables();
}
Handle<Script> wasm::GetAsmWasmScript(Handle<JSObject> object) {
return Handle<WasmInstanceObject>::cast(object)
->get_compiled_module()
->asm_js_script();
Handle<Script> wasm::GetScript(Handle<JSObject> instance) {
DCHECK(IsWasmInstance(*instance));
WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
DCHECK(compiled_module->has_script());
return compiled_module->script();
}
int wasm::GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
@ -1905,10 +1938,12 @@ MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
maybe_compiled_module.ToHandleChecked();
DCHECK_EQ(origin == kAsmJsOrigin, !asm_js_script.is_null());
DCHECK(!compiled_module->has_asm_js_script());
DCHECK(!compiled_module->has_script());
DCHECK(!compiled_module->has_asm_js_offset_tables());
if (origin == kAsmJsOrigin) {
compiled_module->set_asm_js_script(asm_js_script);
// Set script for the asm.js source, and the offset table mapping wasm byte
// offsets to source positions.
compiled_module->set_script(asm_js_script);
size_t offset_tables_len =
asm_js_offset_tables_end - asm_js_offset_tables_start;
DCHECK_GE(static_cast<size_t>(kMaxInt), offset_tables_len);
@ -1917,6 +1952,37 @@ MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
memcpy(offset_tables->GetDataStartAddress(), asm_js_offset_tables_start,
offset_tables_len);
compiled_module->set_asm_js_offset_tables(offset_tables);
} else {
// Create a new Script object representing this wasm module, store it in the
// compiled wasm module, and register it at the debugger.
Handle<Script> script =
isolate->factory()->NewScript(isolate->factory()->empty_string());
script->set_type(Script::TYPE_WASM);
DCHECK_GE(kMaxInt, end - start);
int hash = StringHasher::HashSequentialString(
reinterpret_cast<const char*>(start), static_cast<int>(end - start),
kZeroHashSeed);
const int kBufferSize = 50;
char buffer[kBufferSize];
int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash);
DCHECK(url_chars >= 0 && url_chars < kBufferSize);
MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte(
Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars),
TENURED);
script->set_source_url(*url_str.ToHandleChecked());
int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
DCHECK(name_chars >= 0 && name_chars < kBufferSize);
MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars),
TENURED);
script->set_name(*name_str.ToHandleChecked());
script->set_wasm_compiled_module(*compiled_module);
compiled_module->set_script(script);
isolate->debug()->OnAfterCompile(script);
}
return WasmModuleObject::New(isolate, compiled_module);
@ -2109,3 +2175,14 @@ void WasmCompiledModule::RecreateModuleWrapper(Isolate* isolate,
compiled_module->set_module_wrapper(module_wrapper);
DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
}
MaybeHandle<String> WasmCompiledModule::GetFunctionName(
Handle<WasmCompiledModule> compiled_module, uint32_t func_index) {
DCHECK_LT(func_index, compiled_module->module()->functions.size());
WasmFunction& function = compiled_module->module()->functions[func_index];
Isolate* isolate = compiled_module->GetIsolate();
MaybeHandle<String> string = ExtractStringFromModuleBytes(
isolate, compiled_module, function.name_offset, function.name_length);
if (!string.is_null()) return string.ToHandleChecked();
return {};
}

View File

@ -353,18 +353,13 @@ std::ostream& operator<<(std::ostream& os, const WasmModule& module);
std::ostream& operator<<(std::ostream& os, const WasmFunction& function);
std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
// Extract a function name from the given wasm object.
// Returns "<WASM UNNAMED>" if the function is unnamed or the name is not a
// valid UTF-8 string.
Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> wasm,
// Extract a function name from the given wasm instance.
// Returns "<WASM UNNAMED>" if no instance is passed, the function is unnamed or
// the name is not a valid UTF-8 string.
// TODO(5620): Refactor once we always get a wasm instance.
Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> instance,
uint32_t func_index);
// Extract a function name from the given wasm object.
// Returns a null handle if the function is unnamed or the name is not a valid
// UTF-8 string.
Handle<Object> GetWasmFunctionNameOrNull(Isolate* isolate, Handle<Object> wasm,
uint32_t func_index);
// Return the binary source bytes of a wasm module.
Handle<SeqOneByteString> GetWasmBytes(Handle<JSObject> wasm);
@ -395,8 +390,10 @@ WasmCompiledModule* GetCompiledModule(Object* wasm_instance);
// Check whether the wasm module was generated from asm.js code.
bool WasmIsAsmJs(Object* instance, Isolate* isolate);
// Get the script for the asm.js origin of the wasm module.
Handle<Script> GetAsmWasmScript(Handle<JSObject> instance);
// Get the script of the wasm module. If the origin of the module is asm.js, the
// returned Script will be a JavaScript Script of Script::TYPE_NORMAL, otherwise
// it's of type TYPE_WASM.
Handle<Script> GetScript(Handle<JSObject> instance);
// Get the asm.js source position for the given byte offset in the given
// function.
@ -413,8 +410,14 @@ V8_EXPORT_PRIVATE bool ValidateModuleBytes(Isolate* isolate, const byte* start,
ErrorThrower* thrower,
ModuleOrigin origin);
// Get the number of imported functions for a WASM instance.
int GetNumImportedFunctions(Handle<JSObject> instance);
// Get the offset of the code of a function within a module.
int GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
int func_index);
// Translate from byte offset in the module to function number and byte offset
// within that function, encoded as line and column in the position info.
bool GetPositionInfo(Handle<WasmCompiledModule> compiled_module,
uint32_t position, Script::PositionInfo* info);
// Assumed to be called with a code object associated to a wasm module instance.
// Intended to be called from runtime functions.

View File

@ -189,11 +189,13 @@ class WasmCompiledModule : public FixedArray {
#define CORE_WCM_PROPERTY_TABLE(MACRO) \
MACRO(OBJECT, FixedArray, code_table) \
MACRO(OBJECT, Foreign, module_wrapper) \
/* For debugging: */ \
MACRO(OBJECT, SeqOneByteString, module_bytes) \
MACRO(OBJECT, Script, asm_js_script) \
MACRO(OBJECT, Script, script) \
MACRO(OBJECT, ByteArray, asm_js_offset_tables) \
/* End of debugging stuff */ \
MACRO(OBJECT, FixedArray, function_tables) \
MACRO(OBJECT, FixedArray, empty_function_tables) \
MACRO(OBJECT, ByteArray, asm_js_offset_tables) \
MACRO(OBJECT, JSArrayBuffer, memory) \
MACRO(SMALL_NUMBER, uint32_t, min_mem_pages) \
MACRO(SMALL_NUMBER, uint32_t, max_mem_pages) \
@ -251,6 +253,12 @@ class WasmCompiledModule : public FixedArray {
static void RecreateModuleWrapper(Isolate* isolate,
Handle<FixedArray> compiled_module);
// Extract a function name from the given wasm instance.
// Returns a null handle if the function is unnamed or the name is not a valid
// UTF-8 string.
static MaybeHandle<String> GetFunctionName(
Handle<WasmCompiledModule> compiled_module, uint32_t func_index);
private:
void InitId();

View File

@ -2,8 +2,8 @@ Running testFunction with generated WASM bytes...
Paused on 'debugger;'
Number of frames: 5
- [0] {"functionName":"call_debugger","function_lineNumber":1,"function_columnNumber":24,"lineNumber":2,"columnNumber":4}
- [1] {"functionName":"main","lineNumber":0,"columnNumber":1}
- [2] {"functionName":"main","lineNumber":0,"columnNumber":1}
- [1] {"functionName":"call_func","lineNumber":1,"columnNumber":1}
- [2] {"functionName":"main","lineNumber":2,"columnNumber":1}
- [3] {"functionName":"testFunction","function_lineNumber":0,"function_columnNumber":21,"lineNumber":14,"columnNumber":19}
- [4] {"functionName":"","function_lineNumber":0,"function_columnNumber":0,"lineNumber":0,"columnNumber":0}
Getting v8-generated stack trace...
@ -11,7 +11,7 @@ Result of evaluate (string):
Error: this is your stack trace:
-- skipped --
at call_debugger (<anonymous>:3:5)
at main (<WASM>[1]+1)
at call_func (<WASM>[1]+1)
at main (<WASM>[2]+1)
at testFunction (<anonymous>:15:20)
at <anonymous>:1:1

View File

@ -11,7 +11,7 @@ var builder = new WasmModuleBuilder();
var imported_idx = builder.addImport("func", kSig_v_v);
var call_imported_idx = builder.addFunction("main", kSig_v_v)
var call_imported_idx = builder.addFunction("call_func", kSig_v_v)
.addBody([kExprCallFunction, imported_idx])
.index;

View File

@ -1,128 +0,0 @@
// 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 --expose-debug-as debug
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
Debug = debug.Debug
// Initialized in setup().
var exception;
var break_count;
var num_wasm_scripts;
var module;
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
++break_count;
// Request frame details. This should trigger creation of the Script
// objects for all frames on the stack.
var num_frames = exec_state.frameCount();
for (var i = 0; i < num_frames; ++i) {
var frame = exec_state.frame(i);
var details = frame.details();
var script = details.script();
if (script.type == Debug.ScriptType.Wasm) {
var pos = frame.sourcePosition();
var name = script.nameOrSourceURL();
var disassembly = Debug.disassembleWasmFunction(script.id);
var offset_table = Debug.getWasmFunctionOffsetTable(script.id);
assertEquals(0, offset_table.length % 3);
var lineNr = null;
var columnNr = null;
for (var p = 0; p < offset_table.length; p += 3) {
if (offset_table[p] != pos) continue;
lineNr = offset_table[p+1];
columnNr = offset_table[p+2];
}
assertNotNull(lineNr, "position should occur in offset table");
assertNotNull(columnNr, "position should occur in offset table");
var line = disassembly.split("\n")[lineNr];
assertTrue(!!line, "line number must occur in disassembly");
assertTrue(line.length > columnNr, "column number must be valid");
var expected_string;
if (name.endsWith("/1")) {
// Function 0 calls the imported function.
expected_string = "kExprCallFunction,";
} else if (name.endsWith("/2")) {
// Function 1 calls function 0.
expected_string = "kExprCallFunction,";
} else {
assertTrue(false, "Unexpected wasm script: " + name);
}
assertTrue(line.substr(columnNr).startsWith(expected_string),
"offset " + columnNr + " should start with '" + expected_string
+ "': " + line);
}
}
} else if (event == Debug.DebugEvent.AfterCompile) {
var script = event_data.script();
if (script.scriptType() == Debug.ScriptType.Wasm) {
++num_wasm_scripts;
}
}
} catch (e) {
print("exception: " + e);
exception = e;
}
};
var builder = new WasmModuleBuilder();
builder.addImport("func", kSig_v_v);
builder.addFunction("call_import", kSig_v_v)
.addBody([kExprCallFunction, 0])
.exportFunc();
// Add a bit of unneccessary code to increase the byte offset.
builder.addFunction("call_call_import", kSig_v_v)
.addLocals({i32_count: 2})
.addBody([
kExprI32Const, 27, kExprSetLocal, 0,
kExprI32Const, (-7 & 0x7f), kExprSetLocal, 1,
kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add, kExprI64UConvertI32,
kExprI64Const, 0,
kExprI64Ne, kExprIf, kAstStmt,
kExprCallFunction, 1,
kExprEnd
])
.exportFunc();
function call_debugger() {
debugger;
}
function setup() {
module = builder.instantiate({func: call_debugger});
exception = null;
break_count = 0;
num_wasm_scripts = 0;
}
(function testRegisteredWasmScripts1() {
setup();
Debug.setListener(listener);
// Call the "call_import" function -> 1 script.
module.exports.call_import();
module.exports.call_import();
module.exports.call_call_import();
Debug.setListener(null);
assertEquals(3, break_count);
if (exception) throw exception;
})();
(function testRegisteredWasmScripts2() {
setup();
Debug.setListener(listener);
module.exports.call_call_import();
Debug.setListener(null);
assertEquals(1, break_count);
if (exception) throw exception;
})();

View File

@ -12,10 +12,14 @@ Debug = debug.Debug
var exception = null;
var break_count = 0;
const expected_num_frames = 5;
const expected_wasm_frames = [false, true, true, false, false];
const expected_wasm_positions = [0, 1, 2, 0, 0];
const expected_function_names = ["call_debugger", "wasm_2", "wasm_1", "testFrameInspection", ""];
const expected_frames = [
// func-name; wasm?; pos; line; col
['call_debugger', false], // --
['wasm_2', true, 56, 2, 1], // --
['wasm_1', true, 52, 1, 2], // --
['testFrameInspection', false], // --
['', false]
];
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
@ -23,20 +27,24 @@ function listener(event, exec_state, event_data, data) {
try {
var break_id = exec_state.break_id;
var frame_count = exec_state.frameCount();
assertEquals(expected_num_frames, frame_count);
assertEquals(expected_frames.length, frame_count, 'frame count');
for (var i = 0; i < frame_count; ++i) {
var frame = exec_state.frame(i);
assertEquals(expected_frames[i][0], frame.func().name(), 'name at ' + i);
// wasm frames have unresolved function, others resolved ones.
assertEquals(expected_wasm_frames[i], !frame.func().resolved());
assertEquals(expected_function_names[i], frame.func().name());
if (expected_wasm_frames[i]) {
assertEquals(
expected_frames[i][1], !frame.func().resolved(), 'resolved at ' + i);
if (expected_frames[i][1]) { // wasm frame?
var script = frame.details().script();
assertNotNull(script);
assertEquals(expected_wasm_positions[i], frame.details().sourcePosition());
assertNotNull(script, 'script at ' + i);
assertEquals(
expected_frames[i][2], frame.details().sourcePosition(),
'source pos at ' + i);
var loc = script.locationFromPosition(frame.details().sourcePosition());
assertEquals(expected_wasm_positions[i], loc.column);
assertEquals(expected_wasm_positions[i], loc.position);
assertEquals(expected_frames[i][2], loc.position, 'pos at ' + i);
assertEquals(expected_frames[i][3], loc.line, 'line at ' + i);
assertEquals(expected_frames[i][4], loc.column, 'column at ' + i);
}
}
} catch (e) {
@ -49,14 +57,13 @@ var builder = new WasmModuleBuilder();
// wasm_1 calls wasm_2 on offset 2.
// wasm_2 calls call_debugger on offset 1.
builder.addImport("func", kSig_v_v);
builder.addImport('func', kSig_v_v);
builder.addFunction("wasm_1", kSig_v_v)
.addBody([kExprNop, kExprCallFunction, 2])
.exportAs("main");
builder.addFunction('wasm_1', kSig_v_v)
.addBody([kExprNop, kExprCallFunction, 2])
.exportAs('main');
builder.addFunction("wasm_2", kSig_v_v)
.addBody([kExprCallFunction, 0]);
builder.addFunction('wasm_2', kSig_v_v).addBody([kExprCallFunction, 0]);
function call_debugger() {
debugger;