[wasm] test disassembler for custom name section

This adds a separate test module and output expectation for names in
the custom "name" section in Wasm binaries.

Also fixes a small inconsistency: The index of both data and element
segments is now printed as a comment, in case one prints their name
(as we do for functions, globals, etc.)

Data segment names are currently not printed for consistency with
legacy behavior.

Change-Id: I8900f54b02e031a811dfd9be8326d6f63d26d4a0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4004717
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Daniel Lehmann <dlehmann@chromium.org>
Auto-Submit: Daniel Lehmann <dlehmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84093}
This commit is contained in:
Daniel Lehmann 2022-11-07 14:06:07 +00:00 committed by V8 LUCI CQ
parent c5b52e798a
commit 6d551ef34e
7 changed files with 300 additions and 31 deletions

View File

@ -319,26 +319,30 @@ void NamesProvider::PrintGlobalName(StringBuilder& out, uint32_t global_index,
}
void NamesProvider::PrintElementSegmentName(StringBuilder& out,
uint32_t element_segment_index) {
uint32_t element_segment_index,
IndexAsComment index_as_comment) {
DecodeNamesIfNotYetDone();
WireBytesRef ref =
Get(name_section_names_->element_segment_names_, element_segment_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
MaybeAddComment(out, element_segment_index, index_as_comment);
} else {
out << "$elem" << element_segment_index;
}
}
void NamesProvider::PrintDataSegmentName(StringBuilder& out,
uint32_t data_segment_index) {
uint32_t data_segment_index,
IndexAsComment index_as_comment) {
DecodeNamesIfNotYetDone();
WireBytesRef ref =
Get(name_section_names_->data_segment_names_, data_segment_index);
if (ref.is_set()) {
out << '$';
WriteRef(out, ref);
MaybeAddComment(out, data_segment_index, index_as_comment);
} else {
out << "$data" << data_segment_index;
}

View File

@ -56,9 +56,11 @@ class V8_EXPORT_PRIVATE NamesProvider {
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintGlobalName(StringBuilder& out, uint32_t global_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintElementSegmentName(StringBuilder& out,
uint32_t element_segment_index);
void PrintDataSegmentName(StringBuilder& out, uint32_t data_segment_index);
void PrintElementSegmentName(
StringBuilder& out, uint32_t element_segment_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintDataSegmentName(StringBuilder& out, uint32_t data_segment_index,
IndexAsComment index_as_comment = kDontPrintIndex);
void PrintFieldName(StringBuilder& out, uint32_t struct_index,
uint32_t field_index,
IndexAsComment index_as_comment = kDontPrintIndex);

View File

@ -905,7 +905,7 @@ void ModuleDisassembler::PrintModule(Indentation indentation) {
const WasmElemSegment& elem = module_->elem_segments[i];
out_.NextLine(offsets_->element_offset(i));
out_ << indentation << "(elem ";
names_->PrintElementSegmentName(out_, i);
names_->PrintElementSegmentName(out_, i, kIndicesAsComments);
if (elem.status == WasmElemSegment::kStatusDeclarative) {
out_ << " declare";
} else if (elem.status == WasmElemSegment::kStatusActive) {
@ -965,7 +965,7 @@ void ModuleDisassembler::PrintModule(Indentation indentation) {
out_ << indentation << "(data";
if (!kSkipDataSegmentNames) {
out_ << " ";
names_->PrintDataSegmentName(out_, i);
names_->PrintDataSegmentName(out_, i, kIndicesAsComments);
}
if (data.active) {
ValueType type = module_->is_memory64 ? kWasmI64 : kWasmI32;

View File

@ -569,6 +569,8 @@ v8_source_set("unittests_sources") {
"wasm/wasm-compiler-unittest.cc",
"wasm/wasm-disassembler-unittest-mvp.wasm.inc",
"wasm/wasm-disassembler-unittest-mvp.wat.inc",
"wasm/wasm-disassembler-unittest-names.wasm.inc",
"wasm/wasm-disassembler-unittest-names.wat.inc",
"wasm/wasm-disassembler-unittest.cc",
"wasm/wasm-macro-gen-unittest.cc",
"wasm/wasm-module-builder-unittest.cc",

View File

@ -0,0 +1,201 @@
0x00, 0x61, 0x73, 0x6d, // wasm magic
0x01, 0x00, 0x00, 0x00, // wasm version
0x01, // section kind: Type
0x0a, // section length 10
0x02, // types count 2
0x60, // kind: func
0x00, // param count 0
0x00, // return count 0
0x60, // kind: func
0x03, // param count 3
0x7f, 0x7f, 0x7e, // i32 i32 i64
0x00, // return count 0
0x02, // section kind: Import
0x30, // section length 48
0x02, // imports count 2
// import #0
0x03, // module name length: 3
0x65, 0x6e, 0x76, // module name: env
0x0f, // field name length: 15
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
// field name: imported_global
0x03, 0x7f, 0x00, // kind: global i32 immutable
// import #1
0x03, // module name length: 3
0x65, 0x6e, 0x76, // module name: env
0x11, // field name length: 17
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, // field name: imported_function
0x00, 0x00, // kind: function
0x03, // section kind: Function
0x03, // section length 3
0x02, // functions count 2
0x01, // 1 $function_with_name (param i32 i32 i64)
0x00, // 2 $exported_function_with_name
0x04, // section kind: Table
0x04, // section length 4
0x01, 0x70, 0x00, // table count 1: funcref no maximum
0x00, // initial size 0
0x05, // section kind: Memory
0x03, // section length 3
0x01, 0x00, // memory count 1: no maximum
0x00, // initial size 0
0x06, // section kind: Global
0x0b, // section length 11
0x02, // globals count 2
0x7f, 0x00, // global #1: i32 immutable
0x41, 0x00, 0x0b, // i32.const 0
0x7f, 0x00, // global #2: i32 immutable
0x41, 0x00, 0x0b, // i32.const 0
0x07, // section kind: Export
0x27, // section length 39
0x02, // exports count 2
// export # 0
0x0f, // field name length: 15
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
// field name: exported_global
0x03, 0x02, // kind: global index: 2
// export # 1
0x11, // field name length: 17
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, // field name: exported_function
0x00, 0x02, // kind: function index: 2
0x09, // section kind: Element
0x05, // section length 5
0x01, 0x01, 0x00, // segment count 1: flag: passive, element type: function
0x01, 0x01, // number of elements 1: index: 1
0x0a, // section kind: Code
0x1d, // section length 29
0x02, // functions count 2
// function #1 $function_with_name
0x0b, // body size 11
0x00, // 0 entries in locals list
0x20, 0x00, // local.get $param_with_name_1
0x1a, // drop
0x20, 0x01, // local.get $param_with_name_2
0x1a, // drop
0x20, 0x02, // local.get $param_with_name_3
0x1a, // drop
0x0b, // end
// function #2 $exported_function_with_name
0x0f, // body size 15
0x02, // 2 entries in locals list
0x02, 0x7f, // 2 locals of type i32
0x01, 0x7e, // 1 local of type i64
0x20, 0x00, // local.get $local_with_name_1
0x1a, // drop
0x20, 0x01, // local.get $local_with_name_2
0x1a, // drop
0x20, 0x02, // local.get $local_with_name_3
0x1a, // drop
0x0b, // end
0x0b, // section kind: Data
0x0b, // section length 11
0x01, 0x00, // data segments count 1: flag: active no index
0x41, 0x00, 0x0b, // i32.const 0
0x05, // source size 5
0x66, 0x6f, 0x6f, 0x0a, 0x00, // segment data
0x00, // section kind: Unknown
0xd8, 0x02, // section length 344
0x04, // section name length: 4
0x6e, 0x61, 0x6d, 0x65, // section name: name
0x01, // name type: function
0x4f, // payload length: 79
0x03, // names count 3
0x00, 0x1b, // index 0 name length: 27
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e,
0x61, 0x6d, 0x65, // name: imported_function_with_name
0x01, 0x12, // index 1 name length: 18
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61,
0x6d, 0x65, // name: function_with_name
0x02, 0x1b, // index 2 name length: 27
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e,
0x61, 0x6d, 0x65, // name: exported_function_with_name
0x02, // name type: local
0x79, // payload length: 121
0x03, // outer count 3
0x00, 0x00, // outer index 0 inner count 0
0x01, 0x03, // outer index 1 inner count 3
0x00, 0x11, // inner index 0 name length: 17
0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x31, // name: param_with_name_1
0x01, 0x11, // inner index 1 name length: 17
0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x32, // name: param_with_name_2
0x02, 0x11, // inner index 2 name length: 17
0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x33, // name: param_with_name_3
0x02, 0x03, // outer index 2 inner count 3
0x00, 0x11, // inner index 0 name length: 17
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x31, // name: local_with_name_1
0x01, 0x11, // inner index 1 name length: 17
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x32, // name: local_with_name_2
0x02, 0x11, // inner index 2 name length: 17
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x33, // name: local_with_name_3
0x05, // name type: table
0x12, // payload length: 18
0x01, // names count 1
0x00, 0x0f, // index 0 name length: 15
0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
// name: table_with_name
0x06, // name type: memory
0x13, // payload length: 19
0x01, // names count 1
0x00, 0x10, // index 0 name length: 16
0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x77,
0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
// name: memory_with_name
0x07, // name type: global
0x49, // payload length: 73
0x03, // names count 3
0x00, 0x19, // index 0 name length: 25
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f,
0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d,
0x65, // name: imported_global_with_name
0x01, 0x10, // index 1 name length: 16
0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x77,
0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
// name: global_with_name
0x02, 0x19, // index 2 name length: 25
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f,
0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d,
0x65, // name: exported_global_with_name
0x08, // name type: element segment
0x11, // payload length: 17
0x01, // names count 1
0x00, 0x0e, // index 0 name length: 14
0x65, 0x6c, 0x65, 0x6d, 0x5f, 0x77, 0x69, 0x74,
0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
// name: elem_with_name

View File

@ -0,0 +1,42 @@
;; expected = R"---(;; This is a polyglot C++/WAT file.
;; Comment lines are ignored and not expected in the disassembler output.
(module
;; TODO(jkummerow): This type name is missing from the disassembler ouput.
;; (type $type_with_name (;0;) (func (param f32)))
(global $imported_global_with_name (;0;) (import "env" "imported_global") i32)
(func $imported_function_with_name (;0;) (import "env" "imported_function"))
(table $table_with_name (;0;) 0 funcref)
(memory $memory_with_name (;0;) 0)
(global $global_with_name (;1;) i32 (i32.const 0))
(global $exported_global_with_name (;2;) (export "exported_global") i32 (i32.const 0))
(elem $elem_with_name (;0;) funcref (ref.func $function_with_name))
(func $function_with_name (;1;) (param $param_with_name_1 (;0;) i32) (param $param_with_name_2 (;1;) i32) (param $param_with_name_3 (;2;) i64)
local.get $param_with_name_1
drop
local.get $param_with_name_2
drop
local.get $param_with_name_3
drop
)
(func $exported_function_with_name (;2;) (export "exported_function")
;; Local variables.
(local $local_with_name_1 i32)
(local $local_with_name_2 i32)
(local $local_with_name_3 i64)
local.get $local_with_name_1
drop
local.get $local_with_name_2
drop
local.get $local_with_name_3
drop
)
;; TODO(jkummerow): Functions with a named type are printed with their type
;; inline instead of as follows.
;; (func $another_function (;3;) (type $type_with_name)
;; )
;; For compatibility with the legacy DevTools behavior, we don't print data
;; segment names. If we change that, uncomment the following line.
;; (data $data_with_name (;0;) (i32.const 0) "foo\0a\00")
(data (i32.const 0) "foo\0a\00")
)
;;)---";

View File

@ -18,32 +18,19 @@ namespace wasm {
class WasmDisassemblerTest : public ::v8::TestWithPlatform {};
TEST_F(WasmDisassemblerTest, Mvp) {
// If you want to extend this test:
// 1. Modify the .wat.inc file included below, e.g., add more instructions.
// 2. Convert the Wasm text file to a Wasm binary with `wat2wasm`.
// 3. Convert the Wasm binary to an array init expression with
// `wami --full-hexdump` and paste it into the included file below.
// One liner (Linux):
// wat2wasm wasm-disassembler-unittest-mvp.wat.inc --output=-
// | wami --full-hexdump
// | head -n-1 | tail -n+2 > wasm-disassembler-unittest-mvp.wasm.inc
constexpr byte module_bytes_array[] = {
#include "wasm-disassembler-unittest-mvp.wasm.inc"
};
base::Vector<const byte> module_bytes_vector =
base::ArrayVector(module_bytes_array);
// Code that is shared for all tests, the only difference is the input module
// and expected disassembler output.
void CheckDisassemblerOutput(base::Vector<const byte> module_bytes,
std::string expected_output) {
AccountingAllocator allocator;
ModuleResult module_result = DecodeWasmModuleForDisassembler(
module_bytes_vector.begin(), module_bytes_vector.end(), &allocator);
module_bytes.begin(), module_bytes.end(), &allocator);
DCHECK(module_result.ok());
WasmModule* module = module_result.value().get();
ModuleWireBytes wire_bytes(module_bytes_vector);
NamesProvider names(module, module_bytes_vector);
ModuleWireBytes wire_bytes(module_bytes);
NamesProvider names(module, module_bytes);
MultiLineStringBuilder output_sb;
@ -53,6 +40,29 @@ TEST_F(WasmDisassemblerTest, Mvp) {
std::ostringstream output;
output_sb.WriteTo(output);
// Remove comment lines from expected output since they cannot be recovered
// by a disassembler.
// They were also used as part of the C++/WAT polyglot trick described below.
expected_output =
std::regex_replace(expected_output, std::regex(" *;;[^\\n]*\\n?"), "");
EXPECT_EQ(output.str(), expected_output);
}
TEST_F(WasmDisassemblerTest, Mvp) {
// If you want to extend this test (and the other tests below):
// 1. Modify the included .wat.inc file(s), e.g., add more instructions.
// 2. Convert the Wasm text file to a Wasm binary with `wat2wasm`.
// 3. Convert the Wasm binary to an array init expression with
// `wami --full-hexdump` and paste it into the included file below.
// One liner example (Linux):
// wat2wasm wasm-disassembler-unittest-mvp.wat.inc --output=-
// | wami --full-hexdump
// | head -n-1 | tail -n+2 > wasm-disassembler-unittest-mvp.wasm.inc
constexpr byte module_bytes[] = {
#include "wasm-disassembler-unittest-mvp.wasm.inc"
};
// Little trick: polyglot C++/WebAssembly text file.
// We want to include the expected disassembler text output as a string into
// this test (instead of reading it from the file at runtime, which would make
@ -64,11 +74,19 @@ TEST_F(WasmDisassemblerTest, Mvp) {
// harm when including the file here either.
std::string expected;
#include "wasm-disassembler-unittest-mvp.wat.inc"
// Remove comment lines which cannot be recovered by a disassembler.
// They were also used as part of the C++/WAT polyglot trick above.
expected = std::regex_replace(expected, std::regex(" *;;[^\\n]*\\n?"), "");
EXPECT_EQ(output.str(), expected);
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
TEST_F(WasmDisassemblerTest, Names) {
// You can create a binary with a custom name section from the text format via
// `wat2wasm --debug-names`.
constexpr byte module_bytes[] = {
#include "wasm-disassembler-unittest-names.wasm.inc"
};
std::string expected;
#include "wasm-disassembler-unittest-names.wat.inc"
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
} // namespace wasm