Files
wazero/internal/testing/binaryencoding/encoder_test.go
Takeshi Yoneda e17a85146a Holds wasm.Code as values on wasm.Module (#1243)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
2023-03-15 14:45:54 +09:00

223 lines
7.6 KiB
Go

package binaryencoding
import (
"testing"
"github.com/tetratelabs/wazero/internal/leb128"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/internal/wasm"
)
func TestModule_Encode(t *testing.T) {
i32, f32 := wasm.ValueTypeI32, wasm.ValueTypeF32
zero := uint32(0)
tests := []struct {
name string
input *wasm.Module
expected []byte
}{
{
name: "empty",
input: &wasm.Module{},
expected: append(Magic, version...),
},
{
name: "only name section",
input: &wasm.Module{NameSection: &wasm.NameSection{ModuleName: "simple"}},
expected: append(append(Magic, version...),
wasm.SectionIDCustom, 0x0e, // 14 bytes in this section
0x04, 'n', 'a', 'm', 'e',
subsectionIDModuleName, 0x07, // 7 bytes in this subsection
0x06, // the Module name simple is 6 bytes long
's', 'i', 'm', 'p', 'l', 'e'),
},
{
name: "type section",
input: &wasm.Module{
TypeSection: []wasm.FunctionType{
{},
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
{Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}},
},
},
expected: append(append(Magic, version...),
wasm.SectionIDType, 0x12, // 18 bytes in this section
0x03, // 3 types
0x60, 0x00, 0x00, // func=0x60 no param no result
0x60, 0x02, i32, i32, 0x01, i32, // func=0x60 2 params and 1 result
0x60, 0x04, i32, i32, i32, i32, 0x01, i32, // func=0x60 4 params and 1 result
),
},
{
name: "type and import section",
input: &wasm.Module{
TypeSection: []wasm.FunctionType{
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
{Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32}},
},
ImportSection: []wasm.Import{
{
Module: "Math", Name: "Mul",
Type: wasm.ExternTypeFunc,
DescFunc: 1,
}, {
Module: "Math", Name: "Add",
Type: wasm.ExternTypeFunc,
DescFunc: 0,
},
},
},
expected: append(append(Magic, version...),
wasm.SectionIDType, 0x0d, // 13 bytes in this section
0x02, // 2 types
0x60, 0x02, i32, i32, 0x01, i32, // func=0x60 2 params and 1 result
0x60, 0x02, f32, f32, 0x01, f32, // func=0x60 2 params and 1 result
wasm.SectionIDImport, 0x17, // 23 bytes in this section
0x02, // 2 imports
0x04, 'M', 'a', 't', 'h', 0x03, 'M', 'u', 'l', wasm.ExternTypeFunc,
0x01, // type index
0x04, 'M', 'a', 't', 'h', 0x03, 'A', 'd', 'd', wasm.ExternTypeFunc,
0x00, // type index
),
},
{
name: "type function and start section",
input: &wasm.Module{
TypeSection: []wasm.FunctionType{{}},
ImportSection: []wasm.Import{{
Module: "", Name: "hello",
Type: wasm.ExternTypeFunc,
DescFunc: 0,
}},
StartSection: &zero,
},
expected: append(append(Magic, version...),
wasm.SectionIDType, 0x04, // 4 bytes in this section
0x01, // 1 type
0x60, 0x0, 0x0, // func=0x60 0 params and 0 result
wasm.SectionIDImport, 0x0a, // 10 bytes in this section
0x01, // 1 import
0x00, 0x05, 'h', 'e', 'l', 'l', 'o', wasm.ExternTypeFunc,
0x00, // type index
wasm.SectionIDStart, 0x01,
0x00, // start function index
),
},
{
name: "table and memory section",
input: &wasm.Module{
TableSection: []wasm.Table{{Min: 3, Type: wasm.RefTypeFuncref}},
MemorySection: &wasm.Memory{Min: 1, Max: 1, IsMaxEncoded: true},
},
expected: append(append(Magic, version...),
wasm.SectionIDTable, 0x04, // 4 bytes in this section
0x01, // 1 table
wasm.RefTypeFuncref, 0x0, 0x03, // func, only min: 3
wasm.SectionIDMemory, 0x04, // 4 bytes in this section
0x01, // 1 memory
0x01, 0x01, 0x01, // min and max = 1
),
},
{
name: "exported func with instructions",
input: &wasm.Module{
TypeSection: []wasm.FunctionType{
{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}},
},
FunctionSection: []wasm.Index{0},
CodeSection: []wasm.Code{
{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeI32Add, wasm.OpcodeEnd}},
},
ExportSection: []wasm.Export{
{Name: "AddInt", Type: wasm.ExternTypeFunc, Index: wasm.Index(0)},
},
NameSection: &wasm.NameSection{
FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "addInt"}},
LocalNames: wasm.IndirectNameMap{
{Index: wasm.Index(0), NameMap: wasm.NameMap{
{Index: wasm.Index(0), Name: "value_1"},
{Index: wasm.Index(1), Name: "value_2"},
}},
},
},
},
expected: append(append(Magic, version...),
wasm.SectionIDType, 0x07, // 7 bytes in this section
0x01, // 1 type
0x60, 0x02, i32, i32, 0x01, i32, // func=0x60 2 params and 1 result
wasm.SectionIDFunction, 0x02, // 2 bytes in this section
0x01, // 1 function
0x00, // func[0] type index 0
wasm.SectionIDExport, 0x0a, // 10 bytes in this section
0x01, // 1 export
0x06, 'A', 'd', 'd', 'I', 'n', 't', // size of "AddInt", "AddInt"
wasm.ExternTypeFunc, 0x00, // func[0]
wasm.SectionIDCode, 0x09, // 9 bytes in this section
0o1, // one code section
0o7, // length of the body + locals
0o0, // count of local blocks
wasm.OpcodeLocalGet, 0, // local.get 0
wasm.OpcodeLocalGet, 1, // local.get 1
wasm.OpcodeI32Add, // i32.add
wasm.OpcodeEnd, // end of instructions/code
wasm.SectionIDCustom, 0x27, // 39 bytes in this section
0x04, 'n', 'a', 'm', 'e',
subsectionIDFunctionNames, 0x09, // 9 bytes
0x01, // two function names
0x00, 0x06, 'a', 'd', 'd', 'I', 'n', 't', // index 0, size of "addInt", "addInt"
subsectionIDLocalNames, 0x15, // 21 bytes
0x01, // one function
0x00, 0x02, // index 0 has 2 locals
0x00, 0x07, 'v', 'a', 'l', 'u', 'e', '_', '1', // index 0, size of "value_1", "value_1"
0x01, 0x07, 'v', 'a', 'l', 'u', 'e', '_', '2', // index 1, size of "value_2", "value_2"
),
},
{
name: "exported global var",
input: &wasm.Module{
GlobalSection: []wasm.Global{
{
Type: wasm.GlobalType{ValType: i32, Mutable: true},
Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(0)},
},
},
ExportSection: []wasm.Export{
{Name: "sp", Type: wasm.ExternTypeGlobal, Index: wasm.Index(0)},
},
},
expected: append(append(Magic, version...),
wasm.SectionIDGlobal, 0x06, // 6 bytes in this section
0x01, wasm.ValueTypeI32, 0x01, // 1 global i32 mutable
wasm.OpcodeI32Const, 0x00, wasm.OpcodeEnd, // arbitrary init to zero
wasm.SectionIDExport, 0x06, // 6 bytes in this section
0x01, // 1 export
0x02, 's', 'p', // size of "sp", "sp"
wasm.ExternTypeGlobal, 0x00, // global[0]
),
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
bytes := EncodeModule(tc.input)
require.Equal(t, tc.expected, bytes)
})
}
}
func TestModule_Encode_HostFunctionSection_Unsupported(t *testing.T) {
// We don't currently have an approach to serialize reflect.Value pointers
fn := func() {}
captured := require.CapturePanic(func() {
EncodeModule(&wasm.Module{
TypeSection: []wasm.FunctionType{{}},
CodeSection: []wasm.Code{wasm.MustParseGoReflectFuncCode(fn)},
})
})
require.EqualError(t, captured, "BUG: GoFunction is not encodable")
}