Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Changes the type of Size() to uint64 #6

Merged
merged 5 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ type Memory interface {
// has 1 page: 65536
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#-hrefsyntax-instr-memorymathsfmemorysize%E2%91%A0
Size() uint32
Size() uint64

// Grow increases memory by the delta in pages (65536 bytes per page).
// The return val is the previous memory size in pages, or false if the
Expand Down Expand Up @@ -635,7 +635,7 @@ type Memory interface {
// shared. Those who need a stable view must set Wasm memory min=max, or
// use wazero.RuntimeConfig WithMemoryCapacityPages to ensure max is always
// allocated.
Read(offset, byteCount uint32) ([]byte, bool)
Read(offset uint32, byteCount uint64) ([]byte, bool)

// WriteByte writes a single byte to the underlying buffer at the offset in or returns false if out of range.
WriteByte(offset uint32, v byte) bool
Expand Down
61 changes: 60 additions & 1 deletion builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package wazero

import (
"context"
"fmt"

"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/wasm"
"github.com/tetratelabs/wazero/internal/wasm/binary"
)

// HostFunctionBuilder defines a host function (in Go), so that a
Expand Down Expand Up @@ -182,6 +184,35 @@ type HostFunctionBuilder interface {
type HostModuleBuilder interface {
// Note: until golang/go#5860, we can't use example tests to embed code in interface godocs.

// ExportMemory adds linear memory, which a WebAssembly module can import and become available via api.Memory.
// If a memory is already exported with the same name, this overwrites it.
//
// # Parameters
//
// - name - the name to export. Ex "memory" for wasi_snapshot_preview1.ModuleSnapshotPreview1
// - minPages - the possibly zero initial size in pages (65536 bytes per page).
//
// For example, the WebAssembly 1.0 Text Format below is the equivalent of this builder method:
// // (memory (export "memory") 1)
// builder.ExportMemory(1)
//
// # Notes
//
// - This is allowed to grow to (4GiB) limited by api.MemorySizer. To bound it, use ExportMemoryWithMax.
// - Version 1.0 (20191205) of the WebAssembly spec allows at most one memory per module.
//
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-section%E2%91%A0
ExportMemory(name string, minPages uint32) HostModuleBuilder

// ExportMemoryWithMax is like ExportMemory, but can prevent overuse of memory.
//
// For example, the WebAssembly 1.0 Text Format below is the equivalent of this builder method:
// // (memory (export "memory") 1 1)
// builder.ExportMemoryWithMax(1, 1)
//
// Note: api.MemorySizer determines the capacity.
ExportMemoryWithMax(name string, minPages, maxPages uint32) HostModuleBuilder

// NewFunctionBuilder begins the definition of a host function.
NewFunctionBuilder() HostFunctionBuilder

Expand Down Expand Up @@ -218,6 +249,7 @@ type hostModuleBuilder struct {
moduleName string
exportNames []string
nameToHostFunc map[string]*wasm.HostFunc
nameToMemory map[string]*wasm.Memory
}

// NewHostModuleBuilder implements Runtime.NewHostModuleBuilder
Expand All @@ -226,6 +258,7 @@ func (r *runtime) NewHostModuleBuilder(moduleName string) HostModuleBuilder {
r: r,
moduleName: moduleName,
nameToHostFunc: map[string]*wasm.HostFunc{},
nameToMemory: map[string]*wasm.Memory{},
}
}

Expand Down Expand Up @@ -299,6 +332,18 @@ func (h *hostFunctionBuilder) Export(exportName string) HostModuleBuilder {
return h.b
}

// ExportMemory implements ModuleBuilder.ExportMemory
func (b *hostModuleBuilder) ExportMemory(name string, minPages uint32) HostModuleBuilder {
b.nameToMemory[name] = &wasm.Memory{Min: minPages}
return b
}

// ExportMemoryWithMax implements ModuleBuilder.ExportMemoryWithMax
func (b *hostModuleBuilder) ExportMemoryWithMax(name string, minPages, maxPages uint32) HostModuleBuilder {
b.nameToMemory[name] = &wasm.Memory{Min: minPages, Max: maxPages, IsMaxEncoded: true}
return b
}

// ExportHostFunc implements wasm.HostFuncExporter
func (b *hostModuleBuilder) ExportHostFunc(fn *wasm.HostFunc) {
if _, ok := b.nameToHostFunc[fn.ExportName]; !ok { // add a new name
Expand All @@ -314,7 +359,19 @@ func (b *hostModuleBuilder) NewFunctionBuilder() HostFunctionBuilder {

// Compile implements HostModuleBuilder.Compile
func (b *hostModuleBuilder) Compile(ctx context.Context) (CompiledModule, error) {
module, err := wasm.NewHostModule(b.moduleName, b.exportNames, b.nameToHostFunc, b.r.enabledFeatures)
// Verify the maximum limit here, so we don't have to pass it to wasm.NewHostModule
for name, mem := range b.nameToMemory {
var maxP *uint32
if mem.IsMaxEncoded {
maxP = &mem.Max
}
mem.Min, mem.Cap, mem.Max = binary.NewMemorySizer(b.r.memoryLimitPages, b.r.memoryCapacityFromMax)(mem.Min, maxP)
if err := mem.Validate(b.r.memoryLimitPages); err != nil {
return nil, fmt.Errorf("memory[%s] %v", name, err)
}
}

module, err := wasm.NewHostModule(b.moduleName, b.exportNames, b.nameToHostFunc, b.nameToMemory, b.r.enabledFeatures)
if err != nil {
return nil, err
} else if err = module.Validate(b.r.enabledFeatures); err != nil {
Expand All @@ -327,6 +384,8 @@ func (b *hostModuleBuilder) Compile(ctx context.Context) (CompiledModule, error)
return nil, err
}

module.BuildMemoryDefinitions()

if err = b.r.store.Engine.CompileModule(ctx, module, listeners, false); err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions examples/allocation/rust/greet.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func main() {
defer deallocate.Call(ctx, uint64(greetingPtr), uint64(greetingSize))

// The pointer is a linear memory offset, which is where we write the name.
if bytes, ok := mod.Memory().Read(greetingPtr, greetingSize); !ok {
if bytes, ok := mod.Memory().Read(greetingPtr, uint64(greetingSize)); !ok {
log.Panicf("Memory.Read(%d, %d) out of range of memory size %d",
greetingPtr, greetingSize, mod.Memory().Size())
} else {
Expand All @@ -100,7 +100,7 @@ func main() {
}

func logString(ctx context.Context, m api.Module, offset, byteCount uint32) {
buf, ok := m.Memory().Read(offset, byteCount)
buf, ok := m.Memory().Read(offset, uint64(byteCount))
if !ok {
log.Panicf("Memory.Read(%d, %d) out of range", offset, byteCount)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/allocation/tinygo/greet.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func main() {
}

// The pointer is a linear memory offset, which is where we write the name.
if bytes, ok := mod.Memory().Read(greetingPtr, greetingSize); !ok {
if bytes, ok := mod.Memory().Read(greetingPtr, uint64(greetingSize)); !ok {
log.Panicf("Memory.Read(%d, %d) out of range of memory size %d",
greetingPtr, greetingSize, mod.Memory().Size())
} else {
Expand All @@ -115,7 +115,7 @@ func main() {
}

func logString(_ context.Context, m api.Module, offset, byteCount uint32) {
buf, ok := m.Memory().Read(offset, byteCount)
buf, ok := m.Memory().Read(offset, uint64(byteCount))
if !ok {
log.Panicf("Memory.Read(%d, %d) out of range", offset, byteCount)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/allocation/zig/greet.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func run() error {
greetingPtr := uint32(ptrSize[0] >> 32)
greetingSize := uint32(ptrSize[0])
// The pointer is a linear memory offset, which is where we write the name.
if bytes, ok := mod.Memory().Read(greetingPtr, greetingSize); !ok {
if bytes, ok := mod.Memory().Read(greetingPtr, uint64(greetingSize)); !ok {
return fmt.Errorf("Memory.Read(%d, %d) out of range of memory size %d",
greetingPtr, greetingSize, mod.Memory().Size())
} else {
Expand All @@ -109,7 +109,7 @@ func run() error {
}

func logString(_ context.Context, m api.Module, offset, byteCount uint32) {
buf, ok := m.Memory().Read(offset, byteCount)
buf, ok := m.Memory().Read(offset, uint64(byteCount))
if !ok {
log.Panicf("Memory.Read(%d, %d) out of range", offset, byteCount)
}
Expand Down
15 changes: 8 additions & 7 deletions experimental/wazerotest/wazerotest.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,8 +507,8 @@ func (m *Memory) Definition() api.MemoryDefinition {
return memoryDefinition{memory: m}
}

func (m *Memory) Size() uint32 {
return uint32(len(m.Bytes))
func (m *Memory) Size() uint64 {
return uint64(len(m.Bytes))
}

func (m *Memory) Grow(deltaPages uint32) (previousPages uint32, ok bool) {
Expand Down Expand Up @@ -561,11 +561,11 @@ func (m *Memory) ReadFloat64Le(offset uint32) (float64, bool) {
return math.Float64frombits(v), ok
}

func (m *Memory) Read(offset, length uint32) ([]byte, bool) {
func (m *Memory) Read(offset uint32, length uint64) ([]byte, bool) {
if m.isOutOfRange(offset, length) {
return nil, false
}
return m.Bytes[offset : offset+length : offset+length], true
return m.Bytes[offset : uint64(offset)+length : uint64(offset)+length], true
}

func (m *Memory) WriteByte(offset uint32, value byte) bool {
Expand Down Expand Up @@ -609,23 +609,24 @@ func (m *Memory) WriteFloat64Le(offset uint32, value float64) bool {
}

func (m *Memory) Write(offset uint32, value []byte) bool {
if m.isOutOfRange(offset, uint32(len(value))) {
if m.isOutOfRange(offset, uint64(len(value))) {
return false
}
copy(m.Bytes[offset:], value)
return true
}

func (m *Memory) WriteString(offset uint32, value string) bool {
if m.isOutOfRange(offset, uint32(len(value))) {
if m.isOutOfRange(offset, uint64(len(value))) {
return false
}
copy(m.Bytes[offset:], value)
return true
}

func (m *Memory) isOutOfRange(offset, length uint32) bool {
func (m *Memory) isOutOfRange(_offset uint32, length uint64) bool {
size := m.Size()
offset := uint64(_offset)
return offset >= size || length > size || offset > (size-length)
}

Expand Down
2 changes: 1 addition & 1 deletion imports/assemblyscript/assemblyscript.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ func readAssemblyScriptString(mem api.Memory, offset uint32) (string, bool) {
if !ok || byteCount%2 != 0 {
return "", false
}
buf, ok := mem.Read(offset, byteCount)
buf, ok := mem.Read(offset, uint64(byteCount))
if !ok {
return "", false
}
Expand Down
8 changes: 4 additions & 4 deletions imports/wasi_snapshot_preview1/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func Test_argsGet(t *testing.T) {
<== errno=ESUCCESS
`, "\n"+log.String())

actual, ok := mod.Memory().Read(argvBuf-1, uint32(len(expectedMemory)))
actual, ok := mod.Memory().Read(argvBuf-1, uint64(len(expectedMemory)))
require.True(t, ok)
require.Equal(t, expectedMemory, actual)
}
Expand All @@ -41,7 +41,7 @@ func Test_argsGet_Errors(t *testing.T) {
mod, r, log := requireProxyModule(t, wazero.NewModuleConfig().WithArgs("a", "bc"))
defer r.Close(testCtx)

memorySize := mod.Memory().Size()
memorySize := uint32(mod.Memory().Size())
validAddress := uint32(0) // arbitrary

tests := []struct {
Expand Down Expand Up @@ -124,7 +124,7 @@ func Test_argsSizesGet(t *testing.T) {
<== errno=ESUCCESS
`, "\n"+log.String())

actual, ok := mod.Memory().Read(resultArgc-1, uint32(len(expectedMemory)))
actual, ok := mod.Memory().Read(resultArgc-1, uint64(len(expectedMemory)))
require.True(t, ok)
require.Equal(t, expectedMemory, actual)
}
Expand All @@ -133,7 +133,7 @@ func Test_argsSizesGet_Errors(t *testing.T) {
mod, r, log := requireProxyModule(t, wazero.NewModuleConfig().WithArgs("a", "bc"))
defer r.Close(testCtx)

memorySize := mod.Memory().Size()
memorySize := uint32(mod.Memory().Size())
validAddress := uint32(0) // arbitrary valid address as arguments to args_sizes_get. We chose 0 here.

tests := []struct {
Expand Down
6 changes: 3 additions & 3 deletions imports/wasi_snapshot_preview1/clock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func Test_clockResGet(t *testing.T) {
requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.ClockResGetName, uint64(tc.clockID), uint64(resultResolution))
require.Equal(t, tc.expectedLog, "\n"+log.String())

actual, ok := mod.Memory().Read(uint32(resultResolution-1), uint32(len(tc.expectedMemory)))
actual, ok := mod.Memory().Read(uint32(resultResolution-1), uint64(len(tc.expectedMemory)))
require.True(t, ok)
require.Equal(t, tc.expectedMemory, actual)
})
Expand Down Expand Up @@ -170,7 +170,7 @@ func Test_clockTimeGet(t *testing.T) {
requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.ClockTimeGetName, uint64(tc.clockID), 0 /* TODO: precision */, uint64(resultTimestamp))
require.Equal(t, tc.expectedLog, "\n"+log.String())

actual, ok := mod.Memory().Read(uint32(resultTimestamp-1), uint32(len(tc.expectedMemory)))
actual, ok := mod.Memory().Read(uint32(resultTimestamp-1), uint64(len(tc.expectedMemory)))
require.True(t, ok)
require.Equal(t, tc.expectedMemory, actual)
})
Expand Down Expand Up @@ -259,7 +259,7 @@ func Test_clockTimeGet_Errors(t *testing.T) {
mod, r, log := requireProxyModule(t, wazero.NewModuleConfig())
defer r.Close(testCtx)

memorySize := mod.Memory().Size()
memorySize := uint32(mod.Memory().Size())

tests := []struct {
name string
Expand Down
8 changes: 4 additions & 4 deletions imports/wasi_snapshot_preview1/environ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func Test_environGet(t *testing.T) {
<== errno=ESUCCESS
`, "\n"+log.String())

actual, ok := mod.Memory().Read(resultEnvironBuf-1, uint32(len(expectedMemory)))
actual, ok := mod.Memory().Read(resultEnvironBuf-1, uint64(len(expectedMemory)))
require.True(t, ok)
require.Equal(t, expectedMemory, actual)
}
Expand All @@ -44,7 +44,7 @@ func Test_environGet_Errors(t *testing.T) {
WithEnv("a", "bc").WithEnv("b", "cd"))
defer r.Close(testCtx)

memorySize := mod.Memory().Size()
memorySize := uint32(mod.Memory().Size())
validAddress := uint32(0) // arbitrary valid address as arguments to environ_get. We chose 0 here.

tests := []struct {
Expand Down Expand Up @@ -128,7 +128,7 @@ func Test_environSizesGet(t *testing.T) {
<== errno=ESUCCESS
`, "\n"+log.String())

actual, ok := mod.Memory().Read(resultEnvironc-1, uint32(len(expectedMemory)))
actual, ok := mod.Memory().Read(resultEnvironc-1, uint64(len(expectedMemory)))
require.True(t, ok)
require.Equal(t, expectedMemory, actual)
}
Expand All @@ -138,7 +138,7 @@ func Test_environSizesGet_Errors(t *testing.T) {
WithEnv("a", "b").WithEnv("b", "cd"))
defer r.Close(testCtx)

memorySize := mod.Memory().Size()
memorySize := uint32(mod.Memory().Size())
validAddress := uint32(0) // arbitrary

tests := []struct {
Expand Down
Loading