Consistently uses LEB128 signed encoding for global constants (#443)
Global constants can be defined in wasm or in ModuleBuilder. In either case, they end up being decoded and interpreted during instantiation. This chooses signed encoding to avoid surprises. A more comprehensive explanation was added to RATIONALE.md, but the motivation was a global 100 coming out negative. Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
19
RATIONALE.md
19
RATIONALE.md
@@ -186,9 +186,26 @@ See https://github.com/bytecodealliance/wasmtime/blob/2ca01ae9478f199337cf743a6a
|
||||
|
||||
Their semantics match when `pathLen` == the length of `path`, so in practice this difference won't matter match.
|
||||
|
||||
## Signed encoding of integer global constant initializers
|
||||
wazero treats integer global constant initializers signed as their interpretation is not known at declaration time. For
|
||||
example, there is no signed integer [value type](https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#value-types%E2%91%A0).
|
||||
|
||||
To get at the problem, let's use an example.
|
||||
```
|
||||
(global (export "start_epoch") i64 (i64.const 1620216263544))
|
||||
```
|
||||
|
||||
In both signed and unsigned LEB128 encoding, this value is the same bit pattern. The problem is that some numbers are
|
||||
not. For example, 16256 is `807f` encoded as unsigned, but `80ff00` encoded as signed.
|
||||
|
||||
While the specification mentions uninterpreted integers are in abstract [unsigned values](https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#integers%E2%91%A0),
|
||||
the binary encoding is clear that they are encoded [signed](https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#integers%E2%91%A4).
|
||||
|
||||
For consistency, we go with signed encoding in the special case of global constant initializers.
|
||||
|
||||
## Implementation limitations
|
||||
|
||||
WebAssembly 1.0 (20191205) specification allows runtimes to [limit certain aspects of Wasm module or execution](https://www.w3.org/TR/2019/REC-2019/REC-wasm-core-1-20191205/#a2-implementation-limitations).
|
||||
WebAssembly 1.0 (20191205) specification allows runtimes to [limit certain aspects of Wasm module or execution](https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#a2-implementation-limitations).
|
||||
|
||||
wazero limitations are imposed pragmatically and described below.
|
||||
|
||||
|
||||
12
builder.go
12
builder.go
@@ -107,7 +107,7 @@ type ModuleBuilder interface {
|
||||
//
|
||||
// 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/#value-types%E2%91%A0
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-globaltype
|
||||
ExportGlobalI32(name string, v int32) ModuleBuilder
|
||||
|
||||
@@ -119,7 +119,7 @@ type ModuleBuilder interface {
|
||||
//
|
||||
// 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/#value-types%E2%91%A0
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-globaltype
|
||||
ExportGlobalI64(name string, v int64) ModuleBuilder
|
||||
|
||||
@@ -202,8 +202,8 @@ func (b *moduleBuilder) ExportMemoryWithMax(name string, minPages, maxPages uint
|
||||
func (b *moduleBuilder) ExportGlobalI32(name string, v int32) ModuleBuilder {
|
||||
b.nameToGlobal[name] = &wasm.Global{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
||||
// 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))},
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(v)},
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -212,8 +212,8 @@ func (b *moduleBuilder) ExportGlobalI32(name string, v int32) ModuleBuilder {
|
||||
func (b *moduleBuilder) ExportGlobalI64(name string, v int64) ModuleBuilder {
|
||||
b.nameToGlobal[name] = &wasm.Global{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64},
|
||||
// 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))},
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(v)},
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
GlobalSection: []*wasm.Global{
|
||||
{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeUint32(1024)},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(1024)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
@@ -262,7 +262,7 @@ func TestNewModuleBuilder_Build(t *testing.T) {
|
||||
GlobalSection: []*wasm.Global{
|
||||
{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeUint64(math.MaxInt64)},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(math.MaxInt64)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
|
||||
@@ -81,7 +81,7 @@ func (c *RuntimeConfig) WithContext(ctx context.Context) *RuntimeConfig {
|
||||
// * Zero is a valid value and results in a crash if any module uses memory.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem
|
||||
// See https://www.w3.org/TR/wasm-core-1/#memory-types%E2%91%A0
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-types%E2%91%A0
|
||||
func (c *RuntimeConfig) WithMemoryMaxPages(memoryMaxPages uint32) *RuntimeConfig {
|
||||
ret := c.clone()
|
||||
ret.memoryMaxPages = memoryMaxPages
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
// DecodeFloat32 decodes a float32 in IEEE 754 binary representation.
|
||||
// See https://www.w3.org/TR/wasm-core-1/#floating-point%E2%91%A2
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#floating-point%E2%91%A2
|
||||
func DecodeFloat32(r io.Reader) (float32, error) {
|
||||
buf := make([]byte, 4)
|
||||
_, err := io.ReadFull(r, buf)
|
||||
@@ -19,7 +19,7 @@ func DecodeFloat32(r io.Reader) (float32, error) {
|
||||
}
|
||||
|
||||
// DecodeFloat64 decodes a float64 in IEEE 754 binary representation.
|
||||
// See https://www.w3.org/TR/wasm-core-1/#floating-point%E2%91%A2
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#floating-point%E2%91%A2
|
||||
func DecodeFloat64(r io.Reader) (float64, error) {
|
||||
buf := make([]byte, 8)
|
||||
_, err := io.ReadFull(r, buf)
|
||||
|
||||
@@ -29,6 +29,40 @@ var encodeCache = [0x80][]byte{
|
||||
{0x70}, {0x71}, {0x72}, {0x73}, {0x74}, {0x75}, {0x76}, {0x77}, {0x78}, {0x79}, {0x7a}, {0x7b}, {0x7c}, {0x7d}, {0x7e}, {0x7f},
|
||||
}
|
||||
|
||||
// EncodeInt32 encodes the signed value into a buffer in LEB128 format
|
||||
//
|
||||
// See https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer
|
||||
func EncodeInt32(value int32) []byte {
|
||||
return EncodeInt64(int64(value))
|
||||
}
|
||||
|
||||
// EncodeInt64 encodes the signed value into a buffer in LEB128 format
|
||||
//
|
||||
// See https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer
|
||||
func EncodeInt64(value int64) (buf []byte) {
|
||||
for {
|
||||
// Take 7 remaining low-order bits from the value into b.
|
||||
b := uint8(value & 0x7f)
|
||||
// Extract the sign bit.
|
||||
s := uint8(value & 0x40)
|
||||
value >>= 7
|
||||
|
||||
// The encoding unsigned numbers is simpler as it only needs to check if the value is non-zero to tell if there
|
||||
// are more bits to encode. Signed is a little more complicated as you have to double-check the sign bit.
|
||||
// If either case, set the high-order bit to tell the reader there are more bytes in this int.
|
||||
if (value != -1 || s == 0) && (value != 0 || s != 0) {
|
||||
b |= 0x80
|
||||
}
|
||||
|
||||
// Append b into the buffer
|
||||
buf = append(buf, b)
|
||||
if b&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
// EncodeUint32 encodes the value into a buffer in LEB128 format
|
||||
//
|
||||
// See https://en.wikipedia.org/wiki/LEB128#Encode_unsigned_integer
|
||||
@@ -51,7 +85,7 @@ func EncodeUint64(value uint64) (buf []byte) {
|
||||
value = value >> 7
|
||||
|
||||
// If there are remaining bits, the value won't be zero: Set the high-
|
||||
// order bit to tell the reader there are more bytes in this uint32.
|
||||
// order bit to tell the reader there are more bytes in this uint.
|
||||
if value != 0 {
|
||||
b |= 0x80
|
||||
}
|
||||
|
||||
@@ -10,6 +10,58 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEncode_DecodeInt32(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
input int32
|
||||
expected []byte
|
||||
}{
|
||||
{input: -165675008, expected: []byte{0x80, 0x80, 0x80, 0xb1, 0x7f}},
|
||||
{input: -624485, expected: []byte{0x9b, 0xf1, 0x59}},
|
||||
{input: -16256, expected: []byte{0x80, 0x81, 0x7f}},
|
||||
{input: -4, expected: []byte{0x7c}},
|
||||
{input: -1, expected: []byte{0x7f}},
|
||||
{input: 0, expected: []byte{0x00}},
|
||||
{input: 1, expected: []byte{0x01}},
|
||||
{input: 4, expected: []byte{0x04}},
|
||||
{input: 16256, expected: []byte{0x80, 0xff, 0x0}},
|
||||
{input: 624485, expected: []byte{0xe5, 0x8e, 0x26}},
|
||||
{input: 165675008, expected: []byte{0x80, 0x80, 0x80, 0xcf, 0x0}},
|
||||
{input: int32(math.MaxInt32), expected: []byte{0xff, 0xff, 0xff, 0xff, 0x7}},
|
||||
} {
|
||||
require.Equal(t, c.expected, EncodeInt32(c.input))
|
||||
decoded, _, err := DecodeInt32(bytes.NewReader(c.expected))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.input, decoded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncode_DecodeInt64(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
input int64
|
||||
expected []byte
|
||||
}{
|
||||
{input: -math.MaxInt32, expected: []byte{0x81, 0x80, 0x80, 0x80, 0x78}},
|
||||
{input: -165675008, expected: []byte{0x80, 0x80, 0x80, 0xb1, 0x7f}},
|
||||
{input: -624485, expected: []byte{0x9b, 0xf1, 0x59}},
|
||||
{input: -16256, expected: []byte{0x80, 0x81, 0x7f}},
|
||||
{input: -4, expected: []byte{0x7c}},
|
||||
{input: -1, expected: []byte{0x7f}},
|
||||
{input: 0, expected: []byte{0x00}},
|
||||
{input: 1, expected: []byte{0x01}},
|
||||
{input: 4, expected: []byte{0x04}},
|
||||
{input: 16256, expected: []byte{0x80, 0xff, 0x0}},
|
||||
{input: 624485, expected: []byte{0xe5, 0x8e, 0x26}},
|
||||
{input: 165675008, expected: []byte{0x80, 0x80, 0x80, 0xcf, 0x0}},
|
||||
{input: math.MaxInt32, expected: []byte{0xff, 0xff, 0xff, 0xff, 0x7}},
|
||||
{input: math.MaxInt64, expected: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0}},
|
||||
} {
|
||||
require.Equal(t, c.expected, EncodeInt64(c.input))
|
||||
decoded, _, err := DecodeInt64(bytes.NewReader(c.expected))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.input, decoded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeUint32(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
input uint32
|
||||
|
||||
@@ -21,8 +21,10 @@ func decodeConstantExpression(r *bytes.Reader) (*wasm.ConstantExpression, error)
|
||||
opcode := b
|
||||
switch opcode {
|
||||
case wasm.OpcodeI32Const:
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
_, _, err = leb128.DecodeInt32(r)
|
||||
case wasm.OpcodeI64Const:
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
_, _, err = leb128.DecodeInt64(r)
|
||||
case wasm.OpcodeF32Const:
|
||||
_, err = ieee754.DecodeFloat32(r)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
@@ -180,7 +181,7 @@ func TestModule_Encode(t *testing.T) {
|
||||
GlobalSection: []*wasm.Global{
|
||||
{
|
||||
Type: &wasm.GlobalType{ValType: i32, Mutable: true},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0}},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(0)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
@@ -18,7 +19,7 @@ func TestEncodeGlobal(t *testing.T) {
|
||||
name: "const",
|
||||
input: &wasm.Global{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{1}},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(1)},
|
||||
},
|
||||
expected: []byte{
|
||||
wasm.ValueTypeI32, 0x00, // 0 == const
|
||||
@@ -29,7 +30,7 @@ func TestEncodeGlobal(t *testing.T) {
|
||||
name: "var",
|
||||
input: &wasm.Global{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{1}},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(1)},
|
||||
},
|
||||
expected: []byte{
|
||||
wasm.ValueTypeI32, 0x01, // 1 == var
|
||||
|
||||
@@ -203,7 +203,7 @@ func TestModule_ImportGlobalCount(t *testing.T) {
|
||||
func TestModule_SectionElementCount(t *testing.T) {
|
||||
i32, f32 := ValueTypeI32, ValueTypeF32
|
||||
zero := uint32(0)
|
||||
empty := &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x00}}
|
||||
empty := &ConstantExpression{Opcode: OpcodeI32Const, Data: const0}
|
||||
fn := reflect.ValueOf(func(api.Module) {})
|
||||
|
||||
tests := []struct {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/u64"
|
||||
)
|
||||
|
||||
@@ -156,7 +157,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
GlobalSection: []*Global{
|
||||
{
|
||||
Type: &GlobalType{ValType: ValueTypeI32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{1}},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -167,7 +168,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
GlobalSection: []*Global{
|
||||
{
|
||||
Type: &GlobalType{ValType: ValueTypeI32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{1}},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
@@ -180,7 +181,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
GlobalSection: []*Global{
|
||||
{
|
||||
Type: &GlobalType{ValType: ValueTypeI64},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI64Const, Data: []byte{1}},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI64Const, Data: leb128.EncodeInt64(1)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
@@ -223,7 +224,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
GlobalSection: []*Global{
|
||||
{
|
||||
Type: &GlobalType{ValType: ValueTypeI32, Mutable: true},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{1}},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(1)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
@@ -238,7 +239,7 @@ func TestPublicModule_Global(t *testing.T) {
|
||||
GlobalSection: []*Global{
|
||||
{
|
||||
Type: &GlobalType{ValType: ValueTypeI64, Mutable: true},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI64Const, Data: []byte{1}},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI64Const, Data: leb128.EncodeInt64(1)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
|
||||
|
||||
@@ -93,22 +93,22 @@ func TestNewHostModule(t *testing.T) {
|
||||
nameToGlobal: map[string]*Global{
|
||||
"g2": {
|
||||
Type: &GlobalType{ValType: i32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(2)},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)},
|
||||
},
|
||||
"g1": {
|
||||
Type: &GlobalType{ValType: i32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(1)},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
},
|
||||
},
|
||||
expected: &Module{
|
||||
GlobalSection: []*Global{
|
||||
{
|
||||
Type: &GlobalType{ValType: i32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(1)},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
},
|
||||
{
|
||||
Type: &GlobalType{ValType: i32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(2)},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*Export{
|
||||
@@ -129,7 +129,7 @@ func TestNewHostModule(t *testing.T) {
|
||||
nameToGlobal: map[string]*Global{
|
||||
"g": {
|
||||
Type: &GlobalType{ValType: i32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(1)},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
},
|
||||
},
|
||||
expected: &Module{
|
||||
@@ -141,7 +141,7 @@ func TestNewHostModule(t *testing.T) {
|
||||
GlobalSection: []*Global{
|
||||
{
|
||||
Type: &GlobalType{ValType: i32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(1)},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
},
|
||||
},
|
||||
MemorySection: &Memory{Min: 1, Max: 1},
|
||||
|
||||
@@ -169,7 +169,7 @@ func (m *MemoryInstance) PageSize() (result uint32) {
|
||||
|
||||
// PagesToUnitOfBytes converts the pages to a human-readable form similar to what's specified. Ex. 1 -> "64Ki"
|
||||
//
|
||||
// See https://www.w3.org/TR/wasm-core-1/#memory-instances%E2%91%A0
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-instances%E2%91%A0
|
||||
func PagesToUnitOfBytes(pages uint32) string {
|
||||
k := pages * 64
|
||||
if k < 1024 {
|
||||
|
||||
@@ -152,14 +152,14 @@ type Module struct {
|
||||
// When present, the CodeSection must be nil.
|
||||
//
|
||||
// Note: This section currently has no serialization format, so is not encodable.
|
||||
// See https://www.w3.org/TR/wasm-core-1/#host-functions%E2%91%A2
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#host-functions%E2%91%A2
|
||||
HostFunctionSection []*reflect.Value
|
||||
|
||||
// elementSegments are built on Validate when SectionIDElement is non-empty and all inputs are valid.
|
||||
//
|
||||
// Note: elementSegments retain Module.ElementSection order. Since an ElementSegment can overlap with another, order
|
||||
// preservation ensures a consistent initialization result.
|
||||
// See https://www.w3.org/TR/wasm-core-1/#table-instances%E2%91%A0
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#table-instances%E2%91%A0
|
||||
validatedElementSegments []*validatedElementSegment
|
||||
}
|
||||
|
||||
@@ -393,12 +393,14 @@ func validateConstExpression(globals []*GlobalType, expr *ConstantExpression, ex
|
||||
r := bytes.NewReader(expr.Data)
|
||||
switch expr.Opcode {
|
||||
case OpcodeI32Const:
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
_, _, err = leb128.DecodeInt32(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read i32: %w", err)
|
||||
}
|
||||
actualType = ValueTypeI32
|
||||
case OpcodeI64Const:
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
_, _, err = leb128.DecodeInt64(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read i64: %w", err)
|
||||
|
||||
@@ -368,7 +368,7 @@ func TestModule_validateGlobals(t *testing.T) {
|
||||
m := Module{GlobalSection: []*Global{
|
||||
{
|
||||
Type: &GlobalType{ValType: ValueTypeI32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
||||
},
|
||||
}}
|
||||
err := m.validateGlobals(nil, 9)
|
||||
@@ -500,7 +500,7 @@ func TestModule_validateMemory(t *testing.T) {
|
||||
Init: []byte{0x1},
|
||||
OffsetExpression: &ConstantExpression{
|
||||
Opcode: OpcodeI32Const,
|
||||
Data: []byte{0x1},
|
||||
Data: leb128.EncodeInt32(1),
|
||||
},
|
||||
}}}
|
||||
err := m.validateMemory(&Memory{}, nil)
|
||||
@@ -670,7 +670,7 @@ func TestModule_buildGlobalInstances(t *testing.T) {
|
||||
{
|
||||
Type: &GlobalType{Mutable: false, ValType: ValueTypeI32},
|
||||
Init: &ConstantExpression{Opcode: OpcodeI32Const,
|
||||
Data: leb128.EncodeUint32(math.MaxInt32)},
|
||||
Data: leb128.EncodeInt32(math.MaxInt32)},
|
||||
},
|
||||
}}
|
||||
|
||||
|
||||
@@ -474,8 +474,10 @@ func executeConstExpression(globals []*GlobalInstance, expr *ConstantExpression)
|
||||
r := bytes.NewReader(expr.Data)
|
||||
switch expr.Opcode {
|
||||
case OpcodeI32Const:
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
v, _, _ = leb128.DecodeInt32(r)
|
||||
case OpcodeI64Const:
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
v, _, _ = leb128.DecodeInt64(r)
|
||||
case OpcodeF32Const:
|
||||
v, _ = ieee754.DecodeFloat32(r)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/testing/hammer"
|
||||
"github.com/tetratelabs/wazero/internal/u64"
|
||||
)
|
||||
@@ -148,7 +149,7 @@ func TestStore_CloseModule(t *testing.T) {
|
||||
TypeSection: []*FunctionType{{}},
|
||||
ImportSection: []*Import{{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0}},
|
||||
MemorySection: &Memory{Min: 1},
|
||||
GlobalSection: []*Global{{Type: &GlobalType{}, Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}}}},
|
||||
GlobalSection: []*Global{{Type: &GlobalType{}, Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}}},
|
||||
TableSection: &Table{Min: 10},
|
||||
}, importingModuleName, nil)
|
||||
require.NoError(t, err)
|
||||
@@ -192,7 +193,7 @@ func TestStore_hammer(t *testing.T) {
|
||||
FunctionSection: []uint32{0},
|
||||
CodeSection: []*Code{{Body: []byte{OpcodeEnd}}},
|
||||
MemorySection: &Memory{Min: 1},
|
||||
GlobalSection: []*Global{{Type: &GlobalType{}, Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}}}},
|
||||
GlobalSection: []*Global{{Type: &GlobalType{}, Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}}},
|
||||
TableSection: &Table{Min: 10},
|
||||
ImportSection: []*Import{
|
||||
{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0},
|
||||
@@ -680,21 +681,21 @@ func TestModuleInstance_validateData(t *testing.T) {
|
||||
{
|
||||
name: "ok",
|
||||
data: []*DataSegment{
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}}, Init: []byte{0}},
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x2}}, Init: []byte{0}},
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []byte{0}},
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)}, Init: []byte{0}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "out of bounds - single one byte",
|
||||
data: []*DataSegment{
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x5}}, Init: []byte{0}},
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(5)}, Init: []byte{0}},
|
||||
},
|
||||
expErr: true,
|
||||
},
|
||||
{
|
||||
name: "out of bounds - multi bytes",
|
||||
data: []*DataSegment{
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x3}}, Init: []byte{0, 1, 2}},
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(3)}, Init: []byte{0, 1, 2}},
|
||||
},
|
||||
expErr: true,
|
||||
},
|
||||
@@ -714,8 +715,8 @@ func TestModuleInstance_validateData(t *testing.T) {
|
||||
func TestModuleInstance_applyData(t *testing.T) {
|
||||
m := &ModuleInstance{Memory: &MemoryInstance{Buffer: make([]byte, 10)}}
|
||||
m.applyData([]*DataSegment{
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}}, Init: []byte{0xa, 0xf}},
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x8}}, Init: []byte{0x1, 0x5}},
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0}, Init: []byte{0xa, 0xf}},
|
||||
{OffsetExpression: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeUint32(8)}, Init: []byte{0x1, 0x5}},
|
||||
})
|
||||
require.Equal(t, []byte{0xa, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5}, m.Memory.Buffer)
|
||||
}
|
||||
|
||||
@@ -113,7 +113,8 @@ func (m *Module) validateTable() ([]*validatedElementSegment, error) {
|
||||
|
||||
ret = append(ret, &validatedElementSegment{oc, globalIdx, elem.Init})
|
||||
} else if oc == OpcodeI32Const {
|
||||
o, _, err := leb128.DecodeUint32(bytes.NewReader(elem.OffsetExpr.Data))
|
||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
||||
o, _, err := leb128.DecodeInt32(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)
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func TestModule_validateTable(t *testing.T) {
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0}},
|
||||
},
|
||||
},
|
||||
expected: []*validatedElementSegment{},
|
||||
@@ -94,7 +94,7 @@ func TestModule_validateTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
||||
Init: []Index{0},
|
||||
},
|
||||
},
|
||||
@@ -112,7 +112,7 @@ func TestModule_validateTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
||||
Init: []Index{0},
|
||||
},
|
||||
},
|
||||
@@ -130,7 +130,7 @@ func TestModule_validateTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
||||
Init: []Index{0},
|
||||
},
|
||||
},
|
||||
@@ -148,7 +148,7 @@ func TestModule_validateTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
Init: []Index{0, 2},
|
||||
},
|
||||
},
|
||||
@@ -271,7 +271,7 @@ func TestModule_validateTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
Init: []Index{0, 2},
|
||||
},
|
||||
{
|
||||
@@ -334,7 +334,7 @@ func TestModule_validateTable_Errors(t *testing.T) {
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI64Const, Data: []byte{0x0}}, Init: []Index{0}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI64Const, Data: const0}, Init: []Index{0}},
|
||||
},
|
||||
},
|
||||
expectedErr: "element[0] has an invalid const expression: i64.const",
|
||||
@@ -346,7 +346,7 @@ func TestModule_validateTable_Errors(t *testing.T) {
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}}, Init: []Index{0}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0}, Init: []Index{0}},
|
||||
},
|
||||
},
|
||||
expectedErr: "element was defined, but not table",
|
||||
@@ -359,7 +359,7 @@ func TestModule_validateTable_Errors(t *testing.T) {
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x2}}, Init: []Index{0}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)}, Init: []Index{0}},
|
||||
},
|
||||
},
|
||||
expectedErr: "element[0].init exceeds min table size",
|
||||
@@ -372,8 +372,8 @@ func TestModule_validateTable_Errors(t *testing.T) {
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}}, Init: []Index{0}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}}, Init: []Index{0, 0}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []Index{0}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []Index{0, 0}},
|
||||
},
|
||||
},
|
||||
expectedErr: "element[1].init exceeds min table size",
|
||||
@@ -386,7 +386,7 @@ func TestModule_validateTable_Errors(t *testing.T) {
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x2}}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)}},
|
||||
},
|
||||
},
|
||||
expectedErr: "element[0].init exceeds min table size",
|
||||
@@ -399,7 +399,7 @@ func TestModule_validateTable_Errors(t *testing.T) {
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}}, Init: []Index{0, 1}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []Index{0, 1}},
|
||||
},
|
||||
},
|
||||
expectedErr: "element[0].init[1] funcidx 1 out of range",
|
||||
@@ -513,6 +513,9 @@ func TestModule_validateTable_Errors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var const0 = leb128.EncodeInt32(0)
|
||||
var const1 = leb128.EncodeInt32(1)
|
||||
|
||||
func TestModule_buildTable(t *testing.T) {
|
||||
three := uint32(3)
|
||||
tests := []struct {
|
||||
@@ -553,7 +556,7 @@ func TestModule_buildTable(t *testing.T) {
|
||||
FunctionSection: []Index{0},
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}}},
|
||||
{OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0}},
|
||||
},
|
||||
validatedElementSegments: []*validatedElementSegment{},
|
||||
},
|
||||
@@ -568,7 +571,7 @@ func TestModule_buildTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
||||
Init: []Index{0},
|
||||
},
|
||||
},
|
||||
@@ -588,7 +591,7 @@ func TestModule_buildTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
||||
Init: []Index{0},
|
||||
},
|
||||
},
|
||||
@@ -606,7 +609,7 @@ func TestModule_buildTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
||||
Init: []Index{0},
|
||||
},
|
||||
},
|
||||
@@ -624,7 +627,7 @@ func TestModule_buildTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
Init: []Index{0, 2},
|
||||
},
|
||||
},
|
||||
@@ -766,7 +769,7 @@ func TestModule_buildTable(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd, codeEnd, codeEnd, codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x1}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
||||
Init: []Index{0, 2},
|
||||
},
|
||||
{
|
||||
@@ -822,7 +825,7 @@ func TestModule_buildTable_Errors(t *testing.T) {
|
||||
CodeSection: []*Code{codeEnd},
|
||||
ElementSection: []*ElementSegment{
|
||||
{
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0x0}},
|
||||
OffsetExpr: &ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
|
||||
Init: []Index{0},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -245,7 +245,7 @@ func addSpectestModule(t *testing.T, store *wasm.Store) {
|
||||
// (global (export "global_i32") i32 (i32.const 666))
|
||||
mod.GlobalSection = append(mod.GlobalSection, &wasm.Global{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeUint32(666)},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(666)},
|
||||
})
|
||||
mod.ExportSection["global_i32"] = &wasm.Export{Name: "global_i32", Index: 0, Type: wasm.ExternTypeGlobal}
|
||||
|
||||
|
||||
11
wasm_test.go
11
wasm_test.go
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
"github.com/tetratelabs/wazero/internal/wasm/binary"
|
||||
)
|
||||
@@ -164,6 +165,8 @@ func TestModule_Memory(t *testing.T) {
|
||||
|
||||
// TestModule_Global only covers a couple cases to avoid duplication of internal/wasm/global_test.go
|
||||
func TestModule_Global(t *testing.T) {
|
||||
globalVal := int64(100) // intentionally a value that differs in signed vs unsigned encoding
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
module *wasm.Module // module as wat doesn't yet support globals
|
||||
@@ -180,7 +183,7 @@ func TestModule_Global(t *testing.T) {
|
||||
GlobalSection: []*wasm.Global{
|
||||
{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64, Mutable: true},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: []byte{1}},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(globalVal)},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -188,7 +191,7 @@ func TestModule_Global(t *testing.T) {
|
||||
{
|
||||
name: "global exported",
|
||||
builder: func(r Runtime) ModuleBuilder {
|
||||
return r.NewModuleBuilder(t.Name()).ExportGlobalI64("global", 1)
|
||||
return r.NewModuleBuilder(t.Name()).ExportGlobalI64("global", globalVal)
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
@@ -198,7 +201,7 @@ func TestModule_Global(t *testing.T) {
|
||||
GlobalSection: []*wasm.Global{
|
||||
{
|
||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64, Mutable: true},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: []byte{1}},
|
||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(globalVal)},
|
||||
},
|
||||
},
|
||||
ExportSection: map[string]*wasm.Export{
|
||||
@@ -232,7 +235,7 @@ func TestModule_Global(t *testing.T) {
|
||||
require.Nil(t, global)
|
||||
return
|
||||
}
|
||||
require.Equal(t, uint64(1), global.Get())
|
||||
require.Equal(t, uint64(globalVal), global.Get())
|
||||
|
||||
mutable, ok := global.(api.MutableGlobal)
|
||||
require.Equal(t, tc.expectedMutable, ok)
|
||||
|
||||
Reference in New Issue
Block a user