223 lines
7.6 KiB
Go
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")
|
|
}
|