Removes context parameter from instruction-scoped operations (#923)
We originally had a `context.Context` for anything that might be traced, but it turned out to be only useful for lifecycle and host functions. For instruction-scoped aspects like memory updates, a context parameter is too fine-grained and also invisible in practice. For example, most users will use the compiler engine, and its memory, global or table access will never use go's context. Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
@@ -20,12 +19,12 @@ func (g *mutableGlobal) Type() api.ValueType {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on api.Global.
|
||||
func (g *mutableGlobal) Get(context.Context) uint64 {
|
||||
func (g *mutableGlobal) Get() uint64 {
|
||||
return g.g.Val
|
||||
}
|
||||
|
||||
// Set implements the same method as documented on api.MutableGlobal.
|
||||
func (g *mutableGlobal) Set(_ context.Context, v uint64) {
|
||||
func (g *mutableGlobal) Set(v uint64) {
|
||||
g.g.Val = v
|
||||
}
|
||||
|
||||
@@ -33,11 +32,11 @@ func (g *mutableGlobal) Set(_ context.Context, v uint64) {
|
||||
func (g *mutableGlobal) String() string {
|
||||
switch g.Type() {
|
||||
case ValueTypeI32, ValueTypeI64:
|
||||
return fmt.Sprintf("global(%d)", g.Get(context.Background()))
|
||||
return fmt.Sprintf("global(%d)", g.Get())
|
||||
case ValueTypeF32:
|
||||
return fmt.Sprintf("global(%f)", api.DecodeF32(g.Get(context.Background())))
|
||||
return fmt.Sprintf("global(%f)", api.DecodeF32(g.Get()))
|
||||
case ValueTypeF64:
|
||||
return fmt.Sprintf("global(%f)", api.DecodeF64(g.Get(context.Background())))
|
||||
return fmt.Sprintf("global(%f)", api.DecodeF64(g.Get()))
|
||||
default:
|
||||
panic(fmt.Errorf("BUG: unknown value type %X", g.Type()))
|
||||
}
|
||||
@@ -54,7 +53,7 @@ func (g globalI32) Type() api.ValueType {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on api.Global.
|
||||
func (g globalI32) Get(context.Context) uint64 {
|
||||
func (g globalI32) Get() uint64 {
|
||||
return uint64(g)
|
||||
}
|
||||
|
||||
@@ -74,7 +73,7 @@ func (g globalI64) Type() api.ValueType {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on api.Global.
|
||||
func (g globalI64) Get(context.Context) uint64 {
|
||||
func (g globalI64) Get() uint64 {
|
||||
return uint64(g)
|
||||
}
|
||||
|
||||
@@ -94,13 +93,13 @@ func (g globalF32) Type() api.ValueType {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on api.Global.
|
||||
func (g globalF32) Get(context.Context) uint64 {
|
||||
func (g globalF32) Get() uint64 {
|
||||
return uint64(g)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (g globalF32) String() string {
|
||||
return fmt.Sprintf("global(%f)", api.DecodeF32(g.Get(context.Background())))
|
||||
return fmt.Sprintf("global(%f)", api.DecodeF32(g.Get()))
|
||||
}
|
||||
|
||||
type globalF64 uint64
|
||||
@@ -114,11 +113,11 @@ func (g globalF64) Type() api.ValueType {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on api.Global.
|
||||
func (g globalF64) Get(context.Context) uint64 {
|
||||
func (g globalF64) Get() uint64 {
|
||||
return uint64(g)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (g globalF64) String() string {
|
||||
return fmt.Sprintf("global(%f)", api.DecodeF64(g.Get(context.Background())))
|
||||
return fmt.Sprintf("global(%f)", api.DecodeF64(g.Get()))
|
||||
}
|
||||
|
||||
@@ -126,20 +126,18 @@ func TestGlobalTypes(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
require.Equal(t, tc.expectedType, tc.global.Type())
|
||||
require.Equal(t, tc.expectedVal, tc.global.Get(ctx))
|
||||
require.Equal(t, tc.expectedString, tc.global.String())
|
||||
require.Equal(t, tc.expectedType, tc.global.Type())
|
||||
require.Equal(t, tc.expectedVal, tc.global.Get())
|
||||
require.Equal(t, tc.expectedString, tc.global.String())
|
||||
|
||||
mutable, ok := tc.global.(api.MutableGlobal)
|
||||
require.Equal(t, tc.expectedMutable, ok)
|
||||
if ok {
|
||||
mutable.Set(ctx, 2)
|
||||
require.Equal(t, uint64(2), tc.global.Get(ctx))
|
||||
mutable, ok := tc.global.(api.MutableGlobal)
|
||||
require.Equal(t, tc.expectedMutable, ok)
|
||||
if ok {
|
||||
mutable.Set(2)
|
||||
require.Equal(t, uint64(2), tc.global.Get())
|
||||
|
||||
mutable.Set(ctx, tc.expectedVal) // Set it back!
|
||||
require.Equal(t, tc.expectedVal, tc.global.Get(ctx))
|
||||
}
|
||||
mutable.Set(tc.expectedVal) // Set it back!
|
||||
require.Equal(t, tc.expectedVal, tc.global.Get())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
@@ -59,12 +58,12 @@ func (m *MemoryInstance) Definition() api.MemoryDefinition {
|
||||
}
|
||||
|
||||
// Size implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) Size(context.Context) uint32 {
|
||||
func (m *MemoryInstance) Size() uint32 {
|
||||
return m.size()
|
||||
}
|
||||
|
||||
// ReadByte implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) ReadByte(_ context.Context, offset uint32) (byte, bool) {
|
||||
func (m *MemoryInstance) ReadByte(offset uint32) (byte, bool) {
|
||||
if offset >= m.size() {
|
||||
return 0, false
|
||||
}
|
||||
@@ -72,7 +71,7 @@ func (m *MemoryInstance) ReadByte(_ context.Context, offset uint32) (byte, bool)
|
||||
}
|
||||
|
||||
// ReadUint16Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) ReadUint16Le(_ context.Context, offset uint32) (uint16, bool) {
|
||||
func (m *MemoryInstance) ReadUint16Le(offset uint32) (uint16, bool) {
|
||||
if !m.hasSize(offset, 2) {
|
||||
return 0, false
|
||||
}
|
||||
@@ -80,12 +79,12 @@ func (m *MemoryInstance) ReadUint16Le(_ context.Context, offset uint32) (uint16,
|
||||
}
|
||||
|
||||
// ReadUint32Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) ReadUint32Le(_ context.Context, offset uint32) (uint32, bool) {
|
||||
func (m *MemoryInstance) ReadUint32Le(offset uint32) (uint32, bool) {
|
||||
return m.readUint32Le(offset)
|
||||
}
|
||||
|
||||
// ReadFloat32Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) ReadFloat32Le(_ context.Context, offset uint32) (float32, bool) {
|
||||
func (m *MemoryInstance) ReadFloat32Le(offset uint32) (float32, bool) {
|
||||
v, ok := m.readUint32Le(offset)
|
||||
if !ok {
|
||||
return 0, false
|
||||
@@ -94,12 +93,12 @@ func (m *MemoryInstance) ReadFloat32Le(_ context.Context, offset uint32) (float3
|
||||
}
|
||||
|
||||
// ReadUint64Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) ReadUint64Le(_ context.Context, offset uint32) (uint64, bool) {
|
||||
func (m *MemoryInstance) ReadUint64Le(offset uint32) (uint64, bool) {
|
||||
return m.readUint64Le(offset)
|
||||
}
|
||||
|
||||
// ReadFloat64Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) ReadFloat64Le(_ context.Context, offset uint32) (float64, bool) {
|
||||
func (m *MemoryInstance) ReadFloat64Le(offset uint32) (float64, bool) {
|
||||
v, ok := m.readUint64Le(offset)
|
||||
if !ok {
|
||||
return 0, false
|
||||
@@ -108,7 +107,7 @@ func (m *MemoryInstance) ReadFloat64Le(_ context.Context, offset uint32) (float6
|
||||
}
|
||||
|
||||
// Read implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) Read(_ context.Context, offset, byteCount uint32) ([]byte, bool) {
|
||||
func (m *MemoryInstance) Read(offset, byteCount uint32) ([]byte, bool) {
|
||||
if !m.hasSize(offset, byteCount) {
|
||||
return nil, false
|
||||
}
|
||||
@@ -116,7 +115,7 @@ func (m *MemoryInstance) Read(_ context.Context, offset, byteCount uint32) ([]by
|
||||
}
|
||||
|
||||
// WriteByte implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) WriteByte(_ context.Context, offset uint32, v byte) bool {
|
||||
func (m *MemoryInstance) WriteByte(offset uint32, v byte) bool {
|
||||
if offset >= m.size() {
|
||||
return false
|
||||
}
|
||||
@@ -125,7 +124,7 @@ func (m *MemoryInstance) WriteByte(_ context.Context, offset uint32, v byte) boo
|
||||
}
|
||||
|
||||
// WriteUint16Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) WriteUint16Le(_ context.Context, offset uint32, v uint16) bool {
|
||||
func (m *MemoryInstance) WriteUint16Le(offset uint32, v uint16) bool {
|
||||
if !m.hasSize(offset, 2) {
|
||||
return false
|
||||
}
|
||||
@@ -134,27 +133,27 @@ func (m *MemoryInstance) WriteUint16Le(_ context.Context, offset uint32, v uint1
|
||||
}
|
||||
|
||||
// WriteUint32Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) WriteUint32Le(_ context.Context, offset, v uint32) bool {
|
||||
func (m *MemoryInstance) WriteUint32Le(offset, v uint32) bool {
|
||||
return m.writeUint32Le(offset, v)
|
||||
}
|
||||
|
||||
// WriteFloat32Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) WriteFloat32Le(_ context.Context, offset uint32, v float32) bool {
|
||||
func (m *MemoryInstance) WriteFloat32Le(offset uint32, v float32) bool {
|
||||
return m.writeUint32Le(offset, math.Float32bits(v))
|
||||
}
|
||||
|
||||
// WriteUint64Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) WriteUint64Le(_ context.Context, offset uint32, v uint64) bool {
|
||||
func (m *MemoryInstance) WriteUint64Le(offset uint32, v uint64) bool {
|
||||
return m.writeUint64Le(offset, v)
|
||||
}
|
||||
|
||||
// WriteFloat64Le implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) WriteFloat64Le(_ context.Context, offset uint32, v float64) bool {
|
||||
func (m *MemoryInstance) WriteFloat64Le(offset uint32, v float64) bool {
|
||||
return m.writeUint64Le(offset, math.Float64bits(v))
|
||||
}
|
||||
|
||||
// Write implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) Write(_ context.Context, offset uint32, val []byte) bool {
|
||||
func (m *MemoryInstance) Write(offset uint32, val []byte) bool {
|
||||
if !m.hasSize(offset, uint32(len(val))) {
|
||||
return false
|
||||
}
|
||||
@@ -163,7 +162,7 @@ func (m *MemoryInstance) Write(_ context.Context, offset uint32, val []byte) boo
|
||||
}
|
||||
|
||||
// WriteString implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) WriteString(_ context.Context, offset uint32, val string) bool {
|
||||
func (m *MemoryInstance) WriteString(offset uint32, val string) bool {
|
||||
if !m.hasSize(offset, uint32(len(val))) {
|
||||
return false
|
||||
}
|
||||
@@ -177,7 +176,7 @@ func MemoryPagesToBytesNum(pages uint32) (bytesNum uint64) {
|
||||
}
|
||||
|
||||
// Grow implements the same method as documented on api.Memory.
|
||||
func (m *MemoryInstance) Grow(_ context.Context, delta uint32) (result uint32, ok bool) {
|
||||
func (m *MemoryInstance) Grow(delta uint32) (result uint32, ok bool) {
|
||||
// We take write-lock here as the following might result in a new slice
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
@@ -203,7 +202,7 @@ func (m *MemoryInstance) Grow(_ context.Context, delta uint32) (result uint32, o
|
||||
}
|
||||
|
||||
// PageSize returns the current memory buffer size in pages.
|
||||
func (m *MemoryInstance) PageSize(context.Context) (result uint32) {
|
||||
func (m *MemoryInstance) PageSize() (result uint32) {
|
||||
return memoryBytesNumToPages(uint64(len(m.Buffer)))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -31,20 +30,16 @@ func TestMemoryBytesNumToPages(t *testing.T) {
|
||||
func TestMemoryInstance_Grow_Size(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
capEqualsMax bool
|
||||
}{
|
||||
{name: "nil context"},
|
||||
{name: "context", ctx: testCtx},
|
||||
{name: "nil context, capEqualsMax", capEqualsMax: true},
|
||||
{name: "context, capEqualsMax", ctx: testCtx, capEqualsMax: true},
|
||||
{name: ""},
|
||||
{name: "capEqualsMax", capEqualsMax: true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := tc.ctx
|
||||
max := uint32(10)
|
||||
maxBytes := MemoryPagesToBytesNum(max)
|
||||
var m *MemoryInstance
|
||||
@@ -54,35 +49,35 @@ func TestMemoryInstance_Grow_Size(t *testing.T) {
|
||||
m = &MemoryInstance{Max: max, Buffer: make([]byte, 0)}
|
||||
}
|
||||
|
||||
res, ok := m.Grow(ctx, 5)
|
||||
res, ok := m.Grow(5)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, uint32(0), res)
|
||||
require.Equal(t, uint32(5), m.PageSize(ctx))
|
||||
require.Equal(t, uint32(5), m.PageSize())
|
||||
|
||||
// Zero page grow is well-defined, should return the current page correctly.
|
||||
res, ok = m.Grow(ctx, 0)
|
||||
res, ok = m.Grow(0)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, uint32(5), res)
|
||||
require.Equal(t, uint32(5), m.PageSize(ctx))
|
||||
require.Equal(t, uint32(5), m.PageSize())
|
||||
|
||||
res, ok = m.Grow(ctx, 4)
|
||||
res, ok = m.Grow(4)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, uint32(5), res)
|
||||
require.Equal(t, uint32(9), m.PageSize(ctx))
|
||||
require.Equal(t, uint32(9), m.PageSize())
|
||||
|
||||
// At this point, the page size equal 9,
|
||||
// so trying to grow two pages should result in failure.
|
||||
_, ok = m.Grow(ctx, 2)
|
||||
_, ok = m.Grow(2)
|
||||
require.False(t, ok)
|
||||
require.Equal(t, uint32(9), m.PageSize(ctx))
|
||||
require.Equal(t, uint32(9), m.PageSize())
|
||||
|
||||
// But growing one page is still permitted.
|
||||
res, ok = m.Grow(ctx, 1)
|
||||
res, ok = m.Grow(1)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, uint32(9), res)
|
||||
|
||||
// Ensure that the current page size equals the max.
|
||||
require.Equal(t, max, m.PageSize(ctx))
|
||||
require.Equal(t, max, m.PageSize())
|
||||
|
||||
if tc.capEqualsMax { // Ensure the capacity isn't more than max.
|
||||
require.Equal(t, maxBytes, uint64(cap(m.Buffer)))
|
||||
@@ -94,18 +89,16 @@ func TestMemoryInstance_Grow_Size(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMemoryInstance_ReadByte(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 0, 0, 0, 16}, Min: 1}
|
||||
v, ok := mem.ReadByte(ctx, 7)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, byte(16), v)
|
||||
mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 0, 0, 0, 16}, Min: 1}
|
||||
v, ok := mem.ReadByte(7)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, byte(16), v)
|
||||
|
||||
_, ok = mem.ReadByte(ctx, 8)
|
||||
require.False(t, ok)
|
||||
_, ok = mem.ReadByte(8)
|
||||
require.False(t, ok)
|
||||
|
||||
_, ok = mem.ReadByte(ctx, 9)
|
||||
require.False(t, ok)
|
||||
}
|
||||
_, ok = mem.ReadByte(9)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestPagesToUnitOfBytes(t *testing.T) {
|
||||
@@ -167,19 +160,19 @@ func TestMemoryInstance_HasSize(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "maximum valid sizeInBytes",
|
||||
offset: memory.Size(testCtx) - 8,
|
||||
offset: memory.Size() - 8,
|
||||
sizeInBytes: 8,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "sizeInBytes exceeds the valid size by 1",
|
||||
offset: 100, // arbitrary valid offset
|
||||
sizeInBytes: uint64(memory.Size(testCtx) - 99),
|
||||
sizeInBytes: uint64(memory.Size() - 99),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "offset exceeds the memory size",
|
||||
offset: memory.Size(testCtx),
|
||||
offset: memory.Size(),
|
||||
sizeInBytes: 1, // arbitrary size
|
||||
expected: false,
|
||||
},
|
||||
@@ -246,13 +239,11 @@ func TestMemoryInstance_ReadUint16Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
|
||||
v, ok := memory.ReadUint16Le(ctx, tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
}
|
||||
v, ok := memory.ReadUint16Le(tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -297,13 +288,11 @@ func TestMemoryInstance_ReadUint32Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
|
||||
v, ok := memory.ReadUint32Le(ctx, tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
}
|
||||
v, ok := memory.ReadUint32Le(tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -348,13 +337,11 @@ func TestMemoryInstance_ReadUint64Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
|
||||
v, ok := memory.ReadUint64Le(ctx, tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
}
|
||||
v, ok := memory.ReadUint64Le(tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -399,13 +386,11 @@ func TestMemoryInstance_ReadFloat32Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
|
||||
v, ok := memory.ReadFloat32Le(ctx, tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
}
|
||||
v, ok := memory.ReadFloat32Le(tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -450,36 +435,32 @@ func TestMemoryInstance_ReadFloat64Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
memory := &MemoryInstance{Buffer: tc.memory}
|
||||
|
||||
v, ok := memory.ReadFloat64Le(ctx, tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
}
|
||||
v, ok := memory.ReadFloat64Le(tc.offset)
|
||||
require.Equal(t, tc.expectedOk, ok)
|
||||
require.Equal(t, tc.expected, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryInstance_Read(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1}
|
||||
mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1}
|
||||
|
||||
buf, ok := mem.Read(ctx, 4, 4)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, []byte{16, 0, 0, 0}, buf)
|
||||
buf, ok := mem.Read(4, 4)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, []byte{16, 0, 0, 0}, buf)
|
||||
|
||||
// Test write-through
|
||||
buf[3] = 4
|
||||
require.Equal(t, []byte{16, 0, 0, 4}, buf)
|
||||
require.Equal(t, []byte{0, 0, 0, 0, 16, 0, 0, 4}, mem.Buffer)
|
||||
// Test write-through
|
||||
buf[3] = 4
|
||||
require.Equal(t, []byte{16, 0, 0, 4}, buf)
|
||||
require.Equal(t, []byte{0, 0, 0, 0, 16, 0, 0, 4}, mem.Buffer)
|
||||
|
||||
_, ok = mem.Read(ctx, 5, 4)
|
||||
require.False(t, ok)
|
||||
_, ok = mem.Read(5, 4)
|
||||
require.False(t, ok)
|
||||
|
||||
_, ok = mem.Read(ctx, 9, 4)
|
||||
require.False(t, ok)
|
||||
}
|
||||
_, ok = mem.Read(9, 4)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestMemoryInstance_WriteUint16Le(t *testing.T) {
|
||||
@@ -508,15 +489,15 @@ func TestMemoryInstance_WriteUint16Le(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "maximum boundary valid offset",
|
||||
offset: memory.Size(testCtx) - 2, // 2 is the size of uint16
|
||||
v: 1, // arbitrary valid v
|
||||
offset: memory.Size() - 2, // 2 is the size of uint16
|
||||
v: 1, // arbitrary valid v
|
||||
expectedOk: true,
|
||||
expectedBytes: []byte{0x1, 0x00},
|
||||
},
|
||||
{
|
||||
name: "offset exceeds the maximum valid offset by 1",
|
||||
offset: memory.Size(testCtx) - 2 + 1, // 2 is the size of uint16
|
||||
v: 1, // arbitrary valid v
|
||||
offset: memory.Size() - 2 + 1, // 2 is the size of uint16
|
||||
v: 1, // arbitrary valid v
|
||||
expectedBytes: []byte{0xff, 0xff},
|
||||
},
|
||||
}
|
||||
@@ -525,11 +506,9 @@ func TestMemoryInstance_WriteUint16Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
require.Equal(t, tc.expectedOk, memory.WriteUint16Le(ctx, tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+2]) // 2 is the size of uint16
|
||||
}
|
||||
require.Equal(t, tc.expectedOk, memory.WriteUint16Le(tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+2]) // 2 is the size of uint16
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -561,15 +540,15 @@ func TestMemoryInstance_WriteUint32Le(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "maximum boundary valid offset",
|
||||
offset: memory.Size(testCtx) - 4, // 4 is the size of uint32
|
||||
v: 1, // arbitrary valid v
|
||||
offset: memory.Size() - 4, // 4 is the size of uint32
|
||||
v: 1, // arbitrary valid v
|
||||
expectedOk: true,
|
||||
expectedBytes: []byte{0x1, 0x00, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
name: "offset exceeds the maximum valid offset by 1",
|
||||
offset: memory.Size(testCtx) - 4 + 1, // 4 is the size of uint32
|
||||
v: 1, // arbitrary valid v
|
||||
offset: memory.Size() - 4 + 1, // 4 is the size of uint32
|
||||
v: 1, // arbitrary valid v
|
||||
expectedBytes: []byte{0xff, 0xff, 0xff, 0xff},
|
||||
},
|
||||
}
|
||||
@@ -578,11 +557,9 @@ func TestMemoryInstance_WriteUint32Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
require.Equal(t, tc.expectedOk, memory.WriteUint32Le(ctx, tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+4]) // 4 is the size of uint32
|
||||
}
|
||||
require.Equal(t, tc.expectedOk, memory.WriteUint32Le(tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+4]) // 4 is the size of uint32
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -613,15 +590,15 @@ func TestMemoryInstance_WriteUint64Le(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "maximum boundary valid offset",
|
||||
offset: memory.Size(testCtx) - 8, // 8 is the size of uint64
|
||||
v: 1, // arbitrary valid v
|
||||
offset: memory.Size() - 8, // 8 is the size of uint64
|
||||
v: 1, // arbitrary valid v
|
||||
expectedOk: true,
|
||||
expectedBytes: []byte{0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
name: "offset exceeds the maximum valid offset by 1",
|
||||
offset: memory.Size(testCtx) - 8 + 1, // 8 is the size of uint64
|
||||
v: 1, // arbitrary valid v
|
||||
offset: memory.Size() - 8 + 1, // 8 is the size of uint64
|
||||
v: 1, // arbitrary valid v
|
||||
expectedOk: false,
|
||||
},
|
||||
}
|
||||
@@ -630,11 +607,9 @@ func TestMemoryInstance_WriteUint64Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
require.Equal(t, tc.expectedOk, memory.WriteUint64Le(ctx, tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+8]) // 8 is the size of uint64
|
||||
}
|
||||
require.Equal(t, tc.expectedOk, memory.WriteUint64Le(tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+8]) // 8 is the size of uint64
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -666,15 +641,15 @@ func TestMemoryInstance_WriteFloat32Le(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "maximum boundary valid offset",
|
||||
offset: memory.Size(testCtx) - 4, // 4 is the size of float32
|
||||
v: 0.1, // arbitrary valid v
|
||||
offset: memory.Size() - 4, // 4 is the size of float32
|
||||
v: 0.1, // arbitrary valid v
|
||||
expectedOk: true,
|
||||
expectedBytes: []byte{0xcd, 0xcc, 0xcc, 0x3d},
|
||||
},
|
||||
{
|
||||
name: "offset exceeds the maximum valid offset by 1",
|
||||
offset: memory.Size(testCtx) - 4 + 1, // 4 is the size of float32
|
||||
v: math.MaxFloat32, // arbitrary valid v
|
||||
offset: memory.Size() - 4 + 1, // 4 is the size of float32
|
||||
v: math.MaxFloat32, // arbitrary valid v
|
||||
expectedBytes: []byte{0xff, 0xff, 0xff, 0xff},
|
||||
},
|
||||
}
|
||||
@@ -683,11 +658,9 @@ func TestMemoryInstance_WriteFloat32Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
require.Equal(t, tc.expectedOk, memory.WriteFloat32Le(ctx, tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+4]) // 4 is the size of float32
|
||||
}
|
||||
require.Equal(t, tc.expectedOk, memory.WriteFloat32Le(tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+4]) // 4 is the size of float32
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -718,15 +691,15 @@ func TestMemoryInstance_WriteFloat64Le(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "maximum boundary valid offset",
|
||||
offset: memory.Size(testCtx) - 8, // 8 is the size of float64
|
||||
v: math.MaxFloat64, // arbitrary valid v
|
||||
offset: memory.Size() - 8, // 8 is the size of float64
|
||||
v: math.MaxFloat64, // arbitrary valid v
|
||||
expectedOk: true,
|
||||
expectedBytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f},
|
||||
},
|
||||
{
|
||||
name: "offset exceeds the maximum valid offset by 1",
|
||||
offset: memory.Size(testCtx) - 8 + 1, // 8 is the size of float64
|
||||
v: math.MaxFloat64, // arbitrary valid v
|
||||
offset: memory.Size() - 8 + 1, // 8 is the size of float64
|
||||
v: math.MaxFloat64, // arbitrary valid v
|
||||
expectedOk: false,
|
||||
},
|
||||
}
|
||||
@@ -735,51 +708,45 @@ func TestMemoryInstance_WriteFloat64Le(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
require.Equal(t, tc.expectedOk, memory.WriteFloat64Le(ctx, tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+8]) // 8 is the size of float64
|
||||
}
|
||||
require.Equal(t, tc.expectedOk, memory.WriteFloat64Le(tc.offset, tc.v))
|
||||
if tc.expectedOk {
|
||||
require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+8]) // 8 is the size of float64
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryInstance_Write(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1}
|
||||
mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1}
|
||||
|
||||
buf := []byte{16, 0, 0, 4}
|
||||
require.True(t, mem.Write(ctx, 4, buf))
|
||||
require.Equal(t, []byte{0, 0, 0, 0, 16, 0, 0, 4}, mem.Buffer)
|
||||
buf := []byte{16, 0, 0, 4}
|
||||
require.True(t, mem.Write(4, buf))
|
||||
require.Equal(t, []byte{0, 0, 0, 0, 16, 0, 0, 4}, mem.Buffer)
|
||||
|
||||
// Test it isn't write-through
|
||||
buf[3] = 0
|
||||
require.Equal(t, []byte{16, 0, 0, 0}, buf)
|
||||
require.Equal(t, []byte{0, 0, 0, 0, 16, 0, 0, 4}, mem.Buffer)
|
||||
// Test it isn't write-through
|
||||
buf[3] = 0
|
||||
require.Equal(t, []byte{16, 0, 0, 0}, buf)
|
||||
require.Equal(t, []byte{0, 0, 0, 0, 16, 0, 0, 4}, mem.Buffer)
|
||||
|
||||
ok := mem.Write(ctx, 5, buf)
|
||||
require.False(t, ok)
|
||||
ok := mem.Write(5, buf)
|
||||
require.False(t, ok)
|
||||
|
||||
ok = mem.Write(ctx, 9, buf)
|
||||
require.False(t, ok)
|
||||
}
|
||||
ok = mem.Write(9, buf)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestMemoryInstance_WriteString(t *testing.T) {
|
||||
for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
|
||||
mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1}
|
||||
mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1}
|
||||
|
||||
s := "bear"
|
||||
require.True(t, mem.WriteString(ctx, 4, s))
|
||||
require.Equal(t, []byte{0, 0, 0, 0, 'b', 'e', 'a', 'r'}, mem.Buffer)
|
||||
s := "bear"
|
||||
require.True(t, mem.WriteString(4, s))
|
||||
require.Equal(t, []byte{0, 0, 0, 0, 'b', 'e', 'a', 'r'}, mem.Buffer)
|
||||
|
||||
ok := mem.WriteString(ctx, 5, s)
|
||||
require.False(t, ok)
|
||||
ok := mem.WriteString(5, s)
|
||||
require.False(t, ok)
|
||||
|
||||
ok = mem.WriteString(ctx, 9, s)
|
||||
require.False(t, ok)
|
||||
}
|
||||
ok = mem.WriteString(9, s)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func BenchmarkWriteString(b *testing.B) {
|
||||
@@ -796,14 +763,14 @@ func BenchmarkWriteString(b *testing.B) {
|
||||
b.Run("", func(b *testing.B) {
|
||||
b.Run("Write", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if !mem.Write(testCtx, 0, []byte(tt)) {
|
||||
if !mem.Write(0, []byte(tt)) {
|
||||
b.Fail()
|
||||
}
|
||||
}
|
||||
})
|
||||
b.Run("WriteString", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if !mem.WriteString(testCtx, 0, tt) {
|
||||
if !mem.WriteString(0, tt) {
|
||||
b.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func TestModuleInstance_Memory(t *testing.T) {
|
||||
|
||||
mem := instance.ExportedMemory("memory")
|
||||
if tc.expected {
|
||||
require.Equal(t, tc.expectedLen, mem.Size(testCtx))
|
||||
require.Equal(t, tc.expectedLen, mem.Size())
|
||||
} else {
|
||||
require.Nil(t, mem)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
@@ -340,7 +339,7 @@ func (m *Module) verifyImportGlobalI32(sectionID SectionID, sectionIdx Index, id
|
||||
// Returns -1 if the operation is not valid, otherwise the old length of the table.
|
||||
//
|
||||
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/instructions.html#xref-syntax-instructions-syntax-instr-table-mathsf-table-grow-x
|
||||
func (t *TableInstance) Grow(_ context.Context, delta uint32, initialRef Reference) (currentLen uint32) {
|
||||
func (t *TableInstance) Grow(delta uint32, initialRef Reference) (currentLen uint32) {
|
||||
// We take write-lock here as the following might result in a new slice
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
@@ -1080,7 +1080,7 @@ func TestTableInstance_Grow(t *testing.T) {
|
||||
tc := tt
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
table := &TableInstance{References: make([]uintptr, tc.currentLen), Max: tc.max}
|
||||
actual := table.Grow(testCtx, tc.delta, 0)
|
||||
actual := table.Grow(tc.delta, 0)
|
||||
require.Equal(t, tc.exp, actual)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user