From f5598c9a8ee745e06f60ef8069a1992e682f1a0b Mon Sep 17 00:00:00 2001 From: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com> Date: Wed, 6 Apr 2022 06:35:31 +0800 Subject: [PATCH] Fixes global numeric types to have max of signed encoding (#442) This adjusts towards the exiting code which used int32/64 instead of uint32/64. The reason is that the spec indicates intepretation as signed numbers, which affects the maximum value. See https://www.w3.org/TR/wasm-core-1/#value-types%E2%91%A2 Signed-off-by: Adrian Cole --- api/wasm.go | 4 +++ api/wasm_test.go | 8 +++--- builder.go | 23 +++++++++++------ builder_test.go | 21 ++++++++-------- internal/ieee754/ieee754.go | 4 +++ internal/leb128/leb128_test.go | 3 +++ internal/u64/u64.go | 15 ++++++++++++ internal/u64/u64_test.go | 39 +++++++++++++++++++++++++++++ internal/wasm/global_test.go | 45 +++++++++++++++++++++++++--------- internal/wasm/module.go | 2 ++ internal/wasm/module_test.go | 31 +++++++++-------------- internal/wasm/store_test.go | 13 +++++----- internal/wasm/table.go | 2 +- tests/spectest/spec_test.go | 5 ++-- 14 files changed, 153 insertions(+), 62 deletions(-) create mode 100644 internal/u64/u64.go create mode 100644 internal/u64/u64_test.go diff --git a/api/wasm.go b/api/wasm.go index 408d4d09..3c32735d 100644 --- a/api/wasm.go +++ b/api/wasm.go @@ -31,9 +31,13 @@ import ( type ValueType = byte const ( + // ValueTypeI32 is a 32-bit integer. ValueTypeI32 ValueType = 0x7f + // ValueTypeI64 is a 64-bit integer. ValueTypeI64 ValueType = 0x7e + // ValueTypeF32 is a 32-bit floating point number. ValueTypeF32 ValueType = 0x7d + // ValueTypeF64 is a 32-bit floating point number. ValueTypeF64 ValueType = 0x7c ) diff --git a/api/wasm_test.go b/api/wasm_test.go index 9d0d82f7..456f958c 100644 --- a/api/wasm_test.go +++ b/api/wasm_test.go @@ -64,11 +64,11 @@ func TestEncodeDecodeF64(t *testing.T) { } { t.Run(fmt.Sprintf("%f", v), func(t *testing.T) { encoded := EncodeF64(v) - binary := DecodeF64(encoded) - if math.IsNaN(binary) { // cannot use require.Equal as NaN by definition doesn't equal itself - require.True(t, math.IsNaN(binary)) + val := DecodeF64(encoded) + if math.IsNaN(val) { // cannot use require.Equal as NaN by definition doesn't equal itself + require.True(t, math.IsNaN(val)) } else { - require.Equal(t, v, binary) + require.Equal(t, v, val) } }) } diff --git a/builder.go b/builder.go index 783eeab7..2f856447 100644 --- a/builder.go +++ b/builder.go @@ -5,6 +5,7 @@ import ( "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/internal/leb128" + "github.com/tetratelabs/wazero/internal/u64" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -105,8 +106,10 @@ type ModuleBuilder interface { // builder.ExportGlobalI32("canvas_width", 1024) // // Note: If a global is already exported with the same name, this overwrites it. + // Note: The maximum value of v is math.MaxInt32 to match constraints of initialization in binary format. + // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#value-types%E2%91%A2 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-globaltype - ExportGlobalI32(name string, v uint32) ModuleBuilder + ExportGlobalI32(name string, v int32) ModuleBuilder // ExportGlobalI64 exports a global constant of type api.ValueTypeI64. // @@ -115,8 +118,10 @@ type ModuleBuilder interface { // builder.ExportGlobalI64("start_epoch", 1620216263544) // // Note: If a global is already exported with the same name, this overwrites it. + // Note: The maximum value of v is math.MaxInt64 to match constraints of initialization in binary format. + // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#value-types%E2%91%A2 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-globaltype - ExportGlobalI64(name string, v uint64) ModuleBuilder + ExportGlobalI64(name string, v int64) ModuleBuilder // ExportGlobalF32 exports a global constant of type api.ValueTypeF32. // @@ -194,19 +199,21 @@ func (b *moduleBuilder) ExportMemoryWithMax(name string, minPages, maxPages uint } // ExportGlobalI32 implements ModuleBuilder.ExportGlobalI32 -func (b *moduleBuilder) ExportGlobalI32(name string, v uint32) ModuleBuilder { +func (b *moduleBuilder) ExportGlobalI32(name string, v int32) ModuleBuilder { b.nameToGlobal[name] = &wasm.Global{ Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeUint32(v)}, + // Signed per https://www.w3.org/TR/wasm-core-1/#value-types%E2%91%A2 + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeUint32(uint32(v))}, } return b } // ExportGlobalI64 implements ModuleBuilder.ExportGlobalI64 -func (b *moduleBuilder) ExportGlobalI64(name string, v uint64) ModuleBuilder { +func (b *moduleBuilder) ExportGlobalI64(name string, v int64) ModuleBuilder { b.nameToGlobal[name] = &wasm.Global{ Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeUint64(v)}, + // Signed per https://www.w3.org/TR/wasm-core-1/#value-types%E2%91%A2 + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeUint64(uint64(v))}, } return b } @@ -215,7 +222,7 @@ func (b *moduleBuilder) ExportGlobalI64(name string, v uint64) ModuleBuilder { func (b *moduleBuilder) ExportGlobalF32(name string, v float32) ModuleBuilder { b.nameToGlobal[name] = &wasm.Global{ Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: leb128.EncodeUint64(api.EncodeF32(v))}, + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: u64.LeBytes(api.EncodeF32(v))}, } return b } @@ -224,7 +231,7 @@ func (b *moduleBuilder) ExportGlobalF32(name string, v float32) ModuleBuilder { func (b *moduleBuilder) ExportGlobalF64(name string, v float64) ModuleBuilder { b.nameToGlobal[name] = &wasm.Global{ Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: leb128.EncodeUint64(api.EncodeF64(v))}, + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: u64.LeBytes(api.EncodeF64(v))}, } return b } diff --git a/builder_test.go b/builder_test.go index b567a35a..62be33aa 100644 --- a/builder_test.go +++ b/builder_test.go @@ -9,6 +9,7 @@ import ( "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/internal/leb128" + "github.com/tetratelabs/wazero/internal/u64" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -221,13 +222,13 @@ func TestNewModuleBuilder_Build(t *testing.T) { { name: "ExportGlobalI32 overwrites", input: func(r Runtime) ModuleBuilder { - return r.NewModuleBuilder("").ExportGlobalI32("canvas_width", 1024).ExportGlobalI32("canvas_width", 2048) + return r.NewModuleBuilder("").ExportGlobalI32("canvas_width", 1024).ExportGlobalI32("canvas_width", math.MaxInt32) }, expected: &wasm.Module{ GlobalSection: []*wasm.Global{ { Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeUint32(2048)}, + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeUint32(math.MaxInt32)}, }, }, ExportSection: map[string]*wasm.Export{ @@ -255,13 +256,13 @@ func TestNewModuleBuilder_Build(t *testing.T) { { name: "ExportGlobalI64 overwrites", input: func(r Runtime) ModuleBuilder { - return r.NewModuleBuilder("").ExportGlobalI64("start_epoch", 1620216263544).ExportGlobalI64("start_epoch", 1620216263544000) + return r.NewModuleBuilder("").ExportGlobalI64("start_epoch", 1620216263544).ExportGlobalI64("start_epoch", math.MaxInt64) }, expected: &wasm.Module{ GlobalSection: []*wasm.Global{ { Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeUint64(1620216263544000)}, + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeUint64(math.MaxInt64)}, }, }, ExportSection: map[string]*wasm.Export{ @@ -278,7 +279,7 @@ func TestNewModuleBuilder_Build(t *testing.T) { GlobalSection: []*wasm.Global{ { Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: leb128.EncodeUint64(api.EncodeF32(3.1415926536))}, + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: u64.LeBytes(api.EncodeF32(3.1415926536))}, }, }, ExportSection: map[string]*wasm.Export{ @@ -289,13 +290,13 @@ func TestNewModuleBuilder_Build(t *testing.T) { { name: "ExportGlobalF32 overwrites", input: func(r Runtime) ModuleBuilder { - return r.NewModuleBuilder("").ExportGlobalF32("math/pi", 3.1415926536).ExportGlobalF32("math/pi", 3.14159) + return r.NewModuleBuilder("").ExportGlobalF32("math/pi", 3.1415926536).ExportGlobalF32("math/pi", math.MaxFloat32) }, expected: &wasm.Module{ GlobalSection: []*wasm.Global{ { Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: leb128.EncodeUint64(api.EncodeF32(3.14159))}, + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: u64.LeBytes(api.EncodeF32(math.MaxFloat32))}, }, }, ExportSection: map[string]*wasm.Export{ @@ -312,7 +313,7 @@ func TestNewModuleBuilder_Build(t *testing.T) { GlobalSection: []*wasm.Global{ { Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: leb128.EncodeUint64(api.EncodeF64(math.Pi))}, + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: u64.LeBytes(api.EncodeF64(math.Pi))}, }, }, ExportSection: map[string]*wasm.Export{ @@ -323,13 +324,13 @@ func TestNewModuleBuilder_Build(t *testing.T) { { name: "ExportGlobalF64 overwrites", input: func(r Runtime) ModuleBuilder { - return r.NewModuleBuilder("").ExportGlobalF64("math/pi", math.Pi).ExportGlobalF64("math/pi", 3.14159) + return r.NewModuleBuilder("").ExportGlobalF64("math/pi", math.Pi).ExportGlobalF64("math/pi", math.MaxFloat64) }, expected: &wasm.Module{ GlobalSection: []*wasm.Global{ { Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64}, - Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: leb128.EncodeUint64(api.EncodeF64(3.14159))}, + Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: u64.LeBytes(api.EncodeF64(math.MaxFloat64))}, }, }, ExportSection: map[string]*wasm.Export{ diff --git a/internal/ieee754/ieee754.go b/internal/ieee754/ieee754.go index dd12d1e5..246cbcda 100644 --- a/internal/ieee754/ieee754.go +++ b/internal/ieee754/ieee754.go @@ -6,6 +6,8 @@ import ( "math" ) +// DecodeFloat32 decodes a float32 in IEEE 754 binary representation. +// See https://www.w3.org/TR/wasm-core-1/#floating-point%E2%91%A2 func DecodeFloat32(r io.Reader) (float32, error) { buf := make([]byte, 4) _, err := io.ReadFull(r, buf) @@ -16,6 +18,8 @@ func DecodeFloat32(r io.Reader) (float32, error) { return math.Float32frombits(raw), nil } +// DecodeFloat64 decodes a float64 in IEEE 754 binary representation. +// See https://www.w3.org/TR/wasm-core-1/#floating-point%E2%91%A2 func DecodeFloat64(r io.Reader) (float64, error) { buf := make([]byte, 8) _, err := io.ReadFull(r, buf) diff --git a/internal/leb128/leb128_test.go b/internal/leb128/leb128_test.go index a23d7d92..93e3f68e 100644 --- a/internal/leb128/leb128_test.go +++ b/internal/leb128/leb128_test.go @@ -58,6 +58,7 @@ func TestDecodeUint32(t *testing.T) { {bytes: []byte{0x80, 0x7f}, exp: 16256}, {bytes: []byte{0xe5, 0x8e, 0x26}, exp: 624485}, {bytes: []byte{0x80, 0x80, 0x80, 0x4f}, exp: 165675008}, + {bytes: []byte{0xff, 0xff, 0xff, 0xff, 0xf}, exp: math.MaxUint32}, {bytes: []byte{0x83, 0x80, 0x80, 0x80, 0x80, 0x00}, expErr: true}, {bytes: []byte{0x82, 0x80, 0x80, 0x80, 0x70}, expErr: true}, {bytes: []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x00}, expErr: true}, @@ -83,6 +84,8 @@ func TestDecodeUint64(t *testing.T) { {bytes: []byte{0x80, 0x7f}, exp: 16256}, {bytes: []byte{0xe5, 0x8e, 0x26}, exp: 624485}, {bytes: []byte{0x80, 0x80, 0x80, 0x4f}, exp: 165675008}, + {bytes: []byte{0xff, 0xff, 0xff, 0xff, 0xf}, exp: math.MaxUint32}, + {bytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1}, exp: math.MaxUint64}, {bytes: []byte{0x89, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x71}, expErr: true}, } { actual, num, err := DecodeUint64(bytes.NewReader(c.bytes)) diff --git a/internal/u64/u64.go b/internal/u64/u64.go new file mode 100644 index 00000000..a47d9d60 --- /dev/null +++ b/internal/u64/u64.go @@ -0,0 +1,15 @@ +package u64 + +// LeBytes returns a byte array corresponding to the 8 bytes in the uint64 in little-endian byte order. +func LeBytes(v uint64) []byte { + return []byte{ + byte(v), + byte(v >> 8), + byte(v >> 16), + byte(v >> 24), + byte(v >> 32), + byte(v >> 40), + byte(v >> 48), + byte(v >> 56), + } +} diff --git a/internal/u64/u64_test.go b/internal/u64/u64_test.go new file mode 100644 index 00000000..778318a2 --- /dev/null +++ b/internal/u64/u64_test.go @@ -0,0 +1,39 @@ +package u64 + +import ( + "encoding/binary" + "math" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestBytes(t *testing.T) { + tests := []struct { + name string + input uint64 + }{ + { + name: "zero", + input: 0, + }, + { + name: "half", + input: math.MaxUint32, + }, + { + name: "max", + input: math.MaxUint64, + }, + } + + for _, tt := range tests { + tc := tt + + t.Run(tc.name, func(t *testing.T) { + expected := make([]byte, 8) + binary.LittleEndian.PutUint64(expected, tc.input) + require.Equal(t, expected, LeBytes(tc.input)) + }) + } +} diff --git a/internal/wasm/global_test.go b/internal/wasm/global_test.go index 124ddeff..9aea570c 100644 --- a/internal/wasm/global_test.go +++ b/internal/wasm/global_test.go @@ -2,12 +2,13 @@ package wasm import ( "context" - gobinary "encoding/binary" + "math" "testing" "github.com/stretchr/testify/require" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/internal/u64" ) func TestGlobalTypes(t *testing.T) { @@ -26,6 +27,13 @@ func TestGlobalTypes(t *testing.T) { expectedVal: 1, expectedString: "global(1)", }, + { + name: "i32 - immutable - max", + global: globalI32(math.MaxInt32), + expectedType: ValueTypeI32, + expectedVal: math.MaxInt32, + expectedString: "global(2147483647)", + }, { name: "i64 - immutable", global: globalI64(1), @@ -33,6 +41,13 @@ func TestGlobalTypes(t *testing.T) { expectedVal: 1, expectedString: "global(1)", }, + { + name: "i64 - immutable - max", + global: globalI64(math.MaxInt64), + expectedType: ValueTypeI64, + expectedVal: math.MaxInt64, + expectedString: "global(9223372036854775807)", + }, { name: "f32 - immutable", global: globalF32(api.EncodeF32(1.0)), @@ -40,6 +55,13 @@ func TestGlobalTypes(t *testing.T) { expectedVal: api.EncodeF32(1.0), expectedString: "global(1.000000)", }, + { + name: "f32 - immutable - max", + global: globalF32(api.EncodeF32(math.MaxFloat32)), + expectedType: ValueTypeF32, + expectedVal: api.EncodeF32(math.MaxFloat32), + expectedString: "global(340282346638528859811704183484516925440.000000)", + }, { name: "f64 - immutable", global: globalF64(api.EncodeF64(1.0)), @@ -47,6 +69,13 @@ func TestGlobalTypes(t *testing.T) { expectedVal: api.EncodeF64(1.0), expectedString: "global(1.000000)", }, + { + name: "f64 - immutable - max", + global: globalF64(api.EncodeF64(math.MaxFloat64)), + expectedType: ValueTypeF64, + expectedVal: api.EncodeF64(math.MaxFloat64), + expectedString: "global(179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000)", + }, { name: "i32 - mutable", global: &mutableGlobal{g: &GlobalInstance{ @@ -165,7 +194,7 @@ func TestPublicModule_Global(t *testing.T) { { Type: &GlobalType{ValType: ValueTypeF32}, Init: &ConstantExpression{Opcode: OpcodeF32Const, - Data: uint64Le(api.EncodeF32(1.0)), + Data: u64.LeBytes(api.EncodeF32(1.0)), }, }, }, @@ -180,7 +209,7 @@ func TestPublicModule_Global(t *testing.T) { { Type: &GlobalType{ValType: ValueTypeF64}, Init: &ConstantExpression{Opcode: OpcodeF64Const, - Data: uint64Le(api.EncodeF64(1.0)), + Data: u64.LeBytes(api.EncodeF64(1.0)), }, }, }, @@ -225,7 +254,7 @@ func TestPublicModule_Global(t *testing.T) { { Type: &GlobalType{ValType: ValueTypeF32, Mutable: true}, Init: &ConstantExpression{Opcode: OpcodeF32Const, - Data: uint64Le(api.EncodeF32(1.0)), + Data: u64.LeBytes(api.EncodeF32(1.0)), }, }, }, @@ -242,7 +271,7 @@ func TestPublicModule_Global(t *testing.T) { { Type: &GlobalType{ValType: ValueTypeF64, Mutable: true}, Init: &ConstantExpression{Opcode: OpcodeF64Const, - Data: uint64Le(api.EncodeF64(1.0)), + Data: u64.LeBytes(api.EncodeF64(1.0)), }, }, }, @@ -271,9 +300,3 @@ func TestPublicModule_Global(t *testing.T) { }) } } - -func uint64Le(v uint64) (ret []byte) { - ret = make([]byte, 8) - gobinary.LittleEndian.PutUint64(ret, v) - return -} diff --git a/internal/wasm/module.go b/internal/wasm/module.go index 19088ebf..ed08007a 100644 --- a/internal/wasm/module.go +++ b/internal/wasm/module.go @@ -447,6 +447,8 @@ func (m *Module) buildGlobals(importedGlobals []*GlobalInstance) (globals []*Glo gv = api.EncodeF32(v) case float64: gv = api.EncodeF64(v) + default: + panic(fmt.Errorf("BUG: invalid conversion %d", v)) } globals = append(globals, &GlobalInstance{Type: gs.Type, Val: gv}) } diff --git a/internal/wasm/module_test.go b/internal/wasm/module_test.go index 51a1b0c7..e4ffd13e 100644 --- a/internal/wasm/module_test.go +++ b/internal/wasm/module_test.go @@ -1,7 +1,6 @@ package wasm import ( - "encoding/binary" "fmt" "math" "reflect" @@ -10,6 +9,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/internal/leb128" + "github.com/tetratelabs/wazero/internal/u64" ) func TestFunctionType_String(t *testing.T) { @@ -186,20 +187,19 @@ func TestValidateConstExpression(t *testing.T) { for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} { t.Run(ValueTypeName(vt), func(t *testing.T) { t.Run("valid", func(t *testing.T) { - // Allocate bytes with enough size for all types. - expr := &ConstantExpression{Data: make([]byte, 8)} + expr := &ConstantExpression{} switch vt { case ValueTypeI32: - expr.Data[0] = 1 + expr.Data = []byte{1} expr.Opcode = OpcodeI32Const case ValueTypeI64: - expr.Data[0] = 2 + expr.Data = []byte{2} expr.Opcode = OpcodeI64Const case ValueTypeF32: - binary.LittleEndian.PutUint32(expr.Data, math.Float32bits(math.MaxFloat32)) + expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32)) expr.Opcode = OpcodeF32Const case ValueTypeF64: - binary.LittleEndian.PutUint64(expr.Data, math.Float64bits(math.MaxFloat64)) + expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64)) expr.Opcode = OpcodeF64Const } @@ -661,32 +661,25 @@ func TestModule_validateExports(t *testing.T) { } func TestModule_buildGlobalInstances(t *testing.T) { - data := []byte{0, 0, 0, 0, 0, 0, 0, 0} - binary.LittleEndian.PutUint64(data, math.Float64bits(1.0)) m := Module{GlobalSection: []*Global{ { Type: &GlobalType{Mutable: true, ValType: ValueTypeF64}, Init: &ConstantExpression{Opcode: OpcodeF64Const, - Data: []byte{0, 0, 0, 0, 0, 0, 0xf0, 0x3f}}, // == float64(1.0) + Data: u64.LeBytes(api.EncodeF64(math.MaxFloat64))}, }, { Type: &GlobalType{Mutable: false, ValType: ValueTypeI32}, Init: &ConstantExpression{Opcode: OpcodeI32Const, - Data: []byte{1}}, + Data: leb128.EncodeUint32(math.MaxInt32)}, }, }} globals := m.buildGlobals(nil) expectedGlobals := []*GlobalInstance{ - {Type: &GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: math.Float64bits(1.0)}, - {Type: &GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(1)}, - } - - require.Len(t, globals, len(expectedGlobals)) - for i := range globals { - actual, expected := globals[i], expectedGlobals[i] - require.Equal(t, expected, actual) + {Type: &GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(math.MaxFloat64)}, + {Type: &GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: math.MaxInt32}, } + require.Equal(t, expectedGlobals, globals) } func TestModule_buildFunctionInstances(t *testing.T) { diff --git a/internal/wasm/store_test.go b/internal/wasm/store_test.go index 40a682da..5b8b8e37 100644 --- a/internal/wasm/store_test.go +++ b/internal/wasm/store_test.go @@ -2,7 +2,6 @@ package wasm import ( "context" - "encoding/binary" "errors" "fmt" "math" @@ -13,6 +12,7 @@ import ( "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/internal/testing/hammer" + "github.com/tetratelabs/wazero/internal/u64" ) func TestModuleInstance_Memory(t *testing.T) { @@ -466,20 +466,19 @@ func TestExecuteConstExpression(t *testing.T) { t.Run("non global expr", func(t *testing.T) { for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} { t.Run(ValueTypeName(vt), func(t *testing.T) { - // Allocate bytes with enough size for all types. - expr := &ConstantExpression{Data: make([]byte, 8)} + expr := &ConstantExpression{} switch vt { case ValueTypeI32: - expr.Data[0] = 1 + expr.Data = []byte{1} expr.Opcode = OpcodeI32Const case ValueTypeI64: - expr.Data[0] = 2 + expr.Data = []byte{2} expr.Opcode = OpcodeI64Const case ValueTypeF32: - binary.LittleEndian.PutUint32(expr.Data, math.Float32bits(math.MaxFloat32)) + expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32)) expr.Opcode = OpcodeF32Const case ValueTypeF64: - binary.LittleEndian.PutUint64(expr.Data, math.Float64bits(math.MaxFloat64)) + expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64)) expr.Opcode = OpcodeF64Const } diff --git a/internal/wasm/table.go b/internal/wasm/table.go index fbeb42cd..34cd0bd8 100644 --- a/internal/wasm/table.go +++ b/internal/wasm/table.go @@ -113,7 +113,7 @@ func (m *Module) validateTable() ([]*validatedElementSegment, error) { ret = append(ret, &validatedElementSegment{oc, globalIdx, elem.Init}) } else if oc == OpcodeI32Const { - o, _, err := leb128.DecodeInt32(bytes.NewReader(elem.OffsetExpr.Data)) + o, _, err := leb128.DecodeUint32(bytes.NewReader(elem.OffsetExpr.Data)) if err != nil { return nil, fmt.Errorf("%s[%d] couldn't read i32.const parameter: %w", SectionIDName(SectionIDElement), idx, err) } diff --git a/tests/spectest/spec_test.go b/tests/spectest/spec_test.go index 689f0ef9..03aef6fa 100644 --- a/tests/spectest/spec_test.go +++ b/tests/spectest/spec_test.go @@ -15,6 +15,7 @@ import ( "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/internal/leb128" + "github.com/tetratelabs/wazero/internal/u64" "github.com/tetratelabs/wazero/internal/wasm" "github.com/tetratelabs/wazero/internal/wasm/binary" "github.com/tetratelabs/wazero/internal/wasm/interpreter" @@ -251,14 +252,14 @@ func addSpectestModule(t *testing.T, store *wasm.Store) { // (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: leb128.EncodeUint64(api.EncodeF32(666))}, + 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} // (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: leb128.EncodeUint64(api.EncodeF64(666))}, + 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}