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 <adrian@tetrate.io>
This commit is contained in:
@@ -31,9 +31,13 @@ import (
|
|||||||
type ValueType = byte
|
type ValueType = byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// ValueTypeI32 is a 32-bit integer.
|
||||||
ValueTypeI32 ValueType = 0x7f
|
ValueTypeI32 ValueType = 0x7f
|
||||||
|
// ValueTypeI64 is a 64-bit integer.
|
||||||
ValueTypeI64 ValueType = 0x7e
|
ValueTypeI64 ValueType = 0x7e
|
||||||
|
// ValueTypeF32 is a 32-bit floating point number.
|
||||||
ValueTypeF32 ValueType = 0x7d
|
ValueTypeF32 ValueType = 0x7d
|
||||||
|
// ValueTypeF64 is a 32-bit floating point number.
|
||||||
ValueTypeF64 ValueType = 0x7c
|
ValueTypeF64 ValueType = 0x7c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -64,11 +64,11 @@ func TestEncodeDecodeF64(t *testing.T) {
|
|||||||
} {
|
} {
|
||||||
t.Run(fmt.Sprintf("%f", v), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%f", v), func(t *testing.T) {
|
||||||
encoded := EncodeF64(v)
|
encoded := EncodeF64(v)
|
||||||
binary := DecodeF64(encoded)
|
val := DecodeF64(encoded)
|
||||||
if math.IsNaN(binary) { // cannot use require.Equal as NaN by definition doesn't equal itself
|
if math.IsNaN(val) { // cannot use require.Equal as NaN by definition doesn't equal itself
|
||||||
require.True(t, math.IsNaN(binary))
|
require.True(t, math.IsNaN(val))
|
||||||
} else {
|
} else {
|
||||||
require.Equal(t, v, binary)
|
require.Equal(t, v, val)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
23
builder.go
23
builder.go
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/leb128"
|
"github.com/tetratelabs/wazero/internal/leb128"
|
||||||
|
"github.com/tetratelabs/wazero/internal/u64"
|
||||||
"github.com/tetratelabs/wazero/internal/wasm"
|
"github.com/tetratelabs/wazero/internal/wasm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -105,8 +106,10 @@ type ModuleBuilder interface {
|
|||||||
// builder.ExportGlobalI32("canvas_width", 1024)
|
// builder.ExportGlobalI32("canvas_width", 1024)
|
||||||
//
|
//
|
||||||
// Note: If a global is already exported with the same name, this overwrites it.
|
// 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
|
// 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.
|
// ExportGlobalI64 exports a global constant of type api.ValueTypeI64.
|
||||||
//
|
//
|
||||||
@@ -115,8 +118,10 @@ type ModuleBuilder interface {
|
|||||||
// builder.ExportGlobalI64("start_epoch", 1620216263544)
|
// builder.ExportGlobalI64("start_epoch", 1620216263544)
|
||||||
//
|
//
|
||||||
// Note: If a global is already exported with the same name, this overwrites it.
|
// 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
|
// 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.
|
// 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
|
// 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{
|
b.nameToGlobal[name] = &wasm.Global{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
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
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportGlobalI64 implements ModuleBuilder.ExportGlobalI64
|
// 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{
|
b.nameToGlobal[name] = &wasm.Global{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64},
|
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
|
return b
|
||||||
}
|
}
|
||||||
@@ -215,7 +222,7 @@ func (b *moduleBuilder) ExportGlobalI64(name string, v uint64) ModuleBuilder {
|
|||||||
func (b *moduleBuilder) ExportGlobalF32(name string, v float32) ModuleBuilder {
|
func (b *moduleBuilder) ExportGlobalF32(name string, v float32) ModuleBuilder {
|
||||||
b.nameToGlobal[name] = &wasm.Global{
|
b.nameToGlobal[name] = &wasm.Global{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32},
|
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
|
return b
|
||||||
}
|
}
|
||||||
@@ -224,7 +231,7 @@ func (b *moduleBuilder) ExportGlobalF32(name string, v float32) ModuleBuilder {
|
|||||||
func (b *moduleBuilder) ExportGlobalF64(name string, v float64) ModuleBuilder {
|
func (b *moduleBuilder) ExportGlobalF64(name string, v float64) ModuleBuilder {
|
||||||
b.nameToGlobal[name] = &wasm.Global{
|
b.nameToGlobal[name] = &wasm.Global{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64},
|
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
|
return b
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/leb128"
|
"github.com/tetratelabs/wazero/internal/leb128"
|
||||||
|
"github.com/tetratelabs/wazero/internal/u64"
|
||||||
"github.com/tetratelabs/wazero/internal/wasm"
|
"github.com/tetratelabs/wazero/internal/wasm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -221,13 +222,13 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "ExportGlobalI32 overwrites",
|
name: "ExportGlobalI32 overwrites",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
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{
|
expected: &wasm.Module{
|
||||||
GlobalSection: []*wasm.Global{
|
GlobalSection: []*wasm.Global{
|
||||||
{
|
{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
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{
|
ExportSection: map[string]*wasm.Export{
|
||||||
@@ -255,13 +256,13 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "ExportGlobalI64 overwrites",
|
name: "ExportGlobalI64 overwrites",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
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{
|
expected: &wasm.Module{
|
||||||
GlobalSection: []*wasm.Global{
|
GlobalSection: []*wasm.Global{
|
||||||
{
|
{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64},
|
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{
|
ExportSection: map[string]*wasm.Export{
|
||||||
@@ -278,7 +279,7 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
|||||||
GlobalSection: []*wasm.Global{
|
GlobalSection: []*wasm.Global{
|
||||||
{
|
{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32},
|
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{
|
ExportSection: map[string]*wasm.Export{
|
||||||
@@ -289,13 +290,13 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "ExportGlobalF32 overwrites",
|
name: "ExportGlobalF32 overwrites",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
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{
|
expected: &wasm.Module{
|
||||||
GlobalSection: []*wasm.Global{
|
GlobalSection: []*wasm.Global{
|
||||||
{
|
{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32},
|
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{
|
ExportSection: map[string]*wasm.Export{
|
||||||
@@ -312,7 +313,7 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
|||||||
GlobalSection: []*wasm.Global{
|
GlobalSection: []*wasm.Global{
|
||||||
{
|
{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64},
|
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{
|
ExportSection: map[string]*wasm.Export{
|
||||||
@@ -323,13 +324,13 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "ExportGlobalF64 overwrites",
|
name: "ExportGlobalF64 overwrites",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
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{
|
expected: &wasm.Module{
|
||||||
GlobalSection: []*wasm.Global{
|
GlobalSection: []*wasm.Global{
|
||||||
{
|
{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64},
|
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{
|
ExportSection: map[string]*wasm.Export{
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"math"
|
"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) {
|
func DecodeFloat32(r io.Reader) (float32, error) {
|
||||||
buf := make([]byte, 4)
|
buf := make([]byte, 4)
|
||||||
_, err := io.ReadFull(r, buf)
|
_, err := io.ReadFull(r, buf)
|
||||||
@@ -16,6 +18,8 @@ func DecodeFloat32(r io.Reader) (float32, error) {
|
|||||||
return math.Float32frombits(raw), nil
|
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) {
|
func DecodeFloat64(r io.Reader) (float64, error) {
|
||||||
buf := make([]byte, 8)
|
buf := make([]byte, 8)
|
||||||
_, err := io.ReadFull(r, buf)
|
_, err := io.ReadFull(r, buf)
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ func TestDecodeUint32(t *testing.T) {
|
|||||||
{bytes: []byte{0x80, 0x7f}, exp: 16256},
|
{bytes: []byte{0x80, 0x7f}, exp: 16256},
|
||||||
{bytes: []byte{0xe5, 0x8e, 0x26}, exp: 624485},
|
{bytes: []byte{0xe5, 0x8e, 0x26}, exp: 624485},
|
||||||
{bytes: []byte{0x80, 0x80, 0x80, 0x4f}, exp: 165675008},
|
{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{0x83, 0x80, 0x80, 0x80, 0x80, 0x00}, expErr: true},
|
||||||
{bytes: []byte{0x82, 0x80, 0x80, 0x80, 0x70}, expErr: true},
|
{bytes: []byte{0x82, 0x80, 0x80, 0x80, 0x70}, expErr: true},
|
||||||
{bytes: []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x00}, 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{0x80, 0x7f}, exp: 16256},
|
||||||
{bytes: []byte{0xe5, 0x8e, 0x26}, exp: 624485},
|
{bytes: []byte{0xe5, 0x8e, 0x26}, exp: 624485},
|
||||||
{bytes: []byte{0x80, 0x80, 0x80, 0x4f}, exp: 165675008},
|
{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},
|
{bytes: []byte{0x89, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x71}, expErr: true},
|
||||||
} {
|
} {
|
||||||
actual, num, err := DecodeUint64(bytes.NewReader(c.bytes))
|
actual, num, err := DecodeUint64(bytes.NewReader(c.bytes))
|
||||||
|
|||||||
15
internal/u64/u64.go
Normal file
15
internal/u64/u64.go
Normal file
@@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
39
internal/u64/u64_test.go
Normal file
39
internal/u64/u64_test.go
Normal file
@@ -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))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,13 @@ package wasm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
gobinary "encoding/binary"
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
|
"github.com/tetratelabs/wazero/internal/u64"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGlobalTypes(t *testing.T) {
|
func TestGlobalTypes(t *testing.T) {
|
||||||
@@ -26,6 +27,13 @@ func TestGlobalTypes(t *testing.T) {
|
|||||||
expectedVal: 1,
|
expectedVal: 1,
|
||||||
expectedString: "global(1)",
|
expectedString: "global(1)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "i32 - immutable - max",
|
||||||
|
global: globalI32(math.MaxInt32),
|
||||||
|
expectedType: ValueTypeI32,
|
||||||
|
expectedVal: math.MaxInt32,
|
||||||
|
expectedString: "global(2147483647)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "i64 - immutable",
|
name: "i64 - immutable",
|
||||||
global: globalI64(1),
|
global: globalI64(1),
|
||||||
@@ -33,6 +41,13 @@ func TestGlobalTypes(t *testing.T) {
|
|||||||
expectedVal: 1,
|
expectedVal: 1,
|
||||||
expectedString: "global(1)",
|
expectedString: "global(1)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "i64 - immutable - max",
|
||||||
|
global: globalI64(math.MaxInt64),
|
||||||
|
expectedType: ValueTypeI64,
|
||||||
|
expectedVal: math.MaxInt64,
|
||||||
|
expectedString: "global(9223372036854775807)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "f32 - immutable",
|
name: "f32 - immutable",
|
||||||
global: globalF32(api.EncodeF32(1.0)),
|
global: globalF32(api.EncodeF32(1.0)),
|
||||||
@@ -40,6 +55,13 @@ func TestGlobalTypes(t *testing.T) {
|
|||||||
expectedVal: api.EncodeF32(1.0),
|
expectedVal: api.EncodeF32(1.0),
|
||||||
expectedString: "global(1.000000)",
|
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",
|
name: "f64 - immutable",
|
||||||
global: globalF64(api.EncodeF64(1.0)),
|
global: globalF64(api.EncodeF64(1.0)),
|
||||||
@@ -47,6 +69,13 @@ func TestGlobalTypes(t *testing.T) {
|
|||||||
expectedVal: api.EncodeF64(1.0),
|
expectedVal: api.EncodeF64(1.0),
|
||||||
expectedString: "global(1.000000)",
|
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",
|
name: "i32 - mutable",
|
||||||
global: &mutableGlobal{g: &GlobalInstance{
|
global: &mutableGlobal{g: &GlobalInstance{
|
||||||
@@ -165,7 +194,7 @@ func TestPublicModule_Global(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Type: &GlobalType{ValType: ValueTypeF32},
|
Type: &GlobalType{ValType: ValueTypeF32},
|
||||||
Init: &ConstantExpression{Opcode: OpcodeF32Const,
|
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},
|
Type: &GlobalType{ValType: ValueTypeF64},
|
||||||
Init: &ConstantExpression{Opcode: OpcodeF64Const,
|
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},
|
Type: &GlobalType{ValType: ValueTypeF32, Mutable: true},
|
||||||
Init: &ConstantExpression{Opcode: OpcodeF32Const,
|
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},
|
Type: &GlobalType{ValType: ValueTypeF64, Mutable: true},
|
||||||
Init: &ConstantExpression{Opcode: OpcodeF64Const,
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -447,6 +447,8 @@ func (m *Module) buildGlobals(importedGlobals []*GlobalInstance) (globals []*Glo
|
|||||||
gv = api.EncodeF32(v)
|
gv = api.EncodeF32(v)
|
||||||
case float64:
|
case float64:
|
||||||
gv = api.EncodeF64(v)
|
gv = api.EncodeF64(v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("BUG: invalid conversion %d", v))
|
||||||
}
|
}
|
||||||
globals = append(globals, &GlobalInstance{Type: gs.Type, Val: gv})
|
globals = append(globals, &GlobalInstance{Type: gs.Type, Val: gv})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package wasm
|
package wasm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -10,6 +9,8 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
|
"github.com/tetratelabs/wazero/internal/leb128"
|
||||||
|
"github.com/tetratelabs/wazero/internal/u64"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFunctionType_String(t *testing.T) {
|
func TestFunctionType_String(t *testing.T) {
|
||||||
@@ -186,20 +187,19 @@ func TestValidateConstExpression(t *testing.T) {
|
|||||||
for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} {
|
for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} {
|
||||||
t.Run(ValueTypeName(vt), func(t *testing.T) {
|
t.Run(ValueTypeName(vt), func(t *testing.T) {
|
||||||
t.Run("valid", func(t *testing.T) {
|
t.Run("valid", func(t *testing.T) {
|
||||||
// Allocate bytes with enough size for all types.
|
expr := &ConstantExpression{}
|
||||||
expr := &ConstantExpression{Data: make([]byte, 8)}
|
|
||||||
switch vt {
|
switch vt {
|
||||||
case ValueTypeI32:
|
case ValueTypeI32:
|
||||||
expr.Data[0] = 1
|
expr.Data = []byte{1}
|
||||||
expr.Opcode = OpcodeI32Const
|
expr.Opcode = OpcodeI32Const
|
||||||
case ValueTypeI64:
|
case ValueTypeI64:
|
||||||
expr.Data[0] = 2
|
expr.Data = []byte{2}
|
||||||
expr.Opcode = OpcodeI64Const
|
expr.Opcode = OpcodeI64Const
|
||||||
case ValueTypeF32:
|
case ValueTypeF32:
|
||||||
binary.LittleEndian.PutUint32(expr.Data, math.Float32bits(math.MaxFloat32))
|
expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32))
|
||||||
expr.Opcode = OpcodeF32Const
|
expr.Opcode = OpcodeF32Const
|
||||||
case ValueTypeF64:
|
case ValueTypeF64:
|
||||||
binary.LittleEndian.PutUint64(expr.Data, math.Float64bits(math.MaxFloat64))
|
expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64))
|
||||||
expr.Opcode = OpcodeF64Const
|
expr.Opcode = OpcodeF64Const
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,32 +661,25 @@ func TestModule_validateExports(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestModule_buildGlobalInstances(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{
|
m := Module{GlobalSection: []*Global{
|
||||||
{
|
{
|
||||||
Type: &GlobalType{Mutable: true, ValType: ValueTypeF64},
|
Type: &GlobalType{Mutable: true, ValType: ValueTypeF64},
|
||||||
Init: &ConstantExpression{Opcode: OpcodeF64Const,
|
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},
|
Type: &GlobalType{Mutable: false, ValType: ValueTypeI32},
|
||||||
Init: &ConstantExpression{Opcode: OpcodeI32Const,
|
Init: &ConstantExpression{Opcode: OpcodeI32Const,
|
||||||
Data: []byte{1}},
|
Data: leb128.EncodeUint32(math.MaxInt32)},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
globals := m.buildGlobals(nil)
|
globals := m.buildGlobals(nil)
|
||||||
expectedGlobals := []*GlobalInstance{
|
expectedGlobals := []*GlobalInstance{
|
||||||
{Type: &GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: math.Float64bits(1.0)},
|
{Type: &GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(math.MaxFloat64)},
|
||||||
{Type: &GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(1)},
|
{Type: &GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: math.MaxInt32},
|
||||||
}
|
|
||||||
|
|
||||||
require.Len(t, globals, len(expectedGlobals))
|
|
||||||
for i := range globals {
|
|
||||||
actual, expected := globals[i], expectedGlobals[i]
|
|
||||||
require.Equal(t, expected, actual)
|
|
||||||
}
|
}
|
||||||
|
require.Equal(t, expectedGlobals, globals)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestModule_buildFunctionInstances(t *testing.T) {
|
func TestModule_buildFunctionInstances(t *testing.T) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package wasm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
@@ -13,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/testing/hammer"
|
"github.com/tetratelabs/wazero/internal/testing/hammer"
|
||||||
|
"github.com/tetratelabs/wazero/internal/u64"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestModuleInstance_Memory(t *testing.T) {
|
func TestModuleInstance_Memory(t *testing.T) {
|
||||||
@@ -466,20 +466,19 @@ func TestExecuteConstExpression(t *testing.T) {
|
|||||||
t.Run("non global expr", func(t *testing.T) {
|
t.Run("non global expr", func(t *testing.T) {
|
||||||
for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} {
|
for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} {
|
||||||
t.Run(ValueTypeName(vt), func(t *testing.T) {
|
t.Run(ValueTypeName(vt), func(t *testing.T) {
|
||||||
// Allocate bytes with enough size for all types.
|
expr := &ConstantExpression{}
|
||||||
expr := &ConstantExpression{Data: make([]byte, 8)}
|
|
||||||
switch vt {
|
switch vt {
|
||||||
case ValueTypeI32:
|
case ValueTypeI32:
|
||||||
expr.Data[0] = 1
|
expr.Data = []byte{1}
|
||||||
expr.Opcode = OpcodeI32Const
|
expr.Opcode = OpcodeI32Const
|
||||||
case ValueTypeI64:
|
case ValueTypeI64:
|
||||||
expr.Data[0] = 2
|
expr.Data = []byte{2}
|
||||||
expr.Opcode = OpcodeI64Const
|
expr.Opcode = OpcodeI64Const
|
||||||
case ValueTypeF32:
|
case ValueTypeF32:
|
||||||
binary.LittleEndian.PutUint32(expr.Data, math.Float32bits(math.MaxFloat32))
|
expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32))
|
||||||
expr.Opcode = OpcodeF32Const
|
expr.Opcode = OpcodeF32Const
|
||||||
case ValueTypeF64:
|
case ValueTypeF64:
|
||||||
binary.LittleEndian.PutUint64(expr.Data, math.Float64bits(math.MaxFloat64))
|
expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64))
|
||||||
expr.Opcode = OpcodeF64Const
|
expr.Opcode = OpcodeF64Const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func (m *Module) validateTable() ([]*validatedElementSegment, error) {
|
|||||||
|
|
||||||
ret = append(ret, &validatedElementSegment{oc, globalIdx, elem.Init})
|
ret = append(ret, &validatedElementSegment{oc, globalIdx, elem.Init})
|
||||||
} else if oc == OpcodeI32Const {
|
} 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s[%d] couldn't read i32.const parameter: %w", SectionIDName(SectionIDElement), idx, err)
|
return nil, fmt.Errorf("%s[%d] couldn't read i32.const parameter: %w", SectionIDName(SectionIDElement), idx, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/leb128"
|
"github.com/tetratelabs/wazero/internal/leb128"
|
||||||
|
"github.com/tetratelabs/wazero/internal/u64"
|
||||||
"github.com/tetratelabs/wazero/internal/wasm"
|
"github.com/tetratelabs/wazero/internal/wasm"
|
||||||
"github.com/tetratelabs/wazero/internal/wasm/binary"
|
"github.com/tetratelabs/wazero/internal/wasm/binary"
|
||||||
"github.com/tetratelabs/wazero/internal/wasm/interpreter"
|
"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))
|
// (global (export "global_f32") f32 (f32.const 666))
|
||||||
mod.GlobalSection = append(mod.GlobalSection, &wasm.Global{
|
mod.GlobalSection = append(mod.GlobalSection, &wasm.Global{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32},
|
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}
|
mod.ExportSection["global_f32"] = &wasm.Export{Name: "global_f32", Index: 1, Type: wasm.ExternTypeGlobal}
|
||||||
|
|
||||||
// (global (export "global_f64") f64 (f64.const 666))
|
// (global (export "global_f64") f64 (f64.const 666))
|
||||||
mod.GlobalSection = append(mod.GlobalSection, &wasm.Global{
|
mod.GlobalSection = append(mod.GlobalSection, &wasm.Global{
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64},
|
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}
|
mod.ExportSection["global_f64"] = &wasm.Export{Name: "global_f64", Index: 2, Type: wasm.ExternTypeGlobal}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user