Commit 32077e01 authored by clemensh's avatar clemensh Committed by Commit bot

[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}
parent 244a049b
...@@ -858,16 +858,6 @@ Debug.debuggerFlags = function() { ...@@ -858,16 +858,6 @@ Debug.debuggerFlags = function() {
return debugger_flags; 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; Debug.MakeMirror = MakeMirror;
function MakeExecutionState(break_id) { function MakeExecutionState(break_id) {
......
...@@ -1517,11 +1517,7 @@ uint32_t WasmFrame::function_index() const { ...@@ -1517,11 +1517,7 @@ uint32_t WasmFrame::function_index() const {
Script* WasmFrame::script() const { Script* WasmFrame::script() const {
Handle<JSObject> instance(JSObject::cast(wasm_instance()), isolate()); Handle<JSObject> instance(JSObject::cast(wasm_instance()), isolate());
if (wasm::WasmIsAsmJs(*instance, isolate())) { return *wasm::GetScript(instance);
return *wasm::GetAsmWasmScript(instance);
}
Handle<WasmDebugInfo> debug_info = wasm::GetDebugInfo(instance);
return WasmDebugInfo::GetFunctionScript(debug_info, function_index());
} }
int WasmFrame::position() const { int WasmFrame::position() const {
......
...@@ -550,7 +550,7 @@ v8::Local<v8::Value> V8Debugger::callInternalGetterFunction( ...@@ -550,7 +550,7 @@ v8::Local<v8::Value> V8Debugger::callInternalGetterFunction(
.ToLocalChecked(); .ToLocalChecked();
DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction()); DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction());
return v8::Local<v8::Function>::Cast(getterValue) return v8::Local<v8::Function>::Cast(getterValue)
->Call(m_isolate->GetCurrentContext(), object, 0, 0) ->Call(m_isolate->GetCurrentContext(), object, 0, nullptr)
.ToLocalChecked(); .ToLocalChecked();
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "src/keys.h" #include "src/keys.h"
#include "src/string-builder.h" #include "src/string-builder.h"
#include "src/wasm/wasm-module.h" #include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -635,8 +636,15 @@ Handle<Object> WasmStackFrame::GetFunction() const { ...@@ -635,8 +636,15 @@ Handle<Object> WasmStackFrame::GetFunction() const {
} }
Handle<Object> WasmStackFrame::GetFunctionName() { Handle<Object> WasmStackFrame::GetFunctionName() {
return wasm::GetWasmFunctionNameOrNull(isolate_, wasm_instance_, Handle<Object> name;
wasm_func_index_); 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() { MaybeHandle<String> WasmStackFrame::ToString() {
...@@ -683,18 +691,15 @@ Handle<Object> AsmJsWasmStackFrame::GetFunction() const { ...@@ -683,18 +691,15 @@ Handle<Object> AsmJsWasmStackFrame::GetFunction() const {
Handle<Object> AsmJsWasmStackFrame::GetFileName() { Handle<Object> AsmJsWasmStackFrame::GetFileName() {
Handle<Script> script = 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_); return handle(script->name(), isolate_);
} }
Handle<Object> AsmJsWasmStackFrame::GetFunctionName() {
return wasm::GetWasmFunctionNameOrNull(isolate_, wasm_instance_,
wasm_func_index_);
}
Handle<Object> AsmJsWasmStackFrame::GetScriptNameOrSourceUrl() { Handle<Object> AsmJsWasmStackFrame::GetScriptNameOrSourceUrl() {
Handle<Script> script = 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_); return ScriptNameOrSourceUrl(script, isolate_);
} }
...@@ -708,14 +713,16 @@ int AsmJsWasmStackFrame::GetPosition() const { ...@@ -708,14 +713,16 @@ int AsmJsWasmStackFrame::GetPosition() const {
int AsmJsWasmStackFrame::GetLineNumber() { int AsmJsWasmStackFrame::GetLineNumber() {
DCHECK_LE(0, GetPosition()); DCHECK_LE(0, GetPosition());
Handle<Script> script = 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; return Script::GetLineNumber(script, GetPosition()) + 1;
} }
int AsmJsWasmStackFrame::GetColumnNumber() { int AsmJsWasmStackFrame::GetColumnNumber() {
DCHECK_LE(0, GetPosition()); DCHECK_LE(0, GetPosition());
Handle<Script> script = 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; return Script::GetColumnNumber(script, GetPosition()) + 1;
} }
......
...@@ -153,6 +153,7 @@ class WasmStackFrame : public StackFrameBase { ...@@ -153,6 +153,7 @@ class WasmStackFrame : public StackFrameBase {
Isolate* isolate_; Isolate* isolate_;
// TODO(wasm): Use proper typing.
Handle<Object> wasm_instance_; Handle<Object> wasm_instance_;
uint32_t wasm_func_index_; uint32_t wasm_func_index_;
Handle<AbstractCode> code_; Handle<AbstractCode> code_;
...@@ -172,7 +173,6 @@ class AsmJsWasmStackFrame : public WasmStackFrame { ...@@ -172,7 +173,6 @@ class AsmJsWasmStackFrame : public WasmStackFrame {
Handle<Object> GetFunction() const override; Handle<Object> GetFunction() const override;
Handle<Object> GetFileName() override; Handle<Object> GetFileName() override;
Handle<Object> GetFunctionName() override;
Handle<Object> GetScriptNameOrSourceUrl() override; Handle<Object> GetScriptNameOrSourceUrl() override;
int GetPosition() const override; int GetPosition() const override;
......
...@@ -5935,10 +5935,8 @@ ACCESSORS(Script, shared_function_infos, Object, kSharedFunctionInfosOffset) ...@@ -5935,10 +5935,8 @@ ACCESSORS(Script, shared_function_infos, Object, kSharedFunctionInfosOffset)
SMI_ACCESSORS(Script, flags, kFlagsOffset) SMI_ACCESSORS(Script, flags, kFlagsOffset)
ACCESSORS(Script, source_url, Object, kSourceUrlOffset) ACCESSORS(Script, source_url, Object, kSourceUrlOffset)
ACCESSORS(Script, source_mapping_url, Object, kSourceMappingUrlOffset) 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) this->type() == TYPE_WASM)
SMI_ACCESSORS_CHECKED(Script, wasm_function_index, kEvalFromPositionOffset,
this->type() == TYPE_WASM)
Script::CompilationType Script::compilation_type() { Script::CompilationType Script::compilation_type() {
return BooleanBit::get(flags(), kCompilationTypeBit) ? return BooleanBit::get(flags(), kCompilationTypeBit) ?
......
...@@ -62,6 +62,7 @@ ...@@ -62,6 +62,7 @@
#include "src/string-stream.h" #include "src/string-stream.h"
#include "src/utils.h" #include "src/utils.h"
#include "src/wasm/wasm-module.h" #include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/zone/zone.h" #include "src/zone/zone.h"
#ifdef ENABLE_DISASSEMBLER #ifdef ENABLE_DISASSEMBLER
...@@ -13403,6 +13404,7 @@ int Script::GetEvalPosition() { ...@@ -13403,6 +13404,7 @@ int Script::GetEvalPosition() {
void Script::InitLineEnds(Handle<Script> script) { void Script::InitLineEnds(Handle<Script> script) {
Isolate* isolate = script->GetIsolate(); Isolate* isolate = script->GetIsolate();
if (!script->line_ends()->IsUndefined(isolate)) return; if (!script->line_ends()->IsUndefined(isolate)) return;
DCHECK_NE(Script::TYPE_WASM, script->type());
Object* src_obj = script->source(); Object* src_obj = script->source();
if (!src_obj->IsString()) { if (!src_obj->IsString()) {
...@@ -13420,6 +13422,16 @@ void Script::InitLineEnds(Handle<Script> script) { ...@@ -13420,6 +13422,16 @@ void Script::InitLineEnds(Handle<Script> script) {
bool Script::GetPositionInfo(Handle<Script> script, int position, bool Script::GetPositionInfo(Handle<Script> script, int position,
PositionInfo* info, OffsetFlag offset_flag) { 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); InitLineEnds(script);
return script->GetPositionInfo(position, info, offset_flag); return script->GetPositionInfo(position, info, offset_flag);
} }
......
...@@ -7084,13 +7084,9 @@ class Script: public Struct { ...@@ -7084,13 +7084,9 @@ class Script: public Struct {
// [source_mapping_url]: sourceMappingURL magic comment // [source_mapping_url]: sourceMappingURL magic comment
DECL_ACCESSORS(source_mapping_url, Object) 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. // This must only be called if the type of this script is TYPE_WASM.
DECL_ACCESSORS(wasm_instance, JSObject) DECL_ACCESSORS(wasm_compiled_module, Object)
// [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)
// [compilation_type]: how the the script was compiled. Encoded in the // [compilation_type]: how the the script was compiled. Encoded in the
// 'flags' field. // 'flags' field.
......
...@@ -569,8 +569,20 @@ RUNTIME_FUNCTION(Runtime_GetFrameDetails) { ...@@ -569,8 +569,20 @@ RUNTIME_FUNCTION(Runtime_GetFrameDetails) {
details->set(kFrameDetailsLocalCountIndex, Smi::kZero); details->set(kFrameDetailsLocalCountIndex, Smi::kZero);
// Add the source position. // 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) { 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. // Add the constructor information.
...@@ -1897,36 +1909,5 @@ RUNTIME_FUNCTION(Runtime_DebugBreakInOptimizedCode) { ...@@ -1897,36 +1909,5 @@ RUNTIME_FUNCTION(Runtime_DebugBreakInOptimizedCode) {
return NULL; 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 internal
} // namespace v8 } // namespace v8
...@@ -199,9 +199,7 @@ namespace internal { ...@@ -199,9 +199,7 @@ namespace internal {
F(DebugNextMicrotaskId, 0, 1) \ F(DebugNextMicrotaskId, 0, 1) \
F(DebugAsyncTaskEvent, 3, 1) \ F(DebugAsyncTaskEvent, 3, 1) \
F(DebugIsActive, 0, 1) \ F(DebugIsActive, 0, 1) \
F(DebugBreakInOptimizedCode, 0, 1) \ F(DebugBreakInOptimizedCode, 0, 1)
F(GetWasmFunctionOffsetTable, 1, 1) \
F(DisassembleWasmFunction, 1, 1)
#define FOR_EACH_INTRINSIC_ERROR(F) F(ErrorToString, 1, 1) #define FOR_EACH_INTRINSIC_ERROR(F) F(ErrorToString, 1, 1)
......
...@@ -1163,9 +1163,8 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, ...@@ -1163,9 +1163,8 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone,
return decoder.DecodeSingleFunction(module_env, function); return decoder.DecodeSingleFunction(module_env, function);
} }
FunctionOffsetsResult DecodeWasmFunctionOffsets( FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
const byte* module_start, const byte* module_end, const byte* module_end) {
uint32_t num_imported_functions) {
// Find and decode the code section. // Find and decode the code section.
Vector<const byte> code_section = Vector<const byte> code_section =
FindSection(module_start, module_end, kCodeSectionCode); FindSection(module_start, module_end, kCodeSectionCode);
...@@ -1179,12 +1178,9 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets( ...@@ -1179,12 +1178,9 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
uint32_t functions_count = decoder.consume_u32v("functions count"); uint32_t functions_count = decoder.consume_u32v("functions count");
// Reserve space for the entries, taking care of invalid input. // Reserve space for the entries, taking care of invalid input.
if (functions_count < static_cast<unsigned>(code_section.length()) / 2) { 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); int section_offset = static_cast<int>(code_section.start() - module_start);
DCHECK_LE(0, section_offset); DCHECK_LE(0, section_offset);
for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) { for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
...@@ -1200,20 +1196,16 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets( ...@@ -1200,20 +1196,16 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
} }
AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start, AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start,
const byte* tables_end, const byte* tables_end) {
uint32_t num_imported_functions) {
AsmJsOffsets table; AsmJsOffsets table;
Decoder decoder(tables_start, tables_end); Decoder decoder(tables_start, tables_end);
uint32_t functions_count = decoder.consume_u32v("functions count"); uint32_t functions_count = decoder.consume_u32v("functions count");
// Reserve space for the entries, taking care of invalid input. // Reserve space for the entries, taking care of invalid input.
if (functions_count < static_cast<unsigned>(tables_end - tables_start)) { 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) { for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
uint32_t size = decoder.consume_u32v("table size"); uint32_t size = decoder.consume_u32v("table size");
if (size == 0) { if (size == 0) {
......
...@@ -44,9 +44,8 @@ V8_EXPORT_PRIVATE FunctionResult DecodeWasmFunction(Isolate* isolate, ...@@ -44,9 +44,8 @@ V8_EXPORT_PRIVATE FunctionResult DecodeWasmFunction(Isolate* isolate,
// Extracts the function offset table from the wasm module bytes. // Extracts the function offset table from the wasm module bytes.
// Returns a vector with <offset, length> entries, or failure if the wasm 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. // are detected as invalid. Note that this validation is not complete.
FunctionOffsetsResult DecodeWasmFunctionOffsets( FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
const byte* module_start, const byte* module_end, const byte* module_end);
uint32_t num_imported_functions);
V8_EXPORT_PRIVATE WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, V8_EXPORT_PRIVATE WasmInitExpr DecodeWasmInitExprForTesting(const byte* start,
const byte* end); const byte* end);
...@@ -57,8 +56,7 @@ V8_EXPORT_PRIVATE WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, ...@@ -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 // failure if the wasm bytes are detected as invalid. Note that this validation
// is not complete. // is not complete.
AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* module_start, AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* module_start,
const byte* module_end, const byte* module_end);
uint32_t num_imported_functions);
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
......
This diff is collapsed.
...@@ -51,6 +51,8 @@ MaybeHandle<String> ExtractStringFromModuleBytes( ...@@ -51,6 +51,8 @@ MaybeHandle<String> ExtractStringFromModuleBytes(
uint32_t offset, uint32_t size) { uint32_t offset, uint32_t size) {
// TODO(wasm): cache strings from modules if it's a performance win. // TODO(wasm): cache strings from modules if it's a performance win.
Handle<SeqOneByteString> module_bytes = compiled_module->module_bytes(); 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; Address raw = module_bytes->GetCharsAddress() + offset;
if (!unibrow::Utf8::Validate(reinterpret_cast<const byte*>(raw), size)) if (!unibrow::Utf8::Validate(reinterpret_cast<const byte*>(raw), size))
return {}; // UTF8 decoding error for name. return {}; // UTF8 decoding error for name.
...@@ -583,6 +585,18 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { ...@@ -583,6 +585,18 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
TRACE("}\n"); 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 } // namespace
const char* wasm::SectionName(WasmSectionCode code) { const char* wasm::SectionName(WasmSectionCode code) {
...@@ -664,10 +678,38 @@ Object* wasm::GetOwningWasmInstance(Code* code) { ...@@ -664,10 +678,38 @@ Object* wasm::GetOwningWasmInstance(Code* code) {
return cell->value(); return cell->value();
} }
int wasm::GetNumImportedFunctions(Handle<JSObject> object) { int wasm::GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
return static_cast<int>(Handle<WasmInstanceObject>::cast(object) int func_index) {
->module() return GetFunctionOffsetAndLength(compiled_module, func_index).first;
->num_imported_functions); }
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) WasmModule::WasmModule(Zone* owned, const byte* module_start)
...@@ -1800,29 +1842,16 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate, ...@@ -1800,29 +1842,16 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
return builder.Build(); 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<String> wasm::GetWasmFunctionName(Isolate* isolate,
Handle<Object> instance, Handle<Object> instance_or_undef,
uint32_t func_index) { uint32_t func_index) {
Handle<Object> name_or_null = if (!instance_or_undef->IsUndefined(isolate)) {
GetWasmFunctionNameOrNull(isolate, instance, func_index); Handle<WasmCompiledModule> compiled_module(
if (!name_or_null->IsNull(isolate)) { Handle<WasmInstanceObject>::cast(instance_or_undef)
return Handle<String>::cast(name_or_null); ->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>"); return isolate->factory()->NewStringFromStaticChars("<WASM UNNAMED>");
} }
...@@ -1835,17 +1864,21 @@ WasmCompiledModule* wasm::GetCompiledModule(Object* object) { ...@@ -1835,17 +1864,21 @@ WasmCompiledModule* wasm::GetCompiledModule(Object* object) {
return WasmInstanceObject::cast(object)->get_compiled_module(); return WasmInstanceObject::cast(object)->get_compiled_module();
} }
bool wasm::WasmIsAsmJs(Object* object, Isolate* isolate) { bool wasm::WasmIsAsmJs(Object* instance, Isolate* isolate) {
return IsWasmInstance(object) && if (instance->IsUndefined(isolate)) return false;
WasmInstanceObject::cast(object) DCHECK(IsWasmInstance(instance));
->get_compiled_module() WasmCompiledModule* compiled_module =
->has_asm_js_script(); 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) { Handle<Script> wasm::GetScript(Handle<JSObject> instance) {
return Handle<WasmInstanceObject>::cast(object) DCHECK(IsWasmInstance(*instance));
->get_compiled_module() WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
->asm_js_script(); DCHECK(compiled_module->has_script());
return compiled_module->script();
} }
int wasm::GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index, int wasm::GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
...@@ -1905,10 +1938,12 @@ MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes( ...@@ -1905,10 +1938,12 @@ MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
maybe_compiled_module.ToHandleChecked(); maybe_compiled_module.ToHandleChecked();
DCHECK_EQ(origin == kAsmJsOrigin, !asm_js_script.is_null()); 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()); DCHECK(!compiled_module->has_asm_js_offset_tables());
if (origin == kAsmJsOrigin) { 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 = size_t offset_tables_len =
asm_js_offset_tables_end - asm_js_offset_tables_start; asm_js_offset_tables_end - asm_js_offset_tables_start;
DCHECK_GE(static_cast<size_t>(kMaxInt), offset_tables_len); DCHECK_GE(static_cast<size_t>(kMaxInt), offset_tables_len);
...@@ -1917,6 +1952,37 @@ MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes( ...@@ -1917,6 +1952,37 @@ MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
memcpy(offset_tables->GetDataStartAddress(), asm_js_offset_tables_start, memcpy(offset_tables->GetDataStartAddress(), asm_js_offset_tables_start,
offset_tables_len); offset_tables_len);
compiled_module->set_asm_js_offset_tables(offset_tables); 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); return WasmModuleObject::New(isolate, compiled_module);
...@@ -2109,3 +2175,14 @@ void WasmCompiledModule::RecreateModuleWrapper(Isolate* isolate, ...@@ -2109,3 +2175,14 @@ void WasmCompiledModule::RecreateModuleWrapper(Isolate* isolate,
compiled_module->set_module_wrapper(module_wrapper); compiled_module->set_module_wrapper(module_wrapper);
DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module)); 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 {};
}
...@@ -353,18 +353,13 @@ std::ostream& operator<<(std::ostream& os, const WasmModule& module); ...@@ -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 WasmFunction& function);
std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name); std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
// Extract a function name from the given wasm object. // Extract a function name from the given wasm instance.
// Returns "<WASM UNNAMED>" if the function is unnamed or the name is not a // Returns "<WASM UNNAMED>" if no instance is passed, the function is unnamed or
// valid UTF-8 string. // the name is not a valid UTF-8 string.
Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> wasm, // TODO(5620): Refactor once we always get a wasm instance.
Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> instance,
uint32_t func_index); 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. // Return the binary source bytes of a wasm module.
Handle<SeqOneByteString> GetWasmBytes(Handle<JSObject> wasm); Handle<SeqOneByteString> GetWasmBytes(Handle<JSObject> wasm);
...@@ -395,8 +390,10 @@ WasmCompiledModule* GetCompiledModule(Object* wasm_instance); ...@@ -395,8 +390,10 @@ WasmCompiledModule* GetCompiledModule(Object* wasm_instance);
// Check whether the wasm module was generated from asm.js code. // Check whether the wasm module was generated from asm.js code.
bool WasmIsAsmJs(Object* instance, Isolate* isolate); bool WasmIsAsmJs(Object* instance, Isolate* isolate);
// Get the script for the asm.js origin of the wasm module. // Get the script of the wasm module. If the origin of the module is asm.js, the
Handle<Script> GetAsmWasmScript(Handle<JSObject> instance); // 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 // Get the asm.js source position for the given byte offset in the given
// function. // function.
...@@ -413,8 +410,14 @@ V8_EXPORT_PRIVATE bool ValidateModuleBytes(Isolate* isolate, const byte* start, ...@@ -413,8 +410,14 @@ V8_EXPORT_PRIVATE bool ValidateModuleBytes(Isolate* isolate, const byte* start,
ErrorThrower* thrower, ErrorThrower* thrower,
ModuleOrigin origin); ModuleOrigin origin);
// Get the number of imported functions for a WASM instance. // Get the offset of the code of a function within a module.
int GetNumImportedFunctions(Handle<JSObject> instance); 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. // Assumed to be called with a code object associated to a wasm module instance.
// Intended to be called from runtime functions. // Intended to be called from runtime functions.
......
...@@ -189,11 +189,13 @@ class WasmCompiledModule : public FixedArray { ...@@ -189,11 +189,13 @@ class WasmCompiledModule : public FixedArray {
#define CORE_WCM_PROPERTY_TABLE(MACRO) \ #define CORE_WCM_PROPERTY_TABLE(MACRO) \
MACRO(OBJECT, FixedArray, code_table) \ MACRO(OBJECT, FixedArray, code_table) \
MACRO(OBJECT, Foreign, module_wrapper) \ MACRO(OBJECT, Foreign, module_wrapper) \
/* For debugging: */ \
MACRO(OBJECT, SeqOneByteString, module_bytes) \ 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, function_tables) \
MACRO(OBJECT, FixedArray, empty_function_tables) \ MACRO(OBJECT, FixedArray, empty_function_tables) \
MACRO(OBJECT, ByteArray, asm_js_offset_tables) \
MACRO(OBJECT, JSArrayBuffer, memory) \ MACRO(OBJECT, JSArrayBuffer, memory) \
MACRO(SMALL_NUMBER, uint32_t, min_mem_pages) \ MACRO(SMALL_NUMBER, uint32_t, min_mem_pages) \
MACRO(SMALL_NUMBER, uint32_t, max_mem_pages) \ MACRO(SMALL_NUMBER, uint32_t, max_mem_pages) \
...@@ -251,6 +253,12 @@ class WasmCompiledModule : public FixedArray { ...@@ -251,6 +253,12 @@ class WasmCompiledModule : public FixedArray {
static void RecreateModuleWrapper(Isolate* isolate, static void RecreateModuleWrapper(Isolate* isolate,
Handle<FixedArray> compiled_module); 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: private:
void InitId(); void InitId();
......
...@@ -2,8 +2,8 @@ Running testFunction with generated WASM bytes... ...@@ -2,8 +2,8 @@ Running testFunction with generated WASM bytes...
Paused on 'debugger;' Paused on 'debugger;'
Number of frames: 5 Number of frames: 5
- [0] {"functionName":"call_debugger","function_lineNumber":1,"function_columnNumber":24,"lineNumber":2,"columnNumber":4} - [0] {"functionName":"call_debugger","function_lineNumber":1,"function_columnNumber":24,"lineNumber":2,"columnNumber":4}
- [1] {"functionName":"main","lineNumber":0,"columnNumber":1} - [1] {"functionName":"call_func","lineNumber":1,"columnNumber":1}
- [2] {"functionName":"main","lineNumber":0,"columnNumber":1} - [2] {"functionName":"main","lineNumber":2,"columnNumber":1}
- [3] {"functionName":"testFunction","function_lineNumber":0,"function_columnNumber":21,"lineNumber":14,"columnNumber":19} - [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} - [4] {"functionName":"","function_lineNumber":0,"function_columnNumber":0,"lineNumber":0,"columnNumber":0}
Getting v8-generated stack trace... Getting v8-generated stack trace...
...@@ -11,7 +11,7 @@ Result of evaluate (string): ...@@ -11,7 +11,7 @@ Result of evaluate (string):
Error: this is your stack trace: Error: this is your stack trace:
-- skipped -- -- skipped --
at call_debugger (<anonymous>:3:5) at call_debugger (<anonymous>:3:5)
at main (<WASM>[1]+1) at call_func (<WASM>[1]+1)
at main (<WASM>[2]+1) at main (<WASM>[2]+1)
at testFunction (<anonymous>:15:20) at testFunction (<anonymous>:15:20)
at <anonymous>:1:1 at <anonymous>:1:1
......
...@@ -11,7 +11,7 @@ var builder = new WasmModuleBuilder(); ...@@ -11,7 +11,7 @@ var builder = new WasmModuleBuilder();
var imported_idx = builder.addImport("func", kSig_v_v); 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]) .addBody([kExprCallFunction, imported_idx])
.index; .index;
......
// 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;
})();
...@@ -12,10 +12,14 @@ Debug = debug.Debug ...@@ -12,10 +12,14 @@ Debug = debug.Debug
var exception = null; var exception = null;
var break_count = 0; var break_count = 0;
const expected_num_frames = 5; const expected_frames = [
const expected_wasm_frames = [false, true, true, false, false]; // func-name; wasm?; pos; line; col
const expected_wasm_positions = [0, 1, 2, 0, 0]; ['call_debugger', false], // --
const expected_function_names = ["call_debugger", "wasm_2", "wasm_1", "testFrameInspection", ""]; ['wasm_2', true, 56, 2, 1], // --
['wasm_1', true, 52, 1, 2], // --
['testFrameInspection', false], // --
['', false]
];
function listener(event, exec_state, event_data, data) { function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return; if (event != Debug.DebugEvent.Break) return;
...@@ -23,20 +27,24 @@ function listener(event, exec_state, event_data, data) { ...@@ -23,20 +27,24 @@ function listener(event, exec_state, event_data, data) {
try { try {
var break_id = exec_state.break_id; var break_id = exec_state.break_id;
var frame_count = exec_state.frameCount(); 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) { for (var i = 0; i < frame_count; ++i) {
var frame = exec_state.frame(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. // wasm frames have unresolved function, others resolved ones.
assertEquals(expected_wasm_frames[i], !frame.func().resolved()); assertEquals(
assertEquals(expected_function_names[i], frame.func().name()); expected_frames[i][1], !frame.func().resolved(), 'resolved at ' + i);
if (expected_wasm_frames[i]) { if (expected_frames[i][1]) { // wasm frame?
var script = frame.details().script(); var script = frame.details().script();
assertNotNull(script); assertNotNull(script, 'script at ' + i);
assertEquals(expected_wasm_positions[i], frame.details().sourcePosition()); assertEquals(
expected_frames[i][2], frame.details().sourcePosition(),
'source pos at ' + i);
var loc = script.locationFromPosition(frame.details().sourcePosition()); var loc = script.locationFromPosition(frame.details().sourcePosition());
assertEquals(expected_wasm_positions[i], loc.column); assertEquals(expected_frames[i][2], loc.position, 'pos at ' + i);
assertEquals(expected_wasm_positions[i], loc.position); assertEquals(expected_frames[i][3], loc.line, 'line at ' + i);
assertEquals(expected_frames[i][4], loc.column, 'column at ' + i);
} }
} }
} catch (e) { } catch (e) {
...@@ -49,14 +57,13 @@ var builder = new WasmModuleBuilder(); ...@@ -49,14 +57,13 @@ var builder = new WasmModuleBuilder();
// wasm_1 calls wasm_2 on offset 2. // wasm_1 calls wasm_2 on offset 2.
// wasm_2 calls call_debugger on offset 1. // 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) builder.addFunction('wasm_1', kSig_v_v)
.addBody([kExprNop, kExprCallFunction, 2]) .addBody([kExprNop, kExprCallFunction, 2])
.exportAs("main"); .exportAs('main');
builder.addFunction("wasm_2", kSig_v_v) builder.addFunction('wasm_2', kSig_v_v).addBody([kExprCallFunction, 0]);
.addBody([kExprCallFunction, 0]);
function call_debugger() { function call_debugger() {
debugger; debugger;
......
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