refactor binary encoding to its own package (#1187)
move binary encoder to its own package Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
This commit is contained in:
48
internal/testing/binaryencoding/code.go
Normal file
48
internal/testing/binaryencoding/code.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// encodeCode returns the wasm.Code encoded in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-code
|
||||
func encodeCode(c *wasm.Code) []byte {
|
||||
if c.GoFunc != nil {
|
||||
panic("BUG: GoFunction is not encodable")
|
||||
}
|
||||
|
||||
// local blocks compress locals while preserving index order by grouping locals of the same type.
|
||||
// https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#code-section%E2%91%A0
|
||||
localBlockCount := uint32(0) // how many blocks of locals with the same type (types can repeat!)
|
||||
var localBlocks []byte
|
||||
localTypeLen := len(c.LocalTypes)
|
||||
if localTypeLen > 0 {
|
||||
i := localTypeLen - 1
|
||||
var runCount uint32 // count of the same type
|
||||
var lastValueType wasm.ValueType // initialize to an invalid type 0
|
||||
|
||||
// iterate backwards so it is easier to size prefix
|
||||
for ; i >= 0; i-- {
|
||||
vt := c.LocalTypes[i]
|
||||
if lastValueType != vt {
|
||||
if runCount != 0 { // Only on the first iteration, this is zero when vt is compared against invalid
|
||||
localBlocks = append(leb128.EncodeUint32(runCount), localBlocks...)
|
||||
}
|
||||
lastValueType = vt
|
||||
localBlocks = append(leb128.EncodeUint32(uint32(vt)), localBlocks...) // reuse the EncodeUint32 cache
|
||||
localBlockCount++
|
||||
runCount = 1
|
||||
} else {
|
||||
runCount++
|
||||
}
|
||||
}
|
||||
localBlocks = append(leb128.EncodeUint32(runCount), localBlocks...)
|
||||
localBlocks = append(leb128.EncodeUint32(localBlockCount), localBlocks...)
|
||||
} else {
|
||||
localBlocks = leb128.EncodeUint32(0)
|
||||
}
|
||||
code := append(localBlocks, c.Body...)
|
||||
return append(leb128.EncodeUint32(uint32(len(code))), code...)
|
||||
}
|
||||
96
internal/testing/binaryencoding/code_test.go
Normal file
96
internal/testing/binaryencoding/code_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
var addLocalZeroLocalTwo = []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 2, wasm.OpcodeI32Add, wasm.OpcodeEnd}
|
||||
|
||||
func TestEncodeCode(t *testing.T) {
|
||||
addLocalZeroLocalOne := []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeI32Add, wasm.OpcodeEnd}
|
||||
tests := []struct {
|
||||
name string
|
||||
input *wasm.Code
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
name: "smallest function body",
|
||||
input: &wasm.Code{ // e.g. (func)
|
||||
Body: []byte{wasm.OpcodeEnd},
|
||||
},
|
||||
expected: []byte{
|
||||
0x02, // 2 bytes to encode locals and the body
|
||||
0x00, // no local blocks
|
||||
wasm.OpcodeEnd, // Body
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "params and instructions", // local.get index is params, then locals
|
||||
input: &wasm.Code{ // e.g. (func (type 3) local.get 0 local.get 1 i32.add)
|
||||
Body: addLocalZeroLocalOne,
|
||||
},
|
||||
expected: append([]byte{
|
||||
0x07, // 7 bytes to encode locals and the body
|
||||
0x00, // no local blocks
|
||||
},
|
||||
addLocalZeroLocalOne..., // Body
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "locals and instructions",
|
||||
input: &wasm.Code{ // e.g. (func (result i32) (local i32, i32) local.get 0 local.get 1 i32.add)
|
||||
LocalTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||
Body: addLocalZeroLocalOne,
|
||||
},
|
||||
expected: append([]byte{
|
||||
0x09, // 9 bytes to encode locals and the body
|
||||
0x01, // 1 local block
|
||||
0x02, wasm.ValueTypeI32, // local block 1
|
||||
},
|
||||
addLocalZeroLocalOne..., // Body
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "mixed locals and instructions",
|
||||
input: &wasm.Code{ // e.g. (func (result i32) (local i32) (local i64) (local i32) local.get 0 local.get 2 i32.add)
|
||||
LocalTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeI32},
|
||||
Body: addLocalZeroLocalTwo,
|
||||
},
|
||||
expected: append([]byte{
|
||||
0x0d, // 13 bytes to encode locals and the body
|
||||
0x03, // 3 local blocks
|
||||
0x01, wasm.ValueTypeI32, // local block 1
|
||||
0x01, wasm.ValueTypeI64, // local block 2
|
||||
0x01, wasm.ValueTypeI32, // local block 3
|
||||
},
|
||||
addLocalZeroLocalTwo..., // Body
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bytes := encodeCode(tc.input)
|
||||
require.Equal(t, tc.expected, bytes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeCode(b *testing.B) {
|
||||
input := &wasm.Code{ // e.g. (func (result i32) (local i32) (local i64) (local i32) local.get 0 local.get 2 i32.add)
|
||||
LocalTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeI32},
|
||||
Body: addLocalZeroLocalTwo,
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if bytes := encodeCode(input); len(bytes) == 0 {
|
||||
b.Fatal("didn't encode anything")
|
||||
}
|
||||
}
|
||||
}
|
||||
12
internal/testing/binaryencoding/const_expr.go
Normal file
12
internal/testing/binaryencoding/const_expr.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func encodeConstantExpression(expr *wasm.ConstantExpression) (ret []byte) {
|
||||
ret = append(ret, expr.Opcode)
|
||||
ret = append(ret, expr.Data...)
|
||||
ret = append(ret, wasm.OpcodeEnd)
|
||||
return
|
||||
}
|
||||
15
internal/testing/binaryencoding/data.go
Normal file
15
internal/testing/binaryencoding/data.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func encodeDataSegment(d *wasm.DataSegment) (ret []byte) {
|
||||
// Currently multiple memories are not supported.
|
||||
ret = append(ret, leb128.EncodeInt32(0)...)
|
||||
ret = append(ret, encodeConstantExpression(d.OffsetExpression)...)
|
||||
ret = append(ret, leb128.EncodeUint32(uint32(len(d.Init)))...)
|
||||
ret = append(ret, d.Init...)
|
||||
return
|
||||
}
|
||||
37
internal/testing/binaryencoding/element.go
Normal file
37
internal/testing/binaryencoding/element.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func ensureElementKindFuncRef(r *bytes.Reader) error {
|
||||
elemKind, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read element prefix: %w", err)
|
||||
}
|
||||
if elemKind != 0x0 { // ElemKind is fixed to 0x0 now: https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
|
||||
return fmt.Errorf("element kind must be zero but was 0x%x", elemKind)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeCode returns the wasm.ElementSegment encoded in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#element-section%E2%91%A0
|
||||
func encodeElement(e *wasm.ElementSegment) (ret []byte) {
|
||||
if e.Mode == wasm.ElementModeActive {
|
||||
ret = append(ret, leb128.EncodeInt32(int32(e.TableIndex))...)
|
||||
ret = append(ret, encodeConstantExpression(e.OffsetExpr)...)
|
||||
ret = append(ret, leb128.EncodeUint32(uint32(len(e.Init)))...)
|
||||
for _, idx := range e.Init {
|
||||
ret = append(ret, leb128.EncodeInt32(int32(*idx))...)
|
||||
}
|
||||
} else {
|
||||
panic("TODO: support encoding for non-active elements in bulk-memory-operations proposal")
|
||||
}
|
||||
return
|
||||
}
|
||||
13
internal/testing/binaryencoding/element_test.go
Normal file
13
internal/testing/binaryencoding/element_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
)
|
||||
|
||||
func Test_ensureElementKindFuncRef(t *testing.T) {
|
||||
require.NoError(t, ensureElementKindFuncRef(bytes.NewReader([]byte{0x0})))
|
||||
require.Error(t, ensureElementKindFuncRef(bytes.NewReader([]byte{0x1})))
|
||||
}
|
||||
56
internal/testing/binaryencoding/encoder.go
Normal file
56
internal/testing/binaryencoding/encoder.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
var sizePrefixedName = []byte{4, 'n', 'a', 'm', 'e'}
|
||||
|
||||
// EncodeModule implements wasm.EncodeModule for the WebAssembly 1.0 (20191205) Binary Format.
|
||||
// Note: If saving to a file, the conventional extension is wasm
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-format%E2%91%A0
|
||||
func EncodeModule(m *wasm.Module) (bytes []byte) {
|
||||
bytes = append(Magic, version...)
|
||||
if m.SectionElementCount(wasm.SectionIDType) > 0 {
|
||||
bytes = append(bytes, encodeTypeSection(m.TypeSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDImport) > 0 {
|
||||
bytes = append(bytes, encodeImportSection(m.ImportSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDFunction) > 0 {
|
||||
bytes = append(bytes, EncodeFunctionSection(m.FunctionSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDTable) > 0 {
|
||||
bytes = append(bytes, encodeTableSection(m.TableSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDMemory) > 0 {
|
||||
bytes = append(bytes, encodeMemorySection(m.MemorySection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDGlobal) > 0 {
|
||||
bytes = append(bytes, encodeGlobalSection(m.GlobalSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDExport) > 0 {
|
||||
bytes = append(bytes, encodeExportSection(m.ExportSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDStart) > 0 {
|
||||
bytes = append(bytes, EncodeStartSection(*m.StartSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDElement) > 0 {
|
||||
bytes = append(bytes, encodeElementSection(m.ElementSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDCode) > 0 {
|
||||
bytes = append(bytes, encodeCodeSection(m.CodeSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDData) > 0 {
|
||||
bytes = append(bytes, encodeDataSection(m.DataSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDCustom) > 0 {
|
||||
// >> The name section should appear only once in a module, and only after the data section.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namesec
|
||||
if m.NameSection != nil {
|
||||
nameSection := append(sizePrefixedName, EncodeNameSectionData(m.NameSection)...)
|
||||
bytes = append(bytes, encodeSection(wasm.SectionIDCustom, nameSection)...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
222
internal/testing/binaryencoding/encoder_test.go
Normal file
222
internal/testing/binaryencoding/encoder_test.go
Normal file
@@ -0,0 +1,222 @@
|
||||
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")
|
||||
}
|
||||
16
internal/testing/binaryencoding/export.go
Normal file
16
internal/testing/binaryencoding/export.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// encodeExport returns the wasm.Export encoded in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#export-section%E2%91%A0
|
||||
func encodeExport(i *wasm.Export) []byte {
|
||||
data := encodeSizePrefixed([]byte(i.Name))
|
||||
data = append(data, i.Type)
|
||||
data = append(data, leb128.EncodeUint32(i.Index)...)
|
||||
return data
|
||||
}
|
||||
131
internal/testing/binaryencoding/export_test.go
Normal file
131
internal/testing/binaryencoding/export_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func TestEncodeExport(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input *wasm.Export
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
name: "func no name, index 0",
|
||||
input: &wasm.Export{ // e.g. (export "" (func 0)))
|
||||
Type: wasm.ExternTypeFunc,
|
||||
Name: "",
|
||||
Index: 0,
|
||||
},
|
||||
expected: []byte{wasm.ExternTypeFunc, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
name: "func name, func index 0",
|
||||
input: &wasm.Export{ // e.g. (export "pi" (func 0))
|
||||
Type: wasm.ExternTypeFunc,
|
||||
Name: "pi",
|
||||
Index: 0,
|
||||
},
|
||||
expected: []byte{
|
||||
0x02, 'p', 'i',
|
||||
wasm.ExternTypeFunc,
|
||||
0x00,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "func name, index 10",
|
||||
input: &wasm.Export{ // e.g. (export "pi" (func 10))
|
||||
Type: wasm.ExternTypeFunc,
|
||||
Name: "pi",
|
||||
Index: 10,
|
||||
},
|
||||
expected: []byte{
|
||||
0x02, 'p', 'i',
|
||||
wasm.ExternTypeFunc,
|
||||
0x0a,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global no name, index 0",
|
||||
input: &wasm.Export{ // e.g. (export "" (global 0)))
|
||||
Type: wasm.ExternTypeGlobal,
|
||||
Name: "",
|
||||
Index: 0,
|
||||
},
|
||||
expected: []byte{0x00, wasm.ExternTypeGlobal, 0x00},
|
||||
},
|
||||
{
|
||||
name: "global name, global index 0",
|
||||
input: &wasm.Export{ // e.g. (export "pi" (global 0))
|
||||
Type: wasm.ExternTypeGlobal,
|
||||
Name: "pi",
|
||||
Index: 0,
|
||||
},
|
||||
expected: []byte{
|
||||
0x02, 'p', 'i',
|
||||
wasm.ExternTypeGlobal,
|
||||
0x00,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global name, index 10",
|
||||
input: &wasm.Export{ // e.g. (export "pi" (global 10))
|
||||
Type: wasm.ExternTypeGlobal,
|
||||
Name: "pi",
|
||||
Index: 10,
|
||||
},
|
||||
expected: []byte{
|
||||
0x02, 'p', 'i',
|
||||
wasm.ExternTypeGlobal,
|
||||
0x0a,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory no name, index 0",
|
||||
input: &wasm.Export{ // e.g. (export "" (memory 0)))
|
||||
Type: wasm.ExternTypeMemory,
|
||||
Name: "",
|
||||
Index: 0,
|
||||
},
|
||||
expected: []byte{0x00, wasm.ExternTypeMemory, 0x00},
|
||||
},
|
||||
{
|
||||
name: "memory name, memory index 0",
|
||||
input: &wasm.Export{ // e.g. (export "mem" (memory 0))
|
||||
Type: wasm.ExternTypeMemory,
|
||||
Name: "mem",
|
||||
Index: 0,
|
||||
},
|
||||
expected: []byte{
|
||||
0x03, 'm', 'e', 'm',
|
||||
wasm.ExternTypeMemory,
|
||||
0x00,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory name, index 10",
|
||||
input: &wasm.Export{ // e.g. (export "mem" (memory 10))
|
||||
Type: wasm.ExternTypeMemory,
|
||||
Name: "mem",
|
||||
Index: 10,
|
||||
},
|
||||
expected: []byte{
|
||||
0x03, 'm', 'e', 'm',
|
||||
wasm.ExternTypeMemory,
|
||||
0x0a,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bytes := encodeExport(tc.input)
|
||||
require.Equal(t, tc.expected, bytes)
|
||||
})
|
||||
}
|
||||
}
|
||||
15
internal/testing/binaryencoding/function.go
Normal file
15
internal/testing/binaryencoding/function.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// EncodeFunctionType returns the wasm.FunctionType encoded in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// Note: Function types are encoded by the byte 0x60 followed by the respective vectors of parameter and result types.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-types%E2%91%A4
|
||||
func EncodeFunctionType(t *wasm.FunctionType) []byte {
|
||||
// Only reached when "multi-value" is enabled because WebAssembly 1.0 (20191205) supports at most 1 result.
|
||||
data := append([]byte{0x60}, EncodeValTypes(t.Params)...)
|
||||
return append(data, EncodeValTypes(t.Results)...)
|
||||
}
|
||||
18
internal/testing/binaryencoding/global.go
Normal file
18
internal/testing/binaryencoding/global.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// encodeGlobal returns the wasm.Global encoded in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#global-section%E2%91%A0
|
||||
func encodeGlobal(g *wasm.Global) (data []byte) {
|
||||
var mutable byte
|
||||
if g.Type.Mutable {
|
||||
mutable = 1
|
||||
}
|
||||
data = []byte{g.Type.ValType, mutable}
|
||||
data = append(data, encodeConstantExpression(g.Init)...)
|
||||
return
|
||||
}
|
||||
49
internal/testing/binaryencoding/global_test.go
Normal file
49
internal/testing/binaryencoding/global_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func TestEncodeGlobal(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input *wasm.Global
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
name: "const",
|
||||
input: &wasm.Global{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(1)},
|
||||
},
|
||||
expected: []byte{
|
||||
wasm.ValueTypeI32, 0x00, // 0 == const
|
||||
wasm.OpcodeI32Const, 0x01, wasm.OpcodeEnd,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "var",
|
||||
input: &wasm.Global{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(1)},
|
||||
},
|
||||
expected: []byte{
|
||||
wasm.ValueTypeI32, 0x01, // 1 == var
|
||||
wasm.OpcodeI32Const, 0x01, wasm.OpcodeEnd,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bytes := encodeGlobal(tc.input)
|
||||
require.Equal(t, tc.expected, bytes)
|
||||
})
|
||||
}
|
||||
}
|
||||
9
internal/testing/binaryencoding/header.go
Normal file
9
internal/testing/binaryencoding/header.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package binaryencoding
|
||||
|
||||
// Magic is the 4 byte preamble (literally "\0asm") of the binary format
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-magic
|
||||
var Magic = []byte{0x00, 0x61, 0x73, 0x6D}
|
||||
|
||||
// version is format version and doesn't change between known specification versions
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-version
|
||||
var version = []byte{0x01, 0x00, 0x00, 0x00}
|
||||
40
internal/testing/binaryencoding/import.go
Normal file
40
internal/testing/binaryencoding/import.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// EncodeImport returns the wasm.Import encoded in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-import
|
||||
func EncodeImport(i *wasm.Import) []byte {
|
||||
data := encodeSizePrefixed([]byte(i.Module))
|
||||
data = append(data, encodeSizePrefixed([]byte(i.Name))...)
|
||||
data = append(data, i.Type)
|
||||
switch i.Type {
|
||||
case wasm.ExternTypeFunc:
|
||||
data = append(data, leb128.EncodeUint32(i.DescFunc)...)
|
||||
case wasm.ExternTypeTable:
|
||||
data = append(data, wasm.RefTypeFuncref)
|
||||
data = append(data, EncodeLimitsType(i.DescTable.Min, i.DescTable.Max)...)
|
||||
case wasm.ExternTypeMemory:
|
||||
maxPtr := &i.DescMem.Max
|
||||
if !i.DescMem.IsMaxEncoded {
|
||||
maxPtr = nil
|
||||
}
|
||||
data = append(data, EncodeLimitsType(i.DescMem.Min, maxPtr)...)
|
||||
case wasm.ExternTypeGlobal:
|
||||
g := i.DescGlobal
|
||||
var mutable byte
|
||||
if g.Mutable {
|
||||
mutable = 1
|
||||
}
|
||||
data = append(data, g.ValType, mutable)
|
||||
default:
|
||||
panic(fmt.Errorf("invalid externtype: %s", wasm.ExternTypeName(i.Type)))
|
||||
}
|
||||
return data
|
||||
}
|
||||
161
internal/testing/binaryencoding/import_test.go
Normal file
161
internal/testing/binaryencoding/import_test.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func TestEncodeImport(t *testing.T) {
|
||||
ptrOfUint32 := func(v uint32) *uint32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input *wasm.Import
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
name: "func no module, no name, type index 0",
|
||||
input: &wasm.Import{ // e.g. (import "" "" (func (type 0)))
|
||||
Type: wasm.ExternTypeFunc,
|
||||
Module: "",
|
||||
Name: "",
|
||||
DescFunc: 0,
|
||||
},
|
||||
expected: []byte{wasm.ExternTypeFunc, 0x00, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
name: "func module, no name, type index 0",
|
||||
input: &wasm.Import{ // e.g. (import "$test" "" (func (type 0)))
|
||||
Type: wasm.ExternTypeFunc,
|
||||
Module: "test",
|
||||
Name: "",
|
||||
DescFunc: 0,
|
||||
},
|
||||
expected: []byte{
|
||||
0x04, 't', 'e', 's', 't',
|
||||
0x00,
|
||||
wasm.ExternTypeFunc,
|
||||
0x00,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "func module, name, type index 0",
|
||||
input: &wasm.Import{ // e.g. (import "$math" "$pi" (func (type 0)))
|
||||
Type: wasm.ExternTypeFunc,
|
||||
Module: "math",
|
||||
Name: "pi",
|
||||
DescFunc: 0,
|
||||
},
|
||||
expected: []byte{
|
||||
0x04, 'm', 'a', 't', 'h',
|
||||
0x02, 'p', 'i',
|
||||
wasm.ExternTypeFunc,
|
||||
0x00,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "func module, name, type index 10",
|
||||
input: &wasm.Import{ // e.g. (import "$math" "$pi" (func (type 10)))
|
||||
Type: wasm.ExternTypeFunc,
|
||||
Module: "math",
|
||||
Name: "pi",
|
||||
DescFunc: 10,
|
||||
},
|
||||
expected: []byte{
|
||||
0x04, 'm', 'a', 't', 'h',
|
||||
0x02, 'p', 'i',
|
||||
wasm.ExternTypeFunc,
|
||||
0x0a,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global const",
|
||||
input: &wasm.Import{
|
||||
Type: wasm.ExternTypeGlobal,
|
||||
Module: "math",
|
||||
Name: "pi",
|
||||
DescGlobal: &wasm.GlobalType{ValType: wasm.ValueTypeF64},
|
||||
},
|
||||
expected: []byte{
|
||||
0x04, 'm', 'a', 't', 'h',
|
||||
0x02, 'p', 'i',
|
||||
wasm.ExternTypeGlobal,
|
||||
wasm.ValueTypeF64, 0x00, // 0 == const
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global var",
|
||||
input: &wasm.Import{
|
||||
Type: wasm.ExternTypeGlobal,
|
||||
Module: "math",
|
||||
Name: "pi",
|
||||
DescGlobal: &wasm.GlobalType{ValType: wasm.ValueTypeF64, Mutable: true},
|
||||
},
|
||||
expected: []byte{
|
||||
0x04, 'm', 'a', 't', 'h',
|
||||
0x02, 'p', 'i',
|
||||
wasm.ExternTypeGlobal,
|
||||
wasm.ValueTypeF64, 0x01, // 1 == var
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "table",
|
||||
input: &wasm.Import{
|
||||
Type: wasm.ExternTypeTable,
|
||||
Module: "my",
|
||||
Name: "table",
|
||||
DescTable: &wasm.Table{Min: 1, Max: ptrOfUint32(2)},
|
||||
},
|
||||
expected: []byte{
|
||||
0x02, 'm', 'y',
|
||||
0x05, 't', 'a', 'b', 'l', 'e',
|
||||
wasm.ExternTypeTable,
|
||||
wasm.RefTypeFuncref,
|
||||
0x1, 0x1, 0x2, // Limit with max.
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory",
|
||||
input: &wasm.Import{
|
||||
Type: wasm.ExternTypeMemory,
|
||||
Module: "my",
|
||||
Name: "memory",
|
||||
DescMem: &wasm.Memory{Min: 1, Max: 2, IsMaxEncoded: true},
|
||||
},
|
||||
expected: []byte{
|
||||
0x02, 'm', 'y',
|
||||
0x06, 'm', 'e', 'm', 'o', 'r', 'y',
|
||||
wasm.ExternTypeMemory,
|
||||
0x1, 0x1, 0x2, // Limit with max.
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory - defaultt max",
|
||||
input: &wasm.Import{
|
||||
Type: wasm.ExternTypeMemory,
|
||||
Module: "my",
|
||||
Name: "memory",
|
||||
DescMem: &wasm.Memory{Min: 1, Max: wasm.MemoryLimitPages, IsMaxEncoded: false},
|
||||
},
|
||||
expected: []byte{
|
||||
0x02, 'm', 'y',
|
||||
0x06, 'm', 'e', 'm', 'o', 'r', 'y',
|
||||
wasm.ExternTypeMemory,
|
||||
0x0, 0x1, // Limit without max.
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bytes := EncodeImport(tc.input)
|
||||
require.Equal(t, tc.expected, bytes)
|
||||
})
|
||||
}
|
||||
}
|
||||
15
internal/testing/binaryencoding/limits.go
Normal file
15
internal/testing/binaryencoding/limits.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
)
|
||||
|
||||
// EncodeLimitsType returns the `limitsType` (min, max) encoded in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#limits%E2%91%A6
|
||||
func EncodeLimitsType(min uint32, max *uint32) []byte {
|
||||
if max == nil {
|
||||
return append(leb128.EncodeUint32(0x00), leb128.EncodeUint32(min)...)
|
||||
}
|
||||
return append(leb128.EncodeUint32(0x01), append(leb128.EncodeUint32(min), leb128.EncodeUint32(*max)...)...)
|
||||
}
|
||||
16
internal/testing/binaryencoding/memory.go
Normal file
16
internal/testing/binaryencoding/memory.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// EncodeMemory returns the wasm.Memory encoded in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-memory
|
||||
func EncodeMemory(i *wasm.Memory) []byte {
|
||||
maxPtr := &i.Max
|
||||
if !i.IsMaxEncoded {
|
||||
maxPtr = nil
|
||||
}
|
||||
return EncodeLimitsType(i.Min, maxPtr)
|
||||
}
|
||||
93
internal/testing/binaryencoding/names.go
Normal file
93
internal/testing/binaryencoding/names.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
const (
|
||||
// subsectionIDModuleName contains only the module name.
|
||||
subsectionIDModuleName = uint8(0)
|
||||
// subsectionIDFunctionNames is a map of indices to function names, in ascending order by function index
|
||||
subsectionIDFunctionNames = uint8(1)
|
||||
// subsectionIDLocalNames contain a map of function indices to a map of local indices to their names, in ascending
|
||||
// order by function and local index
|
||||
subsectionIDLocalNames = uint8(2)
|
||||
)
|
||||
|
||||
// EncodeNameSectionData serializes the data for the "name" key in wasm.SectionIDCustom according to the
|
||||
// standard:
|
||||
//
|
||||
// Note: The result can be nil because this does not encode empty subsections
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namesec
|
||||
func EncodeNameSectionData(n *wasm.NameSection) (data []byte) {
|
||||
if n.ModuleName != "" {
|
||||
data = append(data, encodeNameSubsection(subsectionIDModuleName, encodeSizePrefixed([]byte(n.ModuleName)))...)
|
||||
}
|
||||
if fd := encodeFunctionNameData(n); len(fd) > 0 {
|
||||
data = append(data, encodeNameSubsection(subsectionIDFunctionNames, fd)...)
|
||||
}
|
||||
if ld := encodeLocalNameData(n); len(ld) > 0 {
|
||||
data = append(data, encodeNameSubsection(subsectionIDLocalNames, ld)...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// encodeFunctionNameData encodes the data for the function name subsection.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-funcnamesec
|
||||
func encodeFunctionNameData(n *wasm.NameSection) []byte {
|
||||
if len(n.FunctionNames) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return encodeNameMap(n.FunctionNames)
|
||||
}
|
||||
|
||||
func encodeNameMap(m wasm.NameMap) []byte {
|
||||
count := uint32(len(m))
|
||||
data := leb128.EncodeUint32(count)
|
||||
for _, na := range m {
|
||||
data = append(data, encodeNameAssoc(na)...)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// encodeLocalNameData encodes the data for the local name subsection.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-localnamesec
|
||||
func encodeLocalNameData(n *wasm.NameSection) []byte {
|
||||
if len(n.LocalNames) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
funcNameCount := uint32(len(n.LocalNames))
|
||||
subsection := leb128.EncodeUint32(funcNameCount)
|
||||
|
||||
for _, na := range n.LocalNames {
|
||||
locals := encodeNameMap(na.NameMap)
|
||||
subsection = append(subsection, append(leb128.EncodeUint32(na.Index), locals...)...)
|
||||
}
|
||||
return subsection
|
||||
}
|
||||
|
||||
// encodeNameSubsection returns a buffer encoding the given subsection
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#subsections%E2%91%A0
|
||||
func encodeNameSubsection(subsectionID uint8, content []byte) []byte {
|
||||
contentSizeInBytes := leb128.EncodeUint32(uint32(len(content)))
|
||||
result := []byte{subsectionID}
|
||||
result = append(result, contentSizeInBytes...)
|
||||
result = append(result, content...)
|
||||
return result
|
||||
}
|
||||
|
||||
// encodeNameAssoc encodes the index and data prefixed by their size.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namemap
|
||||
func encodeNameAssoc(na *wasm.NameAssoc) []byte {
|
||||
return append(leb128.EncodeUint32(na.Index), encodeSizePrefixed([]byte(na.Name))...)
|
||||
}
|
||||
|
||||
// encodeSizePrefixed encodes the data prefixed by their size.
|
||||
func encodeSizePrefixed(data []byte) []byte {
|
||||
size := leb128.EncodeUint32(uint32(len(data)))
|
||||
return append(size, data...)
|
||||
}
|
||||
147
internal/testing/binaryencoding/names_test.go
Normal file
147
internal/testing/binaryencoding/names_test.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func TestEncodeNameSectionData(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input *wasm.NameSection
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: &wasm.NameSection{},
|
||||
},
|
||||
{
|
||||
name: "only module",
|
||||
// e.g. (module $simple )
|
||||
input: &wasm.NameSection{ModuleName: "simple"},
|
||||
expected: []byte{
|
||||
subsectionIDModuleName, 0x07, // 7 bytes
|
||||
0x06, // the Module name simple is 6 bytes long
|
||||
's', 'i', 'm', 'p', 'l', 'e',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "module and function name",
|
||||
// (module $simple
|
||||
// (import "" "Hello" (func $hello))
|
||||
// (start $hello)
|
||||
// )
|
||||
input: &wasm.NameSection{
|
||||
ModuleName: "simple",
|
||||
FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "hello"}},
|
||||
},
|
||||
expected: []byte{
|
||||
subsectionIDModuleName, 0x07, // 7 bytes
|
||||
0x06, // the Module name simple is 6 bytes long
|
||||
's', 'i', 'm', 'p', 'l', 'e',
|
||||
subsectionIDFunctionNames, 0x08, // 8 bytes
|
||||
0x01, // one function name
|
||||
0x00, // the function index is zero
|
||||
0x05, // the function name hello is 5 bytes long
|
||||
'h', 'e', 'l', 'l', 'o',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "two function names", // e.g. TinyGo which at one point didn't set a module name
|
||||
// (module
|
||||
// (import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi.args_sizes_get (param i32, i32) (result i32)))
|
||||
// (import "wasi_snapshot_preview1" "fd_write" (func $wasi.fd_write (param i32, i32, i32, i32) (result i32)))
|
||||
// )
|
||||
input: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{
|
||||
{Index: wasm.Index(0), Name: "wasi.args_sizes_get"},
|
||||
{Index: wasm.Index(1), Name: "wasi.fd_write"},
|
||||
},
|
||||
},
|
||||
expected: []byte{
|
||||
subsectionIDFunctionNames, 0x25, // 37 bytes
|
||||
0x02, // two function names
|
||||
0x00, // the function index is zero
|
||||
0x13, // the function name wasi.args_sizes_get is 19 bytes long
|
||||
'w', 'a', 's', 'i', '.', 'a', 'r', 'g', 's', '_', 's', 'i', 'z', 'e', 's', '_', 'g', 'e', 't',
|
||||
0x01, // the function index is one
|
||||
0x0d, // the function name wasi.fd_write is 13 bytes long
|
||||
'w', 'a', 's', 'i', '.', 'f', 'd', '_', 'w', 'r', 'i', 't', 'e',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "function with local names",
|
||||
// (module
|
||||
// (import "Math" "Mul" (func $mul (param $x f32) (param $y f32) (result f32)))
|
||||
// (import "Math" "Add" (func $add (param $l f32) (param $r f32) (result f32)))
|
||||
// )
|
||||
input: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{
|
||||
{Index: wasm.Index(0), Name: "mul"},
|
||||
{Index: wasm.Index(1), Name: "add"},
|
||||
},
|
||||
LocalNames: wasm.IndirectNameMap{
|
||||
{Index: wasm.Index(0), NameMap: wasm.NameMap{
|
||||
{Index: wasm.Index(0), Name: "x"},
|
||||
{Index: wasm.Index(1), Name: "y"},
|
||||
}},
|
||||
{Index: wasm.Index(1), NameMap: wasm.NameMap{
|
||||
{Index: wasm.Index(0), Name: "l"},
|
||||
{Index: wasm.Index(1), Name: "r"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
expected: []byte{
|
||||
subsectionIDFunctionNames, 0x0b, // 7 bytes
|
||||
0x02, // two function names
|
||||
0x00, 0x03, 'm', 'u', 'l', // index 0, size of "mul", "mul"
|
||||
0x01, 0x03, 'a', 'd', 'd', // index 1, size of "add", "add"
|
||||
subsectionIDLocalNames, 0x11, // 17 bytes
|
||||
0x02, // two functions
|
||||
0x00, 0x02, // index 0 has 2 locals
|
||||
0x00, 0x01, 'x', // index 0, size of "x", "x"
|
||||
0x01, 0x01, 'y', // index 1, size of "y", "y"
|
||||
0x01, 0x02, // index 1 has 2 locals
|
||||
0x00, 0x01, 'l', // index 0, size of "l", "l"
|
||||
0x01, 0x01, 'r', // index 1, size of "r", "r"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bytes := EncodeNameSectionData(tc.input)
|
||||
require.Equal(t, tc.expected, bytes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeNameSubsection(t *testing.T) {
|
||||
subsectionID := uint8(1)
|
||||
name := []byte("simple")
|
||||
require.Equal(t, []byte{
|
||||
subsectionID,
|
||||
byte(1 + 6), // 1 is the size of 6 in LEB128 encoding
|
||||
6, 's', 'i', 'm', 'p', 'l', 'e',
|
||||
}, encodeNameSubsection(subsectionID, encodeSizePrefixed(name)))
|
||||
}
|
||||
|
||||
func TestEncodeNameAssoc(t *testing.T) {
|
||||
na := &wasm.NameAssoc{Index: 1, Name: "hello"}
|
||||
require.Equal(t, []byte{byte(na.Index), 5, 'h', 'e', 'l', 'l', 'o'}, encodeNameAssoc(na))
|
||||
}
|
||||
|
||||
func TestEncodeNameMap(t *testing.T) {
|
||||
na := &wasm.NameAssoc{Index: 1, Name: "hello"}
|
||||
m := wasm.NameMap{na}
|
||||
require.Equal(t, []byte{byte(1), byte(na.Index), 5, 'h', 'e', 'l', 'l', 'o'}, encodeNameMap(m))
|
||||
}
|
||||
|
||||
func TestEncodeSizePrefixed(t *testing.T) {
|
||||
// We expect size in bytes (LEB128 encoded) then the bytes
|
||||
require.Equal(t, []byte{5, 'h', 'e', 'l', 'l', 'o'}, encodeSizePrefixed([]byte("hello")))
|
||||
}
|
||||
144
internal/testing/binaryencoding/section.go
Normal file
144
internal/testing/binaryencoding/section.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// encodeSection encodes the sectionID, the size of its contents in bytes, followed by the contents.
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#sections%E2%91%A0
|
||||
func encodeSection(sectionID wasm.SectionID, contents []byte) []byte {
|
||||
return append([]byte{sectionID}, encodeSizePrefixed(contents)...)
|
||||
}
|
||||
|
||||
// encodeTypeSection encodes a wasm.SectionIDType for the given imports in WebAssembly 1.0 (20191205) Binary
|
||||
// Format.
|
||||
//
|
||||
// See EncodeFunctionType
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#type-section%E2%91%A0
|
||||
func encodeTypeSection(types []*wasm.FunctionType) []byte {
|
||||
contents := leb128.EncodeUint32(uint32(len(types)))
|
||||
for _, t := range types {
|
||||
contents = append(contents, EncodeFunctionType(t)...)
|
||||
}
|
||||
return encodeSection(wasm.SectionIDType, contents)
|
||||
}
|
||||
|
||||
// encodeImportSection encodes a wasm.SectionIDImport for the given imports in WebAssembly 1.0 (20191205) Binary
|
||||
// Format.
|
||||
//
|
||||
// See EncodeImport
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#import-section%E2%91%A0
|
||||
func encodeImportSection(imports []*wasm.Import) []byte {
|
||||
contents := leb128.EncodeUint32(uint32(len(imports)))
|
||||
for _, i := range imports {
|
||||
contents = append(contents, EncodeImport(i)...)
|
||||
}
|
||||
return encodeSection(wasm.SectionIDImport, contents)
|
||||
}
|
||||
|
||||
// EncodeFunctionSection encodes a wasm.SectionIDFunction for the type indices associated with module-defined
|
||||
// functions in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-section%E2%91%A0
|
||||
func EncodeFunctionSection(typeIndices []wasm.Index) []byte {
|
||||
contents := leb128.EncodeUint32(uint32(len(typeIndices)))
|
||||
for _, index := range typeIndices {
|
||||
contents = append(contents, leb128.EncodeUint32(index)...)
|
||||
}
|
||||
return encodeSection(wasm.SectionIDFunction, contents)
|
||||
}
|
||||
|
||||
// encodeCodeSection encodes a wasm.SectionIDCode for the module-defined function in WebAssembly 1.0 (20191205)
|
||||
// Binary Format.
|
||||
//
|
||||
// See encodeCode
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#code-section%E2%91%A0
|
||||
func encodeCodeSection(code []*wasm.Code) []byte {
|
||||
contents := leb128.EncodeUint32(uint32(len(code)))
|
||||
for _, i := range code {
|
||||
contents = append(contents, encodeCode(i)...)
|
||||
}
|
||||
return encodeSection(wasm.SectionIDCode, contents)
|
||||
}
|
||||
|
||||
// encodeTableSection encodes a wasm.SectionIDTable for the module-defined function in WebAssembly 1.0
|
||||
// (20191205) Binary Format.
|
||||
//
|
||||
// See EncodeTable
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#table-section%E2%91%A0
|
||||
func encodeTableSection(tables []*wasm.Table) []byte {
|
||||
var contents []byte = leb128.EncodeUint32(uint32(len(tables)))
|
||||
for _, table := range tables {
|
||||
contents = append(contents, EncodeTable(table)...)
|
||||
}
|
||||
return encodeSection(wasm.SectionIDTable, contents)
|
||||
}
|
||||
|
||||
// encodeMemorySection encodes a wasm.SectionIDMemory for the module-defined function in WebAssembly 1.0
|
||||
// (20191205) Binary Format.
|
||||
//
|
||||
// See EncodeMemory
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-section%E2%91%A0
|
||||
func encodeMemorySection(memory *wasm.Memory) []byte {
|
||||
contents := append([]byte{1}, EncodeMemory(memory)...)
|
||||
return encodeSection(wasm.SectionIDMemory, contents)
|
||||
}
|
||||
|
||||
// encodeGlobalSection encodes a wasm.SectionIDGlobal for the given globals in WebAssembly 1.0 (20191205) Binary
|
||||
// Format.
|
||||
//
|
||||
// See encodeGlobal
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#global-section%E2%91%A0
|
||||
func encodeGlobalSection(globals []*wasm.Global) []byte {
|
||||
contents := leb128.EncodeUint32(uint32(len(globals)))
|
||||
for _, g := range globals {
|
||||
contents = append(contents, encodeGlobal(g)...)
|
||||
}
|
||||
return encodeSection(wasm.SectionIDGlobal, contents)
|
||||
}
|
||||
|
||||
// encodeExportSection encodes a wasm.SectionIDExport for the given exports in WebAssembly 1.0 (20191205) Binary
|
||||
// Format.
|
||||
//
|
||||
// See encodeExport
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#export-section%E2%91%A0
|
||||
func encodeExportSection(exports []*wasm.Export) []byte {
|
||||
contents := leb128.EncodeUint32(uint32(len(exports)))
|
||||
for _, e := range exports {
|
||||
contents = append(contents, encodeExport(e)...)
|
||||
}
|
||||
return encodeSection(wasm.SectionIDExport, contents)
|
||||
}
|
||||
|
||||
// EncodeStartSection encodes a wasm.SectionIDStart for the given function index in WebAssembly 1.0 (20191205)
|
||||
// Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#start-section%E2%91%A0
|
||||
func EncodeStartSection(funcidx wasm.Index) []byte {
|
||||
return encodeSection(wasm.SectionIDStart, leb128.EncodeUint32(funcidx))
|
||||
}
|
||||
|
||||
// encodeEelementSection encodes a wasm.SectionIDElement for the elements in WebAssembly 1.0 (20191205)
|
||||
// Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#element-section%E2%91%A0
|
||||
func encodeElementSection(elements []*wasm.ElementSegment) []byte {
|
||||
contents := leb128.EncodeUint32(uint32(len(elements)))
|
||||
for _, e := range elements {
|
||||
contents = append(contents, encodeElement(e)...)
|
||||
}
|
||||
return encodeSection(wasm.SectionIDElement, contents)
|
||||
}
|
||||
|
||||
// encodeDataSection encodes a wasm.SectionIDData for the data in WebAssembly 1.0 (20191205)
|
||||
// Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#data-section%E2%91%A0
|
||||
func encodeDataSection(datum []*wasm.DataSegment) []byte {
|
||||
contents := leb128.EncodeUint32(uint32(len(datum)))
|
||||
for _, d := range datum {
|
||||
contents = append(contents, encodeDataSegment(d)...)
|
||||
}
|
||||
return encodeSection(wasm.SectionIDData, contents)
|
||||
}
|
||||
17
internal/testing/binaryencoding/section_test.go
Normal file
17
internal/testing/binaryencoding/section_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func TestEncodeFunctionSection(t *testing.T) {
|
||||
require.Equal(t, []byte{wasm.SectionIDFunction, 0x2, 0x01, 0x05}, EncodeFunctionSection([]wasm.Index{5}))
|
||||
}
|
||||
|
||||
// TestEncodeStartSection uses the same index as TestEncodeFunctionSection to highlight the encoding is different.
|
||||
func TestEncodeStartSection(t *testing.T) {
|
||||
require.Equal(t, []byte{wasm.SectionIDStart, 0x01, 0x05}, EncodeStartSection(5))
|
||||
}
|
||||
12
internal/testing/binaryencoding/table.go
Normal file
12
internal/testing/binaryencoding/table.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// EncodeTable returns the wasm.Table encoded in WebAssembly 1.0 (20191205) Binary Format.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-table
|
||||
func EncodeTable(i *wasm.Table) []byte {
|
||||
return append([]byte{i.Type}, EncodeLimitsType(i.Min, i.Max)...)
|
||||
}
|
||||
41
internal/testing/binaryencoding/value.go
Normal file
41
internal/testing/binaryencoding/value.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
var noValType = []byte{0}
|
||||
|
||||
// encodedValTypes is a cache of size prefixed binary encoding of known val types.
|
||||
var encodedValTypes = map[wasm.ValueType][]byte{
|
||||
wasm.ValueTypeI32: {1, wasm.ValueTypeI32},
|
||||
wasm.ValueTypeI64: {1, wasm.ValueTypeI64},
|
||||
wasm.ValueTypeF32: {1, wasm.ValueTypeF32},
|
||||
wasm.ValueTypeF64: {1, wasm.ValueTypeF64},
|
||||
wasm.ValueTypeExternref: {1, wasm.ValueTypeExternref},
|
||||
wasm.ValueTypeFuncref: {1, wasm.ValueTypeFuncref},
|
||||
wasm.ValueTypeV128: {1, wasm.ValueTypeV128},
|
||||
}
|
||||
|
||||
// EncodeValTypes fast paths binary encoding of common value type lengths
|
||||
func EncodeValTypes(vt []wasm.ValueType) []byte {
|
||||
// Special case nullary and parameter lengths of wasi_snapshot_preview1 to avoid excess allocations
|
||||
switch uint32(len(vt)) {
|
||||
case 0: // nullary
|
||||
return noValType
|
||||
case 1: // ex $wasi.fd_close or any result
|
||||
if encoded, ok := encodedValTypes[vt[0]]; ok {
|
||||
return encoded
|
||||
}
|
||||
case 2: // ex $wasi.environ_sizes_get
|
||||
return []byte{2, vt[0], vt[1]}
|
||||
case 4: // ex $wasi.fd_write
|
||||
return []byte{4, vt[0], vt[1], vt[2], vt[3]}
|
||||
case 9: // ex $wasi.fd_write
|
||||
return []byte{9, vt[0], vt[1], vt[2], vt[3], vt[4], vt[5], vt[6], vt[7], vt[8]}
|
||||
}
|
||||
// Slow path others until someone complains with a valid signature
|
||||
count := leb128.EncodeUint32(uint32(len(vt)))
|
||||
return append(count, vt...)
|
||||
}
|
||||
112
internal/testing/binaryencoding/value_test.go
Normal file
112
internal/testing/binaryencoding/value_test.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package binaryencoding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
func TestEncodeValTypes(t *testing.T) {
|
||||
i32, i64, f32, f64, ext, fref := wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueTypeExternref, wasm.ValueTypeFuncref
|
||||
tests := []struct {
|
||||
name string
|
||||
input []wasm.ValueType
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: []wasm.ValueType{},
|
||||
expected: []byte{0},
|
||||
},
|
||||
{
|
||||
name: "undefined", // ensure future spec changes don't panic
|
||||
input: []wasm.ValueType{0x6f},
|
||||
expected: []byte{1, 0x6f},
|
||||
},
|
||||
{
|
||||
name: "funcref",
|
||||
input: []wasm.ValueType{fref},
|
||||
expected: []byte{1, fref},
|
||||
},
|
||||
{
|
||||
name: "externref",
|
||||
input: []wasm.ValueType{ext},
|
||||
expected: []byte{1, ext},
|
||||
},
|
||||
{
|
||||
name: "i32",
|
||||
input: []wasm.ValueType{i32},
|
||||
expected: []byte{1, i32},
|
||||
},
|
||||
{
|
||||
name: "i64",
|
||||
input: []wasm.ValueType{i64},
|
||||
expected: []byte{1, i64},
|
||||
},
|
||||
{
|
||||
name: "f32",
|
||||
input: []wasm.ValueType{f32},
|
||||
expected: []byte{1, f32},
|
||||
},
|
||||
{
|
||||
name: "f64",
|
||||
input: []wasm.ValueType{f64},
|
||||
expected: []byte{1, f64},
|
||||
},
|
||||
{
|
||||
name: "i32i64",
|
||||
input: []wasm.ValueType{i32, i64},
|
||||
expected: []byte{2, i32, i64},
|
||||
},
|
||||
{
|
||||
name: "i32i64f32",
|
||||
input: []wasm.ValueType{i32, i64, f32},
|
||||
expected: []byte{3, i32, i64, f32},
|
||||
},
|
||||
{
|
||||
name: "i32i64f32f64",
|
||||
input: []wasm.ValueType{i32, i64, f32, f64},
|
||||
expected: []byte{4, i32, i64, f32, f64},
|
||||
},
|
||||
{
|
||||
name: "i32i64f32f64i32",
|
||||
input: []wasm.ValueType{i32, i64, f32, f64, i32},
|
||||
expected: []byte{5, i32, i64, f32, f64, i32},
|
||||
},
|
||||
{
|
||||
name: "i32i64f32f64i32i64",
|
||||
input: []wasm.ValueType{i32, i64, f32, f64, i32, i64},
|
||||
expected: []byte{6, i32, i64, f32, f64, i32, i64},
|
||||
},
|
||||
{
|
||||
name: "i32i64f32f64i32i64f32",
|
||||
input: []wasm.ValueType{i32, i64, f32, f64, i32, i64, f32},
|
||||
expected: []byte{7, i32, i64, f32, f64, i32, i64, f32},
|
||||
},
|
||||
{
|
||||
name: "i32i64f32f64i32i64f32f64",
|
||||
input: []wasm.ValueType{i32, i64, f32, f64, i32, i64, f32, f64},
|
||||
expected: []byte{8, i32, i64, f32, f64, i32, i64, f32, f64},
|
||||
},
|
||||
{
|
||||
name: "i32i64f32f64i32i64f32f64i32",
|
||||
input: []wasm.ValueType{i32, i64, f32, f64, i32, i64, f32, f64, i32},
|
||||
expected: []byte{9, i32, i64, f32, f64, i32, i64, f32, f64, i32},
|
||||
},
|
||||
{
|
||||
name: "i32i64f32f64i32i64f32f64i32i64",
|
||||
input: []wasm.ValueType{i32, i64, f32, f64, i32, i64, f32, f64, i32, i64},
|
||||
expected: []byte{10, i32, i64, f32, f64, i32, i64, f32, f64, i32, i64},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bytes := EncodeValTypes(tc.input)
|
||||
require.Equal(t, tc.expected, bytes)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"github.com/tetratelabs/wazero/experimental"
|
||||
"github.com/tetratelabs/wazero/experimental/logging"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/testing/binaryencoding"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary"
|
||||
)
|
||||
|
||||
const proxyModuleName = "internal/testing/proxy/proxy.go"
|
||||
@@ -95,5 +95,5 @@ func NewModuleBinary(moduleName string, proxyTarget wazero.CompiledModule) []byt
|
||||
})
|
||||
cnt++
|
||||
}
|
||||
return binaryformat.EncodeModule(proxyModule)
|
||||
return binaryencoding.EncodeModule(proxyModule)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user