binary: complete encoder (#463)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
@@ -55,8 +55,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
HostFunctionSection: []*reflect.Value{&fnUint32_uint32},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
|
||||
@@ -74,8 +74,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
HostFunctionSection: []*reflect.Value{&fnUint64_uint32},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
|
||||
@@ -95,9 +95,9 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
},
|
||||
FunctionSection: []wasm.Index{0, 1},
|
||||
HostFunctionSection: []*reflect.Value{&fnUint32_uint32, &fnUint64_uint32},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
"2": {Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
{Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}},
|
||||
@@ -119,9 +119,9 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
},
|
||||
FunctionSection: []wasm.Index{0, 1},
|
||||
HostFunctionSection: []*reflect.Value{&fnUint32_uint32, &fnUint64_uint32},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
"2": {Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
{Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}},
|
||||
@@ -144,9 +144,9 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
},
|
||||
FunctionSection: []wasm.Index{0, 1},
|
||||
HostFunctionSection: []*reflect.Value{&fnUint32_uint32, &fnUint64_uint32},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
"2": {Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
{Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}},
|
||||
@@ -160,8 +160,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: wasm.MemoryMaxPages},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"memory": {Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -172,8 +172,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
MemorySection: &wasm.Memory{Min: 2, Max: wasm.MemoryMaxPages},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"memory": {Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -184,8 +184,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: 1},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"memory": {Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -196,8 +196,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: 2},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"memory": {Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -213,8 +213,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(1024)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"canvas_width": {Name: "canvas_width", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "canvas_width", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -230,8 +230,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeUint32(math.MaxInt32)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"canvas_width": {Name: "canvas_width", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "canvas_width", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -247,8 +247,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeUint64(1620216263544)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"start_epoch": {Name: "start_epoch", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "start_epoch", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -264,8 +264,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(math.MaxInt64)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"start_epoch": {Name: "start_epoch", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "start_epoch", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -281,8 +281,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: u64.LeBytes(api.EncodeF32(3.1415926536))},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"math/pi": {Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -298,8 +298,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: u64.LeBytes(api.EncodeF32(math.MaxFloat32))},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"math/pi": {Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -315,8 +315,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: u64.LeBytes(api.EncodeF64(math.Pi))},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"math/pi": {Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -332,8 +332,8 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: u64.LeBytes(api.EncodeF64(math.MaxFloat64))},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"math/pi": {Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -48,10 +48,17 @@ func decodeConstantExpression(r *bytes.Reader) (*wasm.ConstantExpression, error)
|
||||
return nil, fmt.Errorf("constant expression has been not terminated")
|
||||
}
|
||||
|
||||
data := make([]byte, remainingBeforeData-int64(r.Len()))
|
||||
data := make([]byte, remainingBeforeData-int64(r.Len())-1)
|
||||
if _, err := r.ReadAt(data, offsetAtData); err != nil {
|
||||
return nil, fmt.Errorf("error re-buffering ConstantExpression.Data")
|
||||
}
|
||||
|
||||
return &wasm.ConstantExpression{Opcode: opcode, Data: data}, nil
|
||||
}
|
||||
|
||||
func encodeConstantExpression(expr *wasm.ConstantExpression) (ret []byte) {
|
||||
ret = append(ret, expr.Opcode)
|
||||
ret = append(ret, expr.Data...)
|
||||
ret = append(ret, wasm.OpcodeEnd)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -39,3 +39,12 @@ func decodeDataSegment(r *bytes.Reader) (*wasm.DataSegment, error) {
|
||||
Init: b,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func TestDecodeModule(t *testing.T) {
|
||||
name: "table and memory section",
|
||||
input: &wasm.Module{
|
||||
TableSection: &wasm.Table{Min: 3},
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: 1},
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: 1, IsMaxEncoded: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -38,3 +38,17 @@ func decodeElementSegment(r *bytes.Reader) (*wasm.ElementSegment, error) {
|
||||
|
||||
return &wasm.ElementSegment{OffsetExpr: expr, Init: init}, 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) {
|
||||
// Currently multiple tables are not supported.
|
||||
ret = append(ret, leb128.EncodeInt32(0)...)
|
||||
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))...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -40,13 +40,13 @@ func EncodeModule(m *wasm.Module) (bytes []byte) {
|
||||
bytes = append(bytes, encodeStartSection(*m.StartSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDElement) > 0 {
|
||||
panic("TODO: ElementSection")
|
||||
bytes = append(bytes, encodeElementSection(m.ElementSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDCode) > 0 {
|
||||
bytes = append(bytes, encodeCodeSection(m.CodeSection)...)
|
||||
}
|
||||
if m.SectionElementCount(wasm.SectionIDData) > 0 {
|
||||
panic("TODO: DataSection")
|
||||
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.
|
||||
|
||||
@@ -109,7 +109,7 @@ func TestModule_Encode(t *testing.T) {
|
||||
name: "table and memory section",
|
||||
input: &wasm.Module{
|
||||
TableSection: &wasm.Table{Min: 3},
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: 1},
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: 1, IsMaxEncoded: true},
|
||||
},
|
||||
expected: append(append(Magic, version...),
|
||||
wasm.SectionIDTable, 0x04, // 4 bytes in this section
|
||||
@@ -130,8 +130,8 @@ func TestModule_Encode(t *testing.T) {
|
||||
CodeSection: []*wasm.Code{
|
||||
{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeI32Add, wasm.OpcodeEnd}},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"AddInt": {Name: "AddInt", Type: wasm.ExternTypeFunc, Index: wasm.Index(0)},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "AddInt", Type: wasm.ExternTypeFunc, Index: wasm.Index(0)},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "addInt"}},
|
||||
@@ -183,8 +183,8 @@ func TestModule_Encode(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(0)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"sp": {Name: "sp", Type: wasm.ExternTypeGlobal, Index: wasm.Index(0)},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "sp", Type: wasm.ExternTypeGlobal, Index: wasm.Index(0)},
|
||||
},
|
||||
},
|
||||
expected: append(append(Magic, version...),
|
||||
|
||||
@@ -55,12 +55,12 @@ func decodeGlobalType(r *bytes.Reader) (*wasm.GlobalType, error) {
|
||||
// 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) []byte {
|
||||
func encodeGlobal(g *wasm.Global) (data []byte) {
|
||||
var mutable byte
|
||||
if g.Type.Mutable {
|
||||
mutable = 1
|
||||
}
|
||||
data := []byte{g.Type.ValType, mutable, g.Init.Opcode}
|
||||
data = append(data, g.Init.Data...)
|
||||
return append(data, wasm.OpcodeEnd)
|
||||
data = []byte{g.Type.ValType, mutable}
|
||||
data = append(data, encodeConstantExpression(g.Init)...)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -52,9 +52,14 @@ func encodeImport(i *wasm.Import) []byte {
|
||||
case wasm.ExternTypeFunc:
|
||||
data = append(data, leb128.EncodeUint32(i.DescFunc)...)
|
||||
case wasm.ExternTypeTable:
|
||||
panic("TODO: encodeExternTypeTable")
|
||||
data = append(data, wasm.ElemTypeFuncref)
|
||||
data = append(data, encodeLimitsType(i.DescTable.Min, i.DescTable.Max)...)
|
||||
case wasm.ExternTypeMemory:
|
||||
panic("TODO: encodeExternTypeMemory")
|
||||
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
|
||||
|
||||
@@ -8,6 +8,10 @@ import (
|
||||
)
|
||||
|
||||
func TestEncodeImport(t *testing.T) {
|
||||
ptrOfUint32 := func(v uint32) *uint32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input *wasm.Import
|
||||
@@ -98,6 +102,52 @@ func TestEncodeImport(t *testing.T) {
|
||||
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.ElemTypeFuncref,
|
||||
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.MemoryMaxPages, 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 {
|
||||
|
||||
@@ -15,8 +15,11 @@ func decodeMemory(r *bytes.Reader, memoryMaxPages uint32) (*wasm.Memory, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var max uint32
|
||||
var isMaxEncoded bool
|
||||
if maxP != nil {
|
||||
isMaxEncoded = true
|
||||
max = *maxP
|
||||
} else {
|
||||
max = memoryMaxPages
|
||||
@@ -28,12 +31,16 @@ func decodeMemory(r *bytes.Reader, memoryMaxPages uint32) (*wasm.Memory, error)
|
||||
} else if min > max {
|
||||
return nil, fmt.Errorf("min %d pages (%s) > max %d pages (%s)", min, wasm.PagesToUnitOfBytes(min), max, wasm.PagesToUnitOfBytes(max))
|
||||
}
|
||||
return &wasm.Memory{Min: min, Max: max}, nil
|
||||
return &wasm.Memory{Min: min, Max: max, IsMaxEncoded: isMaxEncoded}, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return encodeLimitsType(i.Min, &i.Max)
|
||||
maxPtr := &i.Max
|
||||
if !i.IsMaxEncoded {
|
||||
maxPtr = nil
|
||||
}
|
||||
return encodeLimitsType(i.Min, maxPtr)
|
||||
}
|
||||
|
||||
@@ -20,27 +20,32 @@ func TestMemoryType(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "min 0",
|
||||
input: &wasm.Memory{Max: wasm.MemoryMaxPages},
|
||||
input: &wasm.Memory{Max: wasm.MemoryMaxPages, IsMaxEncoded: true},
|
||||
expected: []byte{0x1, 0, 0x80, 0x80, 0x4},
|
||||
},
|
||||
{
|
||||
name: "min 0 - default max",
|
||||
input: &wasm.Memory{Max: wasm.MemoryMaxPages},
|
||||
expected: []byte{0x0, 0},
|
||||
},
|
||||
{
|
||||
name: "min 0, max 0",
|
||||
input: &wasm.Memory{Max: zero},
|
||||
input: &wasm.Memory{Max: zero, IsMaxEncoded: true},
|
||||
expected: []byte{0x1, 0, 0},
|
||||
},
|
||||
{
|
||||
name: "min=max",
|
||||
input: &wasm.Memory{Min: 1, Max: 1},
|
||||
input: &wasm.Memory{Min: 1, Max: 1, IsMaxEncoded: true},
|
||||
expected: []byte{0x1, 1, 1},
|
||||
},
|
||||
{
|
||||
name: "min 0, max largest",
|
||||
input: &wasm.Memory{Max: max},
|
||||
input: &wasm.Memory{Max: max, IsMaxEncoded: true},
|
||||
expected: []byte{0x1, 0, 0x80, 0x80, 0x4},
|
||||
},
|
||||
{
|
||||
name: "min largest max largest",
|
||||
input: &wasm.Memory{Min: max, Max: max},
|
||||
input: &wasm.Memory{Min: max, Max: max, IsMaxEncoded: true},
|
||||
expected: []byte{0x1, 0x80, 0x80, 0x4, 0x80, 0x80, 0x4},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -92,22 +92,25 @@ func decodeGlobalSection(r *bytes.Reader) ([]*wasm.Global, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func decodeExportSection(r *bytes.Reader) (map[string]*wasm.Export, error) {
|
||||
func decodeExportSection(r *bytes.Reader) ([]*wasm.Export, error) {
|
||||
vs, _, sizeErr := leb128.DecodeUint32(r)
|
||||
if sizeErr != nil {
|
||||
return nil, fmt.Errorf("get size of vector: %v", sizeErr)
|
||||
}
|
||||
|
||||
exportSection := make(map[string]*wasm.Export, vs)
|
||||
usedName := make(map[string]struct{}, vs)
|
||||
exportSection := make([]*wasm.Export, 0, vs)
|
||||
for i := wasm.Index(0); i < vs; i++ {
|
||||
export, err := decodeExport(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read export: %w", err)
|
||||
}
|
||||
if _, ok := exportSection[export.Name]; ok {
|
||||
if _, ok := usedName[export.Name]; ok {
|
||||
return nil, fmt.Errorf("export[%d] duplicates name %q", i, export.Name)
|
||||
} else {
|
||||
usedName[export.Name] = struct{}{}
|
||||
}
|
||||
exportSection[export.Name] = export
|
||||
exportSection = append(exportSection, export)
|
||||
}
|
||||
return exportSection, nil
|
||||
}
|
||||
@@ -260,7 +263,7 @@ func encodeGlobalSection(globals []*wasm.Global) []byte {
|
||||
//
|
||||
// See encodeExport
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#export-section%E2%91%A0
|
||||
func encodeExportSection(exports map[string]*wasm.Export) []byte {
|
||||
func encodeExportSection(exports []*wasm.Export) []byte {
|
||||
contents := leb128.EncodeUint32(uint32(len(exports)))
|
||||
for _, e := range exports {
|
||||
contents = append(contents, encodeExport(e)...)
|
||||
@@ -275,3 +278,27 @@ func encodeExportSection(exports map[string]*wasm.Export) []byte {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestMemorySection(t *testing.T) {
|
||||
0x01, // 1 memory
|
||||
0x01, 0x02, 0x03, // (memory 2 3)
|
||||
},
|
||||
expected: &wasm.Memory{Min: 2, Max: three},
|
||||
expected: &wasm.Memory{Min: 2, Max: three, IsMaxEncoded: true},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func TestDecodeExportSection(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input []byte
|
||||
expected map[string]*wasm.Export
|
||||
expected []*wasm.Export
|
||||
}{
|
||||
{
|
||||
name: "empty and non-empty name",
|
||||
@@ -138,9 +138,9 @@ func TestDecodeExportSection(t *testing.T) {
|
||||
0x01, 'a', // Size of name, name
|
||||
wasm.ExternTypeFunc, 0x01, // func[1]
|
||||
},
|
||||
expected: map[string]*wasm.Export{
|
||||
"": {Name: "", Type: wasm.ExternTypeFunc, Index: wasm.Index(2)},
|
||||
"a": {Name: "a", Type: wasm.ExternTypeFunc, Index: wasm.Index(1)},
|
||||
expected: []*wasm.Export{
|
||||
{Name: "", Type: wasm.ExternTypeFunc, Index: wasm.Index(2)},
|
||||
{Name: "a", Type: wasm.ExternTypeFunc, Index: wasm.Index(1)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -270,8 +270,8 @@ func TestModule_SectionElementCount(t *testing.T) {
|
||||
CodeSection: []*Code{
|
||||
{Body: []byte{OpcodeLocalGet, 0, OpcodeLocalGet, 1, OpcodeI32Add, OpcodeEnd}},
|
||||
},
|
||||
ExportSection: map[string]*Export{
|
||||
"AddInt": {Name: "AddInt", Type: ExternTypeFunc, Index: Index(0)},
|
||||
ExportSection: []*Export{
|
||||
{Name: "AddInt", Type: ExternTypeFunc, Index: Index(0)},
|
||||
},
|
||||
StartSection: &zero,
|
||||
},
|
||||
|
||||
@@ -170,7 +170,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
ExportSection: []*Export{{Type: ExternTypeGlobal, Name: "global"}},
|
||||
},
|
||||
expected: globalI32(1),
|
||||
},
|
||||
@@ -183,7 +183,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
Init: &ConstantExpression{Opcode: OpcodeI64Const, Data: leb128.EncodeInt64(1)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
ExportSection: []*Export{{Type: ExternTypeGlobal, Name: "global"}},
|
||||
},
|
||||
expected: globalI64(1),
|
||||
},
|
||||
@@ -198,7 +198,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
ExportSection: []*Export{{Type: ExternTypeGlobal, Name: "global"}},
|
||||
},
|
||||
expected: globalF32(api.EncodeF32(1.0)),
|
||||
},
|
||||
@@ -213,7 +213,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
ExportSection: []*Export{{Type: ExternTypeGlobal, Name: "global"}},
|
||||
},
|
||||
expected: globalF64(api.EncodeF64(1.0)),
|
||||
},
|
||||
@@ -226,7 +226,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(1)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
ExportSection: []*Export{{Type: ExternTypeGlobal, Name: "global"}},
|
||||
},
|
||||
expected: &mutableGlobal{
|
||||
g: &GlobalInstance{Type: &GlobalType{ValType: ValueTypeI32, Mutable: true}, Val: 1},
|
||||
@@ -241,7 +241,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
Init: &ConstantExpression{Opcode: OpcodeI64Const, Data: leb128.EncodeInt64(1)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
ExportSection: []*Export{{Type: ExternTypeGlobal, Name: "global"}},
|
||||
},
|
||||
expected: &mutableGlobal{
|
||||
g: &GlobalInstance{Type: &GlobalType{ValType: ValueTypeI64, Mutable: true}, Val: 1},
|
||||
@@ -258,7 +258,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
ExportSection: []*Export{{Type: ExternTypeGlobal, Name: "global"}},
|
||||
},
|
||||
expected: &mutableGlobal{
|
||||
g: &GlobalInstance{Type: &GlobalType{ValType: ValueTypeF32, Mutable: true}, Val: api.EncodeF32(1.0)},
|
||||
@@ -275,7 +275,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
ExportSection: []*Export{{Type: ExternTypeGlobal, Name: "global"}},
|
||||
},
|
||||
expected: &mutableGlobal{
|
||||
g: &GlobalInstance{Type: &GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(1.0)},
|
||||
|
||||
@@ -28,7 +28,22 @@ func NewHostModule(
|
||||
globalCount := uint32(len(nameToGlobal))
|
||||
exportCount := funcCount + memoryCount + globalCount
|
||||
if exportCount > 0 {
|
||||
m.ExportSection = make(map[string]*Export, exportCount)
|
||||
m.ExportSection = make([]*Export, 0, exportCount)
|
||||
}
|
||||
|
||||
// Check name collision as exports cannot collide on names, regardless of type.
|
||||
for name := range nameToGoFunc {
|
||||
if _, ok := nameToMemory[name]; ok {
|
||||
return nil, fmt.Errorf("func[%s] exports the same name as a memory", name)
|
||||
}
|
||||
if _, ok := nameToGlobal[name]; ok {
|
||||
return nil, fmt.Errorf("func[%s] exports the same name as a global", name)
|
||||
}
|
||||
}
|
||||
for name := range nameToMemory {
|
||||
if _, ok := nameToGlobal[name]; ok {
|
||||
return nil, fmt.Errorf("memory[%s] exports the same name as a global", name)
|
||||
}
|
||||
}
|
||||
|
||||
if funcCount > 0 {
|
||||
@@ -78,7 +93,7 @@ func addFuncs(m *Module, nameToGoFunc map[string]interface{}, enabledFeatures Fe
|
||||
|
||||
m.FunctionSection = append(m.FunctionSection, m.maybeAddType(functionType))
|
||||
m.HostFunctionSection = append(m.HostFunctionSection, &fn)
|
||||
m.ExportSection[name] = &Export{Type: ExternTypeFunc, Name: name, Index: idx}
|
||||
m.ExportSection = append(m.ExportSection, &Export{Type: ExternTypeFunc, Name: name, Index: idx})
|
||||
m.NameSection.FunctionNames = append(m.NameSection.FunctionNames, &NameAssoc{Index: idx, Name: name})
|
||||
}
|
||||
return nil
|
||||
@@ -107,11 +122,7 @@ func addMemory(m *Module, nameToMemory map[string]*Memory) error {
|
||||
m.MemorySection = v
|
||||
}
|
||||
|
||||
if e, ok := m.ExportSection[name]; ok { // Exports cannot collide on names, regardless of type.
|
||||
return fmt.Errorf("memory[%s] exports the same name as a %s", name, ExternTypeName(e.Type))
|
||||
}
|
||||
|
||||
m.ExportSection[name] = &Export{Type: ExternTypeMemory, Name: name, Index: 0}
|
||||
m.ExportSection = append(m.ExportSection, &Export{Type: ExternTypeMemory, Name: name, Index: 0})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -121,16 +132,13 @@ func addGlobals(m *Module, globals map[string]*Global) error {
|
||||
|
||||
globalNames := make([]string, 0, globalCount)
|
||||
for name := range globals {
|
||||
if e, ok := m.ExportSection[name]; ok { // Exports cannot collide on names, regardless of type.
|
||||
return fmt.Errorf("global[%s] exports the same name as a %s", name, ExternTypeName(e.Type))
|
||||
}
|
||||
globalNames = append(globalNames, name)
|
||||
}
|
||||
sort.Strings(globalNames) // For consistent iteration order
|
||||
|
||||
for i, name := range globalNames {
|
||||
m.GlobalSection = append(m.GlobalSection, globals[name])
|
||||
m.ExportSection[name] = &Export{Type: ExternTypeGlobal, Name: name, Index: Index(i)}
|
||||
m.ExportSection = append(m.ExportSection, &Export{Type: ExternTypeGlobal, Name: name, Index: Index(i)})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -70,9 +70,9 @@ func TestNewHostModule(t *testing.T) {
|
||||
},
|
||||
FunctionSection: []Index{0, 1},
|
||||
HostFunctionSection: []*reflect.Value{&fnArgsSizesGet, &fnFdWrite},
|
||||
ExportSection: map[string]*Export{
|
||||
"args_sizes_get": {Name: "args_sizes_get", Type: ExternTypeFunc, Index: 0},
|
||||
"fd_write": {Name: "fd_write", Type: ExternTypeFunc, Index: 1},
|
||||
ExportSection: []*Export{
|
||||
{Name: "args_sizes_get", Type: ExternTypeFunc, Index: 0},
|
||||
{Name: "fd_write", Type: ExternTypeFunc, Index: 1},
|
||||
},
|
||||
NameSection: &NameSection{
|
||||
ModuleName: "wasi_snapshot_preview1",
|
||||
@@ -93,18 +93,16 @@ func TestNewHostModule(t *testing.T) {
|
||||
TypeSection: []*FunctionType{{Params: []ValueType{i32, i32}, Results: []ValueType{i32, i32}}},
|
||||
FunctionSection: []Index{0},
|
||||
HostFunctionSection: []*reflect.Value{&fnSwap},
|
||||
ExportSection: map[string]*Export{"swap": {Name: "swap", Type: ExternTypeFunc, Index: 0}},
|
||||
ExportSection: []*Export{{Name: "swap", Type: ExternTypeFunc, Index: 0}},
|
||||
NameSection: &NameSection{ModuleName: "swapper", FunctionNames: NameMap{{Index: 0, Name: "swap"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory",
|
||||
nameToMemory: map[string]*Memory{"memory": {1, 2}},
|
||||
nameToMemory: map[string]*Memory{"memory": {Min: 1, Max: 2}},
|
||||
expected: &Module{
|
||||
MemorySection: &Memory{Min: 1, Max: 2},
|
||||
ExportSection: map[string]*Export{
|
||||
"memory": {Name: "memory", Type: ExternTypeMemory, Index: 0},
|
||||
},
|
||||
ExportSection: []*Export{{Name: "memory", Type: ExternTypeMemory, Index: 0}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -130,9 +128,9 @@ func TestNewHostModule(t *testing.T) {
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{
|
||||
"g1": {Name: "g1", Type: ExternTypeGlobal, Index: 0},
|
||||
"g2": {Name: "g2", Type: ExternTypeGlobal, Index: 1},
|
||||
ExportSection: []*Export{
|
||||
{Name: "g1", Type: ExternTypeGlobal, Index: 0},
|
||||
{Name: "g2", Type: ExternTypeGlobal, Index: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -143,7 +141,7 @@ func TestNewHostModule(t *testing.T) {
|
||||
functionArgsSizesGet: a.ArgsSizesGet,
|
||||
},
|
||||
nameToMemory: map[string]*Memory{
|
||||
"memory": {1, 1},
|
||||
"memory": {Min: 1, Max: 1},
|
||||
},
|
||||
nameToGlobal: map[string]*Global{
|
||||
"g": {
|
||||
@@ -164,10 +162,10 @@ func TestNewHostModule(t *testing.T) {
|
||||
},
|
||||
},
|
||||
MemorySection: &Memory{Min: 1, Max: 1},
|
||||
ExportSection: map[string]*Export{
|
||||
"args_sizes_get": {Name: "args_sizes_get", Type: ExternTypeFunc, Index: 0},
|
||||
"memory": {Name: "memory", Type: ExternTypeMemory, Index: 0},
|
||||
"g": {Name: "g", Type: ExternTypeGlobal, Index: 0},
|
||||
ExportSection: []*Export{
|
||||
{Name: "args_sizes_get", Type: ExternTypeFunc, Index: 0},
|
||||
{Name: "memory", Type: ExternTypeMemory, Index: 0},
|
||||
{Name: "g", Type: ExternTypeGlobal, Index: 0},
|
||||
},
|
||||
NameSection: &NameSection{
|
||||
ModuleName: "env",
|
||||
@@ -234,30 +232,30 @@ func TestNewHostModule_Errors(t *testing.T) {
|
||||
{
|
||||
name: "function has multiple results",
|
||||
nameToGoFunc: map[string]interface{}{"fn": func() (uint32, uint32) { return 0, 0 }},
|
||||
nameToMemory: map[string]*Memory{"fn": {1, 1}},
|
||||
nameToMemory: map[string]*Memory{"mem": {Min: 1, Max: 1}},
|
||||
expectedErr: "func[fn] multiple result types invalid as feature \"multi-value\" is disabled",
|
||||
},
|
||||
{
|
||||
name: "memory collides on func name",
|
||||
name: "func collides on memory name",
|
||||
nameToGoFunc: map[string]interface{}{"fn": ArgsSizesGet},
|
||||
nameToMemory: map[string]*Memory{"fn": {1, 1}},
|
||||
expectedErr: "memory[fn] exports the same name as a func",
|
||||
nameToMemory: map[string]*Memory{"fn": {Min: 1, Max: 1}},
|
||||
expectedErr: "func[fn] exports the same name as a memory",
|
||||
},
|
||||
{
|
||||
name: "multiple memories",
|
||||
nameToMemory: map[string]*Memory{"memory": {1, 1}, "mem": {2, 2}},
|
||||
nameToMemory: map[string]*Memory{"memory": {Min: 1, Max: 1}, "mem": {Min: 2, Max: 2}},
|
||||
expectedErr: "only one memory is allowed, but configured: mem, memory",
|
||||
},
|
||||
{
|
||||
name: "memory max < min",
|
||||
nameToMemory: map[string]*Memory{"memory": {1, 0}},
|
||||
nameToMemory: map[string]*Memory{"memory": {Min: 1, Max: 0}},
|
||||
expectedErr: "memory[memory] min 1 pages (64 Ki) > max 0 pages (0 Ki)",
|
||||
},
|
||||
{
|
||||
name: "global collides on func name",
|
||||
name: "func collides on global name",
|
||||
nameToGoFunc: map[string]interface{}{"fn": ArgsSizesGet},
|
||||
nameToGlobal: map[string]*Global{"fn": {}},
|
||||
expectedErr: "global[fn] exports the same name as a func",
|
||||
expectedErr: "func[fn] exports the same name as a global",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -436,9 +436,9 @@ func TestJIT_SliceAllocatedOnHeap(t *testing.T) {
|
||||
{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}},
|
||||
},
|
||||
ImportSection: []*wasm.Import{{Module: hostModuleName, Name: hostFnName, DescFunc: 1}},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
valueStackCorruption: {Type: wasm.ExternTypeFunc, Index: 1, Name: valueStackCorruption},
|
||||
callStackCorruption: {Type: wasm.ExternTypeFunc, Index: 2, Name: callStackCorruption},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Type: wasm.ExternTypeFunc, Index: 1, Name: valueStackCorruption},
|
||||
{Type: wasm.ExternTypeFunc, Index: 2, Name: callStackCorruption},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ type Module struct {
|
||||
// Note: In the Binary Format, this is SectionIDExport.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#exports%E2%91%A0
|
||||
ExportSection map[string]*Export
|
||||
ExportSection []*Export
|
||||
|
||||
// StartSection is the index of a function to call before returning from Store.Instantiate.
|
||||
//
|
||||
@@ -350,30 +350,30 @@ func (m *Module) validateImports(enabledFeatures Features) error {
|
||||
}
|
||||
|
||||
func (m *Module) validateExports(enabledFeatures Features, functions []Index, globals []*GlobalType, memory *Memory, table *Table) error {
|
||||
for name, exp := range m.ExportSection {
|
||||
for _, exp := range m.ExportSection {
|
||||
index := exp.Index
|
||||
switch exp.Type {
|
||||
case ExternTypeFunc:
|
||||
if index >= uint32(len(functions)) {
|
||||
return fmt.Errorf("unknown function for export[%q]", name)
|
||||
return fmt.Errorf("unknown function for export[%q]", exp.Name)
|
||||
}
|
||||
case ExternTypeGlobal:
|
||||
if index >= uint32(len(globals)) {
|
||||
return fmt.Errorf("unknown global for export[%q]", name)
|
||||
return fmt.Errorf("unknown global for export[%q]", exp.Name)
|
||||
}
|
||||
if !globals[index].Mutable {
|
||||
continue
|
||||
}
|
||||
if err := enabledFeatures.Require(FeatureMutableGlobal); err != nil {
|
||||
return fmt.Errorf("invalid export[%q] global[%d]: %w", name, index, err)
|
||||
return fmt.Errorf("invalid export[%q] global[%d]: %w", exp.Name, index, err)
|
||||
}
|
||||
case ExternTypeMemory:
|
||||
if index > 0 || memory == nil {
|
||||
return fmt.Errorf("memory for export[%q] out of range", name)
|
||||
return fmt.Errorf("memory for export[%q] out of range", exp.Name)
|
||||
}
|
||||
case ExternTypeTable:
|
||||
if index > 0 || table == nil {
|
||||
return fmt.Errorf("table for export[%q] out of range", name)
|
||||
return fmt.Errorf("table for export[%q] out of range", exp.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -582,6 +582,8 @@ type limitsType struct {
|
||||
// Memory describes the limits of pages (64KB) in a memory.
|
||||
type Memory struct {
|
||||
Min, Max uint32
|
||||
// IsMaxEncoded true if the Max is encoded in the orignial source (binary or text).
|
||||
IsMaxEncoded bool
|
||||
}
|
||||
|
||||
type GlobalType struct {
|
||||
|
||||
@@ -444,7 +444,7 @@ func TestModule_validateFunctions(t *testing.T) {
|
||||
TypeSection: []*FunctionType{{}},
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{{Body: []byte{OpcodeF32Abs}}},
|
||||
ExportSection: map[string]*Export{"f1": {Name: "f1", Type: ExternTypeFunc, Index: 0}},
|
||||
ExportSection: []*Export{{Name: "f1", Type: ExternTypeFunc, Index: 0}},
|
||||
}
|
||||
err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
|
||||
require.Error(t, err)
|
||||
@@ -456,7 +456,7 @@ func TestModule_validateFunctions(t *testing.T) {
|
||||
ImportSection: []*Import{{Type: ExternTypeFunc}},
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{{Body: []byte{OpcodeF32Abs}}},
|
||||
ExportSection: map[string]*Export{"f1": {Name: "f1", Type: ExternTypeFunc, Index: 1}},
|
||||
ExportSection: []*Export{{Name: "f1", Type: ExternTypeFunc, Index: 1}},
|
||||
}
|
||||
err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
|
||||
require.Error(t, err)
|
||||
@@ -467,9 +467,9 @@ func TestModule_validateFunctions(t *testing.T) {
|
||||
TypeSection: []*FunctionType{{}},
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{{Body: []byte{OpcodeF32Abs}}},
|
||||
ExportSection: map[string]*Export{
|
||||
"f1": {Name: "f1", Type: ExternTypeFunc, Index: 0},
|
||||
"f2": {Name: "f2", Type: ExternTypeFunc, Index: 0},
|
||||
ExportSection: []*Export{
|
||||
{Name: "f1", Type: ExternTypeFunc, Index: 0},
|
||||
{Name: "f2", Type: ExternTypeFunc, Index: 0},
|
||||
},
|
||||
}
|
||||
err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
|
||||
@@ -572,78 +572,78 @@ func TestModule_validateExports(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
enabledFeatures Features
|
||||
exportSection map[string]*Export
|
||||
exportSection []*Export
|
||||
functions []Index
|
||||
globals []*GlobalType
|
||||
memory *Memory
|
||||
table *Table
|
||||
expectedErr string
|
||||
}{
|
||||
{name: "empty export section", exportSection: map[string]*Export{}},
|
||||
{name: "empty export section", exportSection: []*Export{}},
|
||||
{
|
||||
name: "func",
|
||||
enabledFeatures: Features20191205,
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeFunc, Index: 0}},
|
||||
exportSection: []*Export{{Type: ExternTypeFunc, Index: 0}},
|
||||
functions: []Index{100 /* arbitrary type id*/},
|
||||
},
|
||||
{
|
||||
name: "func out of range",
|
||||
enabledFeatures: Features20191205,
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeFunc, Index: 1}},
|
||||
exportSection: []*Export{{Type: ExternTypeFunc, Index: 1, Name: "e"}},
|
||||
functions: []Index{100 /* arbitrary type id*/},
|
||||
expectedErr: `unknown function for export["e1"]`,
|
||||
expectedErr: `unknown function for export["e"]`,
|
||||
},
|
||||
{
|
||||
name: "global const",
|
||||
enabledFeatures: Features20191205,
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeGlobal, Index: 0}},
|
||||
exportSection: []*Export{{Type: ExternTypeGlobal, Index: 0}},
|
||||
globals: []*GlobalType{{ValType: ValueTypeI32}},
|
||||
},
|
||||
{
|
||||
name: "global var",
|
||||
enabledFeatures: Features20191205,
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeGlobal, Index: 0}},
|
||||
exportSection: []*Export{{Type: ExternTypeGlobal, Index: 0}},
|
||||
globals: []*GlobalType{{ValType: ValueTypeI32, Mutable: true}},
|
||||
},
|
||||
{
|
||||
name: "global var disabled",
|
||||
enabledFeatures: Features20191205.Set(FeatureMutableGlobal, false),
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeGlobal, Index: 0}},
|
||||
exportSection: []*Export{{Type: ExternTypeGlobal, Index: 0, Name: "e"}},
|
||||
globals: []*GlobalType{{ValType: ValueTypeI32, Mutable: true}},
|
||||
expectedErr: `invalid export["e1"] global[0]: feature "mutable-global" is disabled`,
|
||||
expectedErr: `invalid export["e"] global[0]: feature "mutable-global" is disabled`,
|
||||
},
|
||||
{
|
||||
name: "global out of range",
|
||||
enabledFeatures: Features20191205,
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeGlobal, Index: 1}},
|
||||
exportSection: []*Export{{Type: ExternTypeGlobal, Index: 1, Name: "e"}},
|
||||
globals: []*GlobalType{{}},
|
||||
expectedErr: `unknown global for export["e1"]`,
|
||||
expectedErr: `unknown global for export["e"]`,
|
||||
},
|
||||
{
|
||||
name: "table",
|
||||
enabledFeatures: Features20191205,
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeTable, Index: 0}},
|
||||
exportSection: []*Export{{Type: ExternTypeTable, Index: 0}},
|
||||
table: &Table{},
|
||||
},
|
||||
{
|
||||
name: "table out of range",
|
||||
enabledFeatures: Features20191205,
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeTable, Index: 1}},
|
||||
exportSection: []*Export{{Type: ExternTypeTable, Index: 1, Name: "e"}},
|
||||
table: &Table{},
|
||||
expectedErr: `table for export["e1"] out of range`,
|
||||
expectedErr: `table for export["e"] out of range`,
|
||||
},
|
||||
{
|
||||
name: "memory",
|
||||
enabledFeatures: Features20191205,
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeMemory, Index: 0}},
|
||||
exportSection: []*Export{{Type: ExternTypeMemory, Index: 0}},
|
||||
memory: &Memory{},
|
||||
},
|
||||
{
|
||||
name: "memory out of range",
|
||||
enabledFeatures: Features20191205,
|
||||
exportSection: map[string]*Export{"e1": {Type: ExternTypeMemory, Index: 0}},
|
||||
exportSection: []*Export{{Type: ExternTypeMemory, Index: 0, Name: "e"}},
|
||||
table: &limitsType{},
|
||||
expectedErr: `memory for export["e1"] out of range`,
|
||||
expectedErr: `memory for export["e"] out of range`,
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
|
||||
@@ -176,7 +176,7 @@ func (m *ModuleInstance) addSections(module *Module, importedFunctions, function
|
||||
m.buildExports(module.ExportSection)
|
||||
}
|
||||
|
||||
func (m *ModuleInstance) buildExports(exports map[string]*Export) {
|
||||
func (m *ModuleInstance) buildExports(exports []*Export) {
|
||||
m.Exports = make(map[string]*ExportInstance, len(exports))
|
||||
for _, exp := range exports {
|
||||
index := exp.Index
|
||||
|
||||
@@ -38,14 +38,14 @@ func TestModuleInstance_Memory(t *testing.T) {
|
||||
name: "memory exported, different name",
|
||||
input: &Module{
|
||||
MemorySection: &Memory{Min: 1},
|
||||
ExportSection: map[string]*Export{"momory": {Type: ExternTypeMemory, Name: "momory", Index: 0}},
|
||||
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "momory", Index: 0}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory exported, but zero length",
|
||||
input: &Module{
|
||||
MemorySection: &Memory{},
|
||||
ExportSection: map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory", Index: 0}},
|
||||
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
@@ -53,7 +53,7 @@ func TestModuleInstance_Memory(t *testing.T) {
|
||||
name: "memory exported, one page",
|
||||
input: &Module{
|
||||
MemorySection: &Memory{Min: 1},
|
||||
ExportSection: map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory", Index: 0}},
|
||||
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
|
||||
},
|
||||
expected: true,
|
||||
expectedLen: 65536,
|
||||
@@ -62,7 +62,7 @@ func TestModuleInstance_Memory(t *testing.T) {
|
||||
name: "memory exported, two pages",
|
||||
input: &Module{
|
||||
MemorySection: &Memory{Min: 2},
|
||||
ExportSection: map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory", Index: 0}},
|
||||
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
|
||||
},
|
||||
expected: true,
|
||||
expectedLen: 65536 * 2,
|
||||
@@ -145,7 +145,7 @@ func TestStore_CloseModule(t *testing.T) {
|
||||
TypeSection: []*FunctionType{{}},
|
||||
FunctionSection: []uint32{0},
|
||||
CodeSection: []*Code{{Body: []byte{OpcodeEnd}}},
|
||||
ExportSection: map[string]*Export{"fn": {Type: ExternTypeFunc, Index: 0, Name: "fn"}},
|
||||
ExportSection: []*Export{{Type: ExternTypeFunc, Index: 0, Name: "fn"}},
|
||||
}, importedModuleName, nil)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
@@ -357,7 +357,7 @@ func TestModuleContext_ExportedFunction(t *testing.T) {
|
||||
TypeSection: []*FunctionType{{}},
|
||||
ImportSection: []*Import{{Type: ExternTypeFunc, Module: "host", Name: "host_fn", DescFunc: 0}},
|
||||
MemorySection: &Memory{Min: 1},
|
||||
ExportSection: map[string]*Export{"host.fn": {Type: ExternTypeFunc, Name: "host.fn", Index: 0}},
|
||||
ExportSection: []*Export{{Type: ExternTypeFunc, Name: "host.fn", Index: 0}},
|
||||
}, "test", nil)
|
||||
require.NoError(t, err)
|
||||
defer importing.Close()
|
||||
@@ -401,7 +401,7 @@ func TestFunctionInstance_Call(t *testing.T) {
|
||||
DescFunc: 0,
|
||||
}},
|
||||
MemorySection: &Memory{Min: 1},
|
||||
ExportSection: map[string]*Export{functionName: {Type: ExternTypeFunc, Name: functionName, Index: 0}},
|
||||
ExportSection: []*Export{{Type: ExternTypeFunc, Name: functionName, Index: 0}},
|
||||
}, "test", nil)
|
||||
require.NoError(t, err)
|
||||
defer imported.Close()
|
||||
|
||||
@@ -98,6 +98,8 @@ type moduleParser struct {
|
||||
// field counts can be different from the count in a section when abbreviated imports exist. To give an accurate
|
||||
// errorContext, we count explicitly.
|
||||
fieldCountFunc uint32
|
||||
|
||||
exportedName map[string]struct{}
|
||||
}
|
||||
|
||||
// DecodeModule implements wasm.DecodeModule for the WebAssembly 1.0 (20191205) Text Format
|
||||
@@ -449,8 +451,8 @@ func (p *moduleParser) endFunc(typeIdx wasm.Index, code *wasm.Code, name string,
|
||||
|
||||
// endMemory adds the limits for the current memory, and increments memoryNamespace as it is shared across imported and
|
||||
// module-defined memories. Finally, this returns parseModule to prepare for the next field.
|
||||
func (p *moduleParser) endMemory(min, max uint32) tokenParser {
|
||||
p.module.MemorySection = &wasm.Memory{Min: min, Max: max}
|
||||
func (p *moduleParser) endMemory(min, max uint32, maxDecoded bool) tokenParser {
|
||||
p.module.MemorySection = &wasm.Memory{Min: min, Max: max, IsMaxEncoded: maxDecoded}
|
||||
p.pos = positionModule
|
||||
return p.parseModule
|
||||
}
|
||||
@@ -468,8 +470,13 @@ func (p *moduleParser) parseExportName(tok tokenType, tokenBytes []byte, _, _ ui
|
||||
switch tok {
|
||||
case tokenString: // Ex. "" or "PI"
|
||||
name := string(tokenBytes[1 : len(tokenBytes)-1]) // strip quotes
|
||||
if _, ok := p.module.ExportSection[name]; ok {
|
||||
if p.exportedName == nil {
|
||||
p.exportedName = map[string]struct{}{}
|
||||
}
|
||||
if _, ok := p.exportedName[name]; ok {
|
||||
return nil, fmt.Errorf("%q already exported", name)
|
||||
} else {
|
||||
p.exportedName[name] = struct{}{}
|
||||
}
|
||||
p.currentModuleField = &wasm.Export{Name: name}
|
||||
return p.parseExport, nil
|
||||
@@ -567,11 +574,7 @@ func (p *moduleParser) parseExportEnd(tok tokenType, tokenBytes []byte, _, _ uin
|
||||
|
||||
e := p.currentModuleField.(*wasm.Export)
|
||||
p.currentModuleField = nil
|
||||
if p.module.ExportSection == nil {
|
||||
p.module.ExportSection = map[string]*wasm.Export{e.Name: e}
|
||||
} else {
|
||||
p.module.ExportSection[e.Name] = e
|
||||
}
|
||||
p.module.ExportSection = append(p.module.ExportSection, e)
|
||||
p.pos = positionModule
|
||||
return p.parseModule, nil
|
||||
}
|
||||
|
||||
@@ -1232,8 +1232,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
ImportSection: []*wasm.Import{
|
||||
{Module: "foo", Name: "bar", Type: wasm.ExternTypeFunc, DescFunc: 0},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"bar": {Name: "bar", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "bar", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
},
|
||||
NameSection: &wasm.NameSection{FunctionNames: wasm.NameMap{{Index: 0, Name: "bar"}}},
|
||||
},
|
||||
@@ -1249,8 +1249,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
ImportSection: []*wasm.Import{
|
||||
{Module: "foo", Name: "bar", Type: wasm.ExternTypeFunc, DescFunc: 0},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"bar": {Name: "bar", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "bar", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1266,9 +1266,9 @@ func TestDecodeModule(t *testing.T) {
|
||||
ImportSection: []*wasm.Import{
|
||||
{Module: "foo", Name: "bar", Type: wasm.ExternTypeFunc, DescFunc: 0},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"foo": {Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
"bar": {Name: "bar", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
{Name: "bar", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{&wasm.NameAssoc{Index: 0, Name: "bar"}},
|
||||
@@ -1288,9 +1288,9 @@ func TestDecodeModule(t *testing.T) {
|
||||
ImportSection: []*wasm.Import{{Module: "foo", Name: "bar", Type: wasm.ExternTypeFunc, DescFunc: 0}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{Body: end}},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"foo": {Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
"bar": {Name: "bar", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
{Name: "bar", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{
|
||||
@@ -1313,9 +1313,9 @@ func TestDecodeModule(t *testing.T) {
|
||||
ImportSection: []*wasm.Import{{Module: "foo", Name: "bar", Type: wasm.ExternTypeFunc, DescFunc: 0}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{Body: end}},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"foo": {Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
"bar": {Name: "bar", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
{Name: "bar", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{
|
||||
@@ -1338,9 +1338,9 @@ func TestDecodeModule(t *testing.T) {
|
||||
ImportSection: []*wasm.Import{{Module: "foo", Name: "bar", Type: wasm.ExternTypeFunc, DescFunc: 0}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{Body: end}},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"foo": {Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
"bar": {Name: "bar", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
{Name: "bar", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1357,9 +1357,9 @@ func TestDecodeModule(t *testing.T) {
|
||||
ImportSection: []*wasm.Import{{Module: "foo", Name: "bar", Type: wasm.ExternTypeFunc, DescFunc: 0}},
|
||||
FunctionSection: []wasm.Index{0},
|
||||
CodeSection: []*wasm.Code{{Body: end}},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"foo": {Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
"bar": {Name: "bar", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "foo", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
{Name: "bar", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1384,8 +1384,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
CodeSection: []*wasm.Code{
|
||||
{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeI32Add, wasm.OpcodeEnd}},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"AddInt": {Name: "AddInt", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "AddInt", Type: wasm.ExternTypeFunc, Index: 0},
|
||||
},
|
||||
NameSection: &wasm.NameSection{
|
||||
FunctionNames: wasm.NameMap{{Index: 0, Name: "addInt"}},
|
||||
@@ -1406,8 +1406,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
MemorySection: &wasm.Memory{Min: 0, Max: wasm.MemoryMaxPages},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"foo": {Name: "foo", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "foo", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1419,8 +1419,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
MemorySection: &wasm.Memory{Min: 0, Max: wasm.MemoryMaxPages},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"foo": {Name: "foo", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "foo", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1437,9 +1437,9 @@ func TestDecodeModule(t *testing.T) {
|
||||
TypeSection: []*wasm.FunctionType{v_v},
|
||||
FunctionSection: []wasm.Index{0, 0, 0},
|
||||
CodeSection: []*wasm.Code{{Body: end}, {Body: end}, {Body: end}},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"": {Name: "", Type: wasm.ExternTypeFunc, Index: wasm.Index(2)},
|
||||
"a": {Name: "a", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "", Type: wasm.ExternTypeFunc, Index: wasm.Index(2)},
|
||||
{Name: "a", Type: wasm.ExternTypeFunc, Index: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1451,8 +1451,8 @@ func TestDecodeModule(t *testing.T) {
|
||||
)`,
|
||||
expected: &wasm.Module{
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: wasm.MemoryMaxPages},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"memory": {Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ func newMemoryParser(memoryMaxPages uint32, memoryNamespace *indexNamespace, onM
|
||||
return &memoryParser{memoryMaxPages: memoryMaxPages, memoryNamespace: memoryNamespace, onMemory: onMemory}
|
||||
}
|
||||
|
||||
type onMemory func(min, max uint32) tokenParser
|
||||
type onMemory func(min, max uint32, maxDecooded bool) tokenParser
|
||||
|
||||
// memoryParser parses a api.Memory from and dispatches to onMemory.
|
||||
//
|
||||
@@ -24,6 +24,7 @@ type onMemory func(min, max uint32) tokenParser
|
||||
type memoryParser struct {
|
||||
// memoryMaxPages is the limit of pages (not bytes) for each wasm.Memory.
|
||||
memoryMaxPages uint32
|
||||
maxDecoded bool
|
||||
|
||||
memoryNamespace *indexNamespace
|
||||
|
||||
@@ -89,6 +90,7 @@ func (p *memoryParser) beginMax(tok tokenType, tokenBytes []byte, line, col uint
|
||||
} else if i < p.currentMin {
|
||||
return nil, fmt.Errorf("min %d pages (%s) > max %d pages (%s)", p.currentMin, wasm.PagesToUnitOfBytes(p.currentMin), i, wasm.PagesToUnitOfBytes(i))
|
||||
}
|
||||
p.maxDecoded = true
|
||||
p.currentMax = i
|
||||
return p.end, nil
|
||||
case tokenRParen:
|
||||
@@ -104,5 +106,5 @@ func (p *memoryParser) end(tok tokenType, tokenBytes []byte, _, _ uint32) (token
|
||||
return nil, unexpectedToken(tok, tokenBytes)
|
||||
}
|
||||
p.memoryNamespace.count++
|
||||
return p.onMemory(p.currentMin, p.currentMax), nil
|
||||
return p.onMemory(p.currentMin, p.currentMax, p.maxDecoded), nil
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestMemoryParser(t *testing.T) {
|
||||
{
|
||||
name: "min 0, max 0",
|
||||
input: "(memory 0 0)",
|
||||
expected: &wasm.Memory{Max: zero},
|
||||
expected: &wasm.Memory{Max: zero, IsMaxEncoded: true},
|
||||
},
|
||||
{
|
||||
name: "min largest",
|
||||
@@ -40,17 +40,17 @@ func TestMemoryParser(t *testing.T) {
|
||||
{
|
||||
name: "min 0, max largest",
|
||||
input: "(memory 0 65536)",
|
||||
expected: &wasm.Memory{Max: max},
|
||||
expected: &wasm.Memory{Max: max, IsMaxEncoded: true},
|
||||
},
|
||||
{
|
||||
name: "min largest max largest",
|
||||
input: "(memory 65536 65536)",
|
||||
expected: &wasm.Memory{Min: max, Max: max},
|
||||
expected: &wasm.Memory{Min: max, Max: max, IsMaxEncoded: true},
|
||||
},
|
||||
{
|
||||
name: "min largest max largest - ID",
|
||||
input: "(memory $mem 65536 65536)",
|
||||
expected: &wasm.Memory{Min: max, Max: max},
|
||||
expected: &wasm.Memory{Min: max, Max: max, IsMaxEncoded: true},
|
||||
expectedID: "mem",
|
||||
},
|
||||
}
|
||||
@@ -162,8 +162,8 @@ func TestMemoryParser_Errors(t *testing.T) {
|
||||
|
||||
func parseMemoryType(memoryNamespace *indexNamespace, input string) (*wasm.Memory, *memoryParser, error) {
|
||||
var parsed *wasm.Memory
|
||||
var setFunc onMemory = func(min, max uint32) tokenParser {
|
||||
parsed = &wasm.Memory{Min: min, Max: max}
|
||||
var setFunc onMemory = func(min, max uint32, maxDecoded bool) tokenParser {
|
||||
parsed = &wasm.Memory{Min: min, Max: max, IsMaxEncoded: maxDecoded}
|
||||
return parseErr
|
||||
}
|
||||
tp := newMemoryParser(wasm.MemoryMaxPages, memoryNamespace, setFunc)
|
||||
|
||||
81
tests/spectest/encoder_test.go
Normal file
81
tests/spectest/encoder_test.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package spectests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
"github.com/tetratelabs/wazero/internal/wasm/binary"
|
||||
)
|
||||
|
||||
// requireStripCustomSections strips all the custom sections from the given binary.
|
||||
func requireStripCustomSections(t *testing.T, binary []byte) []byte {
|
||||
r := bytes.NewReader(binary)
|
||||
out := bytes.NewBuffer(nil)
|
||||
_, err := io.CopyN(out, r, 8)
|
||||
require.NoError(t, err)
|
||||
|
||||
for {
|
||||
sectionID, err := r.ReadByte()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
sectionSize, _, err := leb128.DecodeUint32(r)
|
||||
require.NoError(t, err)
|
||||
|
||||
switch sectionID {
|
||||
case wasm.SectionIDCustom:
|
||||
_, err = io.CopyN(io.Discard, r, int64(sectionSize))
|
||||
require.NoError(t, err)
|
||||
default:
|
||||
out.WriteByte(sectionID)
|
||||
out.Write(leb128.EncodeUint32(sectionSize))
|
||||
_, err := io.CopyN(out, r, int64(sectionSize))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
// TestBinaryEncoder ensures that binary.EncodeModule produces exactly the same binaries
|
||||
// for wasm.Module via binary.DecodeModule modulo custom sections for all the valid binaries in spectests.
|
||||
func TestBinaryEncoder(t *testing.T) {
|
||||
files, err := testcases.ReadDir("testdata")
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, f := range files {
|
||||
filename := f.Name()
|
||||
if strings.HasSuffix(filename, ".json") {
|
||||
raw, err := testcases.ReadFile(testdataPath(filename))
|
||||
require.NoError(t, err)
|
||||
|
||||
var base testbase
|
||||
require.NoError(t, json.Unmarshal(raw, &base))
|
||||
|
||||
for _, c := range base.Commands {
|
||||
if c.CommandType == "module" {
|
||||
t.Run(c.Filename, func(t *testing.T) {
|
||||
buf, err := testcases.ReadFile(testdataPath(c.Filename))
|
||||
require.NoError(t, err)
|
||||
|
||||
buf = requireStripCustomSections(t, buf)
|
||||
|
||||
mod, err := binary.DecodeModule(buf, wasm.Features20191205, wasm.MemoryMaxPages)
|
||||
require.NoError(t, err)
|
||||
|
||||
encodedBuf := binary.EncodeModule(mod)
|
||||
require.Equal(t, buf, encodedBuf)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,26 +246,26 @@ func addSpectestModule(t *testing.T, store *wasm.Store) {
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(666)},
|
||||
})
|
||||
mod.ExportSection["global_i32"] = &wasm.Export{Name: "global_i32", Index: 0, Type: wasm.ExternTypeGlobal}
|
||||
mod.ExportSection = append(mod.ExportSection, &wasm.Export{Name: "global_i32", Index: 0, Type: wasm.ExternTypeGlobal})
|
||||
|
||||
// (global (export "global_f32") f32 (f32.const 666))
|
||||
mod.GlobalSection = append(mod.GlobalSection, &wasm.Global{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: u64.LeBytes(api.EncodeF32(666))},
|
||||
})
|
||||
mod.ExportSection["global_f32"] = &wasm.Export{Name: "global_f32", Index: 1, Type: wasm.ExternTypeGlobal}
|
||||
mod.ExportSection = append(mod.ExportSection, &wasm.Export{Name: "global_f32", Index: 1, Type: wasm.ExternTypeGlobal})
|
||||
|
||||
// (global (export "global_f64") f64 (f64.const 666))
|
||||
mod.GlobalSection = append(mod.GlobalSection, &wasm.Global{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: u64.LeBytes(api.EncodeF64(666))},
|
||||
})
|
||||
mod.ExportSection["global_f64"] = &wasm.Export{Name: "global_f64", Index: 2, Type: wasm.ExternTypeGlobal}
|
||||
mod.ExportSection = append(mod.ExportSection, &wasm.Export{Name: "global_f64", Index: 2, Type: wasm.ExternTypeGlobal})
|
||||
|
||||
// (table (export "table") 10 20 funcref)
|
||||
tableLimitMax := uint32(20)
|
||||
mod.TableSection = &wasm.Table{Min: 10, Max: &tableLimitMax}
|
||||
mod.ExportSection["table"] = &wasm.Export{Name: "table", Index: 0, Type: wasm.ExternTypeTable}
|
||||
mod.ExportSection = append(mod.ExportSection, &wasm.Export{Name: "table", Index: 0, Type: wasm.ExternTypeTable})
|
||||
|
||||
_, err = store.Instantiate(context.Background(), mod, mod.NameSection.ModuleName, wasm.DefaultSysContext())
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -59,12 +59,12 @@ func newExample() *wasm.Module {
|
||||
{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeI64Extend16S, wasm.OpcodeEnd}},
|
||||
{Body: []byte{wasm.OpcodeLocalGet, 1, wasm.OpcodeLocalGet, 0, wasm.OpcodeEnd}},
|
||||
},
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: three},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"": {Name: "", Type: wasm.ExternTypeFunc, Index: wasm.Index(3)},
|
||||
"AddInt": {Name: "AddInt", Type: wasm.ExternTypeFunc, Index: wasm.Index(4)},
|
||||
"swap": {Name: "swap", Type: wasm.ExternTypeFunc, Index: wasm.Index(6)},
|
||||
"mem": {Name: "mem", Type: wasm.ExternTypeMemory, Index: wasm.Index(0)},
|
||||
MemorySection: &wasm.Memory{Min: 1, Max: three, IsMaxEncoded: true},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Name: "AddInt", Type: wasm.ExternTypeFunc, Index: wasm.Index(4)},
|
||||
{Name: "", Type: wasm.ExternTypeFunc, Index: wasm.Index(3)},
|
||||
{Name: "mem", Type: wasm.ExternTypeMemory, Index: wasm.Index(0)},
|
||||
{Name: "swap", Type: wasm.ExternTypeFunc, Index: wasm.Index(6)},
|
||||
},
|
||||
StartSection: &three,
|
||||
NameSection: &wasm.NameSection{
|
||||
|
||||
@@ -100,7 +100,7 @@ func TestRuntime_DecodeModule_Errors(t *testing.T) {
|
||||
{
|
||||
name: "memory has too many pages - binary",
|
||||
runtime: NewRuntimeWithConfig(NewRuntimeConfig().WithMemoryMaxPages(2)),
|
||||
source: binary.EncodeModule(&wasm.Module{MemorySection: &wasm.Memory{Min: 2, Max: 3}}),
|
||||
source: binary.EncodeModule(&wasm.Module{MemorySection: &wasm.Memory{Min: 2, Max: 3, IsMaxEncoded: true}}),
|
||||
expectedErr: "section memory: max 3 pages (192 Ki) outside range of 2 pages (128 Ki)",
|
||||
},
|
||||
}
|
||||
@@ -205,8 +205,8 @@ func TestModule_Global(t *testing.T) {
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(globalVal)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
"global": {Type: wasm.ExternTypeGlobal, Name: "global"},
|
||||
ExportSection: []*wasm.Export{
|
||||
{Type: wasm.ExternTypeGlobal, Name: "global"},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
|
||||
Reference in New Issue
Block a user