Commit 07752032 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] Decode local names for debugging

When providing scope information (containing the value of local
variables of live stack frames), decode the local variable names of all
functions in a wasm module and store this in the WasmDebugInfo
structure.
Use these names to actually name the reported locals, instead of using
the default names "param#<d>" and "local#<d>". These names are only used
as fallbacks for locals which were not assigned a name.

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

Change-Id: Ibf7d30e392248ef5590177cd8b6329239b45e018
Reviewed-on: https://chromium-review.googlesource.com/548495
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46379}
parent eaf850e2
......@@ -1404,6 +1404,60 @@ std::vector<CustomSectionOffset> DecodeCustomSections(const byte* start,
return result;
}
void DecodeLocalNames(const byte* module_start, const byte* module_end,
LocalNames* result) {
DCHECK_NOT_NULL(result);
DCHECK(result->names.empty());
static constexpr int kModuleHeaderSize = 8;
Decoder decoder(module_start, module_end);
decoder.consume_bytes(kModuleHeaderSize, "module header");
WasmSectionIterator section_iter(decoder);
while (decoder.ok() && section_iter.more() &&
section_iter.section_code() != kNameSectionCode) {
section_iter.advance(true);
}
if (!section_iter.more()) return;
// Reset the decoder to not read beyond the name section end.
decoder.Reset(section_iter.payload(), decoder.pc_offset());
while (decoder.ok() && decoder.more()) {
uint8_t name_type = decoder.consume_u8("name type");
if (name_type & 0x80) break; // no varuint7
uint32_t name_payload_len = decoder.consume_u32v("name payload length");
if (!decoder.checkAvailable(name_payload_len)) break;
if (name_type != NameSectionType::kLocal) {
decoder.consume_bytes(name_payload_len, "name subsection payload");
continue;
}
uint32_t local_names_count = decoder.consume_u32v("local names count");
for (uint32_t i = 0; i < local_names_count; ++i) {
uint32_t func_index = decoder.consume_u32v("function index");
if (func_index > kMaxInt) continue;
result->names.emplace_back(static_cast<int>(func_index));
LocalNamesPerFunction& func_names = result->names.back();
result->max_function_index =
std::max(result->max_function_index, func_names.function_index);
uint32_t num_names = decoder.consume_u32v("namings count");
for (uint32_t k = 0; k < num_names; ++k) {
uint32_t local_index = decoder.consume_u32v("local index");
WireBytesRef name = wasm::consume_string(decoder, true, "local name");
if (!decoder.ok()) break;
if (local_index > kMaxInt) continue;
func_names.max_local_index =
std::max(func_names.max_local_index, static_cast<int>(local_index));
func_names.names.emplace_back(static_cast<int>(local_index), name);
}
}
}
}
} // namespace wasm
} // namespace internal
} // namespace v8
......@@ -51,6 +51,7 @@ typedef Result<std::unique_ptr<WasmModule>> ModuleResult;
typedef Result<std::unique_ptr<WasmFunction>> FunctionResult;
typedef std::vector<std::pair<int, int>> FunctionOffsets;
typedef Result<FunctionOffsets> FunctionOffsetsResult;
struct AsmJsOffsetEntry {
int byte_offset;
int source_position_call;
......@@ -59,6 +60,24 @@ struct AsmJsOffsetEntry {
typedef std::vector<std::vector<AsmJsOffsetEntry>> AsmJsOffsets;
typedef Result<AsmJsOffsets> AsmJsOffsetsResult;
struct LocalName {
int local_index;
WireBytesRef name;
LocalName(int local_index, WireBytesRef name)
: local_index(local_index), name(name) {}
};
struct LocalNamesPerFunction {
int function_index;
int max_local_index = -1;
std::vector<LocalName> names;
explicit LocalNamesPerFunction(int function_index)
: function_index(function_index) {}
};
struct LocalNames {
int max_function_index = -1;
std::vector<LocalNamesPerFunction> names;
};
// Decodes the bytes of a wasm module between {module_start} and {module_end}.
V8_EXPORT_PRIVATE ModuleResult SyncDecodeWasmModule(Isolate* isolate,
const byte* module_start,
......@@ -108,6 +127,13 @@ V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections(
AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* module_start,
const byte* module_end);
// Decode the local names assignment from the name section.
// Stores the result in the given {LocalNames} structure. The result will be
// empty if no name section is present. On encountering an error in the name
// section, returns all information decoded up to the first error.
void DecodeLocalNames(const byte* module_start, const byte* module_end,
LocalNames* result);
} // namespace wasm
} // namespace internal
} // namespace v8
......
......@@ -61,6 +61,34 @@ Handle<Object> WasmValToValueObject(Isolate* isolate, WasmVal value) {
}
}
MaybeHandle<String> GetLocalName(Isolate* isolate,
Handle<WasmDebugInfo> debug_info,
int func_index, int local_index) {
DCHECK_LE(0, func_index);
DCHECK_LE(0, local_index);
if (!debug_info->has_locals_names()) {
Handle<WasmCompiledModule> compiled_module(
debug_info->wasm_instance()->compiled_module(), isolate);
Handle<FixedArray> locals_names =
wasm::DecodeLocalNames(isolate, compiled_module);
debug_info->set_locals_names(*locals_names);
}
Handle<FixedArray> locals_names(debug_info->locals_names(), isolate);
if (func_index >= locals_names->length() ||
locals_names->get(func_index)->IsUndefined(isolate)) {
return {};
}
Handle<FixedArray> func_locals_names(
FixedArray::cast(locals_names->get(func_index)), isolate);
if (local_index >= func_locals_names->length() ||
func_locals_names->get(local_index)->IsUndefined(isolate)) {
return {};
}
return handle(String::cast(func_locals_names->get(local_index)));
}
// Forward declaration.
class InterpreterHandle;
InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info);
......@@ -410,8 +438,10 @@ class InterpreterHandle {
}
Handle<JSArray> GetScopeDetails(Address frame_pointer, int frame_index,
Handle<WasmInstanceObject> instance) {
Handle<WasmDebugInfo> debug_info) {
auto frame = GetInterpretedFrame(frame_pointer, frame_index);
Isolate* isolate = debug_info->GetIsolate();
Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
Handle<FixedArray> global_scope =
isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize);
......@@ -434,7 +464,7 @@ class InterpreterHandle {
kExternalUint8Array, memory_buffer, 0, byte_length);
JSObject::SetOwnPropertyIgnoreAttributes(global_scope_object, name,
uint8_array, NONE)
.Check();
.Assert();
}
Handle<FixedArray> local_scope =
......@@ -450,15 +480,30 @@ class InterpreterHandle {
int num_params = frame->GetParameterCount();
int num_locals = frame->GetLocalCount();
DCHECK_LE(num_params, num_locals);
for (int i = 0; i < num_locals; ++i) {
// TODO(clemensh): Use names from name section if present.
const char* label = i < num_params ? "param#%d" : "local#%d";
Handle<String> name = PrintFToOneByteString<true>(isolate_, label, i);
WasmVal value = frame->GetLocalValue(i);
Handle<Object> value_obj = WasmValToValueObject(isolate_, value);
JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, name,
value_obj, NONE)
.Check();
if (num_locals > 0) {
Handle<JSObject> locals_obj =
isolate_->factory()->NewJSObjectWithNullProto();
Handle<String> locals_name =
isolate_->factory()->InternalizeOneByteString(
STATIC_CHAR_VECTOR("locals"));
JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, locals_name,
locals_obj, NONE)
.Assert();
for (int i = 0; i < num_locals; ++i) {
MaybeHandle<String> name =
GetLocalName(isolate, debug_info, frame->function()->func_index, i);
if (name.is_null()) {
// Parameters should come before locals in alphabetical ordering, so
// we name them "args" here.
const char* label = i < num_params ? "arg#%d" : "local#%d";
name = PrintFToOneByteString<true>(isolate_, label, i);
}
WasmVal value = frame->GetLocalValue(i);
Handle<Object> value_obj = WasmValToValueObject(isolate_, value);
JSObject::SetOwnPropertyIgnoreAttributes(
locals_obj, name.ToHandleChecked(), value_obj, NONE)
.Assert();
}
}
// Fill stack values.
......@@ -468,18 +513,18 @@ class InterpreterHandle {
// which does not make too much sense here.
Handle<JSObject> stack_obj =
isolate_->factory()->NewJSObjectWithNullProto();
Handle<String> stack_name = isolate_->factory()->InternalizeOneByteString(
STATIC_CHAR_VECTOR("stack"));
JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, stack_name,
stack_obj, NONE)
.Assert();
for (int i = 0; i < stack_count; ++i) {
WasmVal value = frame->GetStackValue(i);
Handle<Object> value_obj = WasmValToValueObject(isolate_, value);
JSObject::SetOwnElementIgnoreAttributes(
stack_obj, static_cast<uint32_t>(i), value_obj, NONE)
.Check();
.Assert();
}
Handle<String> stack_name = isolate_->factory()->InternalizeOneByteString(
STATIC_CHAR_VECTOR("stack"));
JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, stack_name,
stack_obj, NONE)
.Check();
Handle<JSArray> global_jsarr =
isolate_->factory()->NewJSArrayWithElements(global_scope);
......@@ -695,6 +740,5 @@ Handle<JSArray> WasmDebugInfo::GetScopeDetails(Handle<WasmDebugInfo> debug_info,
Address frame_pointer,
int frame_index) {
InterpreterHandle* interp_handle = GetInterpreterHandle(*debug_info);
Handle<WasmInstanceObject> instance(debug_info->wasm_instance());
return interp_handle->GetScopeDetails(frame_pointer, frame_index, instance);
return interp_handle->GetScopeDetails(frame_pointer, frame_index, debug_info);
}
......@@ -742,6 +742,33 @@ Handle<JSArray> wasm::GetCustomSections(Isolate* isolate,
return array_object;
}
Handle<FixedArray> wasm::DecodeLocalNames(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
Handle<SeqOneByteString> wire_bytes(compiled_module->module_bytes(), isolate);
LocalNames decoded_locals;
{
DisallowHeapAllocation no_gc;
wasm::DecodeLocalNames(wire_bytes->GetChars(),
wire_bytes->GetChars() + wire_bytes->length(),
&decoded_locals);
}
Handle<FixedArray> locals_names =
isolate->factory()->NewFixedArray(decoded_locals.max_function_index + 1);
for (LocalNamesPerFunction& func : decoded_locals.names) {
Handle<FixedArray> func_locals_names =
isolate->factory()->NewFixedArray(func.max_local_index + 1);
locals_names->set(func.function_index, *func_locals_names);
for (LocalName& name : func.names) {
Handle<String> name_str =
WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
isolate, compiled_module, name.name)
.ToHandleChecked();
func_locals_names->set(name.local_index, *name_str);
}
}
return locals_names;
}
bool wasm::SyncValidate(Isolate* isolate, const ModuleWireBytes& bytes) {
if (bytes.start() == nullptr || bytes.length() == 0) return false;
ModuleResult result = SyncDecodeWasmModule(isolate, bytes.start(),
......
......@@ -412,6 +412,11 @@ V8_EXPORT_PRIVATE Handle<JSArray> GetCustomSections(
Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name,
ErrorThrower* thrower);
// Decode local variable names from the names section. Return FixedArray of
// FixedArray of <undefined|String>. The outer fixed array is indexed by the
// function index, the inner one by the local index.
Handle<FixedArray> DecodeLocalNames(Isolate*, Handle<WasmCompiledModule>);
// Assumed to be called with a code object associated to a wasm module instance.
// Intended to be called from runtime functions.
// Returns nullptr on failing to get owning instance.
......
......@@ -1597,3 +1597,6 @@ bool WasmInstanceWrapper::IsWasmInstanceWrapper(Object* obj) {
return false;
return true;
}
DEFINE_OPTIONAL_ARR_ACCESSORS(WasmDebugInfo, locals_names, kLocalsNames,
FixedArray)
......@@ -550,9 +550,13 @@ class WasmDebugInfo : public FixedArray {
kInstance,
kInterpreterHandle,
kInterpretedFunctions,
// FixedArray of FixedArray of <undefined|String>.
kLocalsNames,
kFieldCount
};
DECLARE_OPTIONAL_ACCESSORS(locals_names, FixedArray);
static Handle<WasmDebugInfo> New(Handle<WasmInstanceObject>);
// Setup a WasmDebugInfo with an existing WasmInstance struct.
......
Test retrieving scope information when pausing in wasm functions
Installing code and global variable.
Calling instantiate function.
Waiting for wasm script to be parsed.
Got wasm script!
Setting breakpoint on line 2 (first instruction)
{
columnNumber : 2
lineNumber : 2
scriptId : <scriptId>
}
Paused:
(local i32 f64)
#i32.const 11
set_local 0
at func (2:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 4 (number), "local#1": 0 (number), "unicode☼f64": 0 (number)
stack:
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
i32.const 11
#set_local 0
i32.const 47
at func (3:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 4 (number), "local#1": 0 (number), "unicode☼f64": 0 (number)
stack: "0": 11 (number)
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
set_local 0
#i32.const 47
set_local 1
at func (4:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 11 (number), "local#1": 0 (number), "unicode☼f64": 0 (number)
stack:
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
i32.const 47
#set_local 1
i32.const 1
at func (5:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 11 (number), "local#1": 0 (number), "unicode☼f64": 0 (number)
stack: "0": 47 (number)
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
set_local 1
#i32.const 1
f64.convert_u/i32
at func (6:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 11 (number), "local#1": 47 (number), "unicode☼f64": 0 (number)
stack:
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
i32.const 1
#f64.convert_u/i32
i32.const 7
at func (7:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 11 (number), "local#1": 47 (number), "unicode☼f64": 0 (number)
stack: "0": 1 (number)
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
f64.convert_u/i32
#i32.const 7
f64.convert_u/i32
at func (8:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 11 (number), "local#1": 47 (number), "unicode☼f64": 0 (number)
stack: "0": 1 (number)
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
i32.const 7
#f64.convert_u/i32
f64.div
at func (9:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 11 (number), "local#1": 47 (number), "unicode☼f64": 0 (number)
stack: "0": 1 (number), "1": 7 (number)
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
f64.convert_u/i32
#f64.div
set_local 2
at func (10:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 11 (number), "local#1": 47 (number), "unicode☼f64": 0 (number)
stack: "0": 1 (number), "1": 7 (number)
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
f64.div
#set_local 2
end
at func (11:2):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 11 (number), "local#1": 47 (number), "unicode☼f64": 0 (number)
stack: "0": 0.14285714285714285 (number)
at (anonymous) (0:17):
- scope (global):
-- skipped
Paused:
set_local 2
#end
at func (12:0):
- scope (global):
-- skipped
- scope (local):
locals: "i32Arg": 11 (number), "local#1": 47 (number), "unicode☼f64": 0.14285714285714285 (number)
stack:
at (anonymous) (0:17):
- scope (global):
-- skipped
exports.main returned. Test finished.
// Copyright 2017 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.
let {session, contextGroup, Protocol} = InspectorTest.start(
'Test retrieving scope information when pausing in wasm functions');
session.setupScriptMap();
Protocol.Debugger.enable();
let evaluate = code => Protocol.Runtime.evaluate({expression: code});
(async function test() {
let scriptId = await instantiateWasm();
await setBreakpoint(scriptId);
printPauseLocationsAndContinue();
await evaluate('instance.exports.main(4)');
InspectorTest.log('exports.main returned. Test finished.');
InspectorTest.completeTest();
})();
async function printPauseLocationsAndContinue() {
while (true) {
let msg = await Protocol.Debugger.oncePaused();
let loc = msg.params.callFrames[0].location;
InspectorTest.log('Paused:');
await session.logSourceLocation(loc);
await dumpScopeChainsOnPause(msg);
Protocol.Debugger.stepOver();
}
}
async function instantiateWasm() {
utils.load('test/mjsunit/wasm/wasm-constants.js');
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
var builder = new WasmModuleBuilder();
builder.addFunction('func', kSig_v_i)
.addLocals(
{i32_count: 1, f64_count: 1}, ['i32Arg', undefined, 'unicode☼f64'])
.addBody([
// Set param 0 to 11.
kExprI32Const, 11, kExprSetLocal, 0,
// Set local 1 to 47.
kExprI32Const, 47, kExprSetLocal, 1,
// Set local 2 to 1/7.
kExprI32Const, 1, kExprF64UConvertI32, kExprI32Const, 7,
kExprF64UConvertI32, kExprF64Div, kExprSetLocal, 2
])
.exportAs('main');
var module_bytes = builder.toArray();
function instantiate(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);
}
InspectorTest.log('Installing code and global variable.');
await evaluate('var instance;\n' + instantiate.toString());
InspectorTest.log('Calling instantiate function.');
evaluate('instantiate(' + JSON.stringify(module_bytes) + ')');
return waitForWasmScript();
}
async function setBreakpoint(scriptId) {
InspectorTest.log('Setting breakpoint on line 2 (first instruction)');
let breakpoint = await Protocol.Debugger.setBreakpoint(
{'location': {'scriptId': scriptId, 'lineNumber': 2}});
printFailure(breakpoint);
InspectorTest.logMessage(breakpoint.result.actualLocation);
}
function printFailure(message) {
if (!message.result) {
InspectorTest.logMessage(message);
}
return message;
}
async function waitForWasmScript() {
InspectorTest.log('Waiting for wasm script to be parsed.');
while (true) {
let script_msg = await Protocol.Debugger.onceScriptParsed();
let url = script_msg.params.url;
if (!url.startsWith('wasm://')) {
continue;
}
InspectorTest.log('Got wasm script!');
return script_msg.params.scriptId;
}
}
async function getValueString(value) {
if (value.type == 'object') {
var msg = await Protocol.Runtime.getProperties({objectId: value.objectId});
printFailure(msg);
let printProperty = elem => '"' + elem.name + '"' +
': ' + elem.value.description + ' (' + elem.value.type + ')';
return msg.result.result.map(printProperty).join(', ');
}
return JSON.stringify(value.value) + ' (' + value.type + ')';
}
async function dumpProperties(message) {
printFailure(message);
for (var value of message.result.result) {
var value_str = await getValueString(value.value);
InspectorTest.log(' ' + value.name + ': ' + value_str);
}
}
async function dumpScopeChainsOnPause(message) {
for (var frame of message.params.callFrames) {
var functionName = frame.functionName || '(anonymous)';
var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber;
var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber;
InspectorTest.log(`at ${functionName} (${lineNumber}:${columnNumber}):`);
for (var scope of frame.scopeChain) {
InspectorTest.logObject(' - scope (' + scope.type + '):');
if (scope.type == 'global') {
InspectorTest.logObject(' -- skipped');
} else {
var properties = await Protocol.Runtime.getProperties(
{'objectId': scope.object.objectId});
await dumpProperties(properties);
}
}
}
}
......@@ -37,7 +37,7 @@ at wasm_B (7:6):
- scope (global):
-- skipped
- scope (local):
param#0: 4 (number)
locals: {"arg#0":4} (Object)
stack: {"0":3} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -48,7 +48,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 3 (number)
locals: {"arg#0":3} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -64,7 +64,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 3 (number)
locals: {"arg#0":3} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -80,7 +80,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 3 (number)
locals: {"arg#0":3} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -91,7 +91,7 @@ at wasm_B (9:6):
- scope (global):
-- skipped
- scope (local):
param#0: 3 (number)
locals: {"arg#0":3} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -102,7 +102,7 @@ at wasm_B (7:6):
- scope (global):
-- skipped
- scope (local):
param#0: 3 (number)
locals: {"arg#0":3} (Object)
stack: {"0":2} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -113,7 +113,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 2 (number)
locals: {"arg#0":2} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -124,7 +124,7 @@ at wasm_B (9:6):
- scope (global):
-- skipped
- scope (local):
param#0: 2 (number)
locals: {"arg#0":2} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -135,7 +135,7 @@ at wasm_B (7:6):
- scope (global):
-- skipped
- scope (local):
param#0: 2 (number)
locals: {"arg#0":2} (Object)
stack: {"0":1} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -146,7 +146,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -162,7 +162,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -173,7 +173,7 @@ at wasm_B (9:6):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -184,7 +184,7 @@ at wasm_B (1:2):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -195,7 +195,7 @@ at wasm_B (2:4):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -206,7 +206,7 @@ at wasm_B (3:4):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {"0":1} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -217,7 +217,7 @@ at wasm_B (4:6):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -228,7 +228,7 @@ at wasm_B (5:6):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {"0":1} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -239,7 +239,7 @@ at wasm_B (6:6):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {"0":1,"1":1} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -250,7 +250,7 @@ at wasm_B (7:6):
- scope (global):
-- skipped
- scope (local):
param#0: 1 (number)
locals: {"arg#0":1} (Object)
stack: {"0":0} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -261,7 +261,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 0 (number)
locals: {"arg#0":0} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -277,7 +277,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 0 (number)
locals: {"arg#0":0} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -293,7 +293,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 0 (number)
locals: {"arg#0":0} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -309,7 +309,7 @@ at wasm_B (8:6):
- scope (global):
-- skipped
- scope (local):
param#0: 0 (number)
locals: {"arg#0":0} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......@@ -320,7 +320,7 @@ at wasm_B (9:6):
- scope (global):
-- skipped
- scope (local):
param#0: 0 (number)
locals: {"arg#0":0} (Object)
stack: {} (Object)
at (anonymous) (0:17):
- scope (global):
......
......@@ -86,6 +86,15 @@ class WasmFunctionBuilder {
this.body = [];
}
numLocalNames() {
if (this.local_names === undefined) return 0;
let num_local_names = 0;
for (let loc_name of this.local_names) {
if (loc_name !== undefined) ++num_local_names;
}
return num_local_names;
}
exportAs(name) {
this.module.addExport(name, this.index);
return this;
......@@ -112,8 +121,9 @@ class WasmFunctionBuilder {
return this;
}
addLocals(locals) {
addLocals(locals, names) {
this.locals = locals;
this.local_names = names;
return this;
}
......@@ -341,16 +351,11 @@ class WasmModuleBuilder {
}
// Add functions declarations
let num_function_names = 0;
let names = false;
if (wasm.functions.length > 0) {
if (debug) print("emitting function decls @ " + binary.length);
binary.emit_section(kFunctionSectionCode, section => {
section.emit_u32v(wasm.functions.length);
for (let func of wasm.functions) {
if (func.name !== undefined) {
++num_function_names;
}
section.emit_u32v(func.type_index);
}
});
......@@ -551,15 +556,24 @@ class WasmModuleBuilder {
}
// Add names.
if (num_function_names > 0 || wasm.name !== undefined) {
let num_function_names = 0;
let num_functions_with_local_names = 0;
for (let func of wasm.functions) {
if (func.name !== undefined) ++num_function_names;
if (func.numLocalNames() > 0) ++num_functions_with_local_names;
}
if (num_function_names > 0 || num_functions_with_local_names > 0 ||
wasm.name !== undefined) {
if (debug) print('emitting names @ ' + binary.length);
binary.emit_section(kUnknownSectionCode, section => {
section.emit_string('name');
// Emit module name.
if (wasm.name !== undefined) {
section.emit_section(kModuleNameCode, name_section => {
name_section.emit_string(wasm.name);
});
}
// Emit function names.
if (num_function_names > 0) {
section.emit_section(kFunctionNamesCode, name_section => {
name_section.emit_u32v(num_function_names);
......@@ -570,6 +584,22 @@ class WasmModuleBuilder {
}
});
}
// Emit local names.
if (num_functions_with_local_names > 0) {
section.emit_section(kLocalNamesCode, name_section => {
name_section.emit_u32v(num_functions_with_local_names);
for (let func of wasm.functions) {
if (func.numLocalNames() == 0) continue;
name_section.emit_u32v(func.index);
name_section.emit_u32v(func.numLocalNames());
for (let i = 0; i < func.local_names.length; ++i) {
if (func.local_names[i] === undefined) continue;
name_section.emit_u32v(i);
name_section.emit_string(func.local_names[i]);
}
}
});
}
});
}
......
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