diff --git a/examples/allocation/tinygo/greet.go b/examples/allocation/tinygo/greet.go index 09e1831b017..14d4d882d54 100644 --- a/examples/allocation/tinygo/greet.go +++ b/examples/allocation/tinygo/greet.go @@ -90,9 +90,15 @@ func main() { if err != nil { log.Panicln(err) } - // Note: This pointer is still owned by TinyGo, so don't try to free it! + greetingPtr := uint32(ptrSize[0] >> 32) greetingSize := uint32(ptrSize[0]) + + // We don't need the memory after deserialization: make sure it is freed. + if greetingPtr != 0 { + defer free.Call(ctx, uint64(greetingPtr)) + } + // The pointer is a linear memory offset, which is where we write the name. if bytes, ok := mod.Memory().Read(greetingPtr, greetingSize); !ok { log.Panicf("Memory.Read(%d, %d) out of range of memory size %d", diff --git a/examples/allocation/tinygo/testdata/greet.go b/examples/allocation/tinygo/testdata/greet.go index d858ab38fe2..cfc460a027e 100644 --- a/examples/allocation/tinygo/testdata/greet.go +++ b/examples/allocation/tinygo/testdata/greet.go @@ -6,6 +6,9 @@ import ( "unsafe" ) +// #include +import "C" + // main is required for TinyGo to compile to Wasm. func main() {} @@ -70,10 +73,17 @@ func ptrToString(ptr uint32, size uint32) string { } // stringToPtr returns a pointer and size pair for the given string in a way -// compatible with WebAssembly numeric types. +// compatible with WebAssembly numeric types. The pointer is not automatically +// managed by tinygo but must be freed by the host. func stringToPtr(s string) (uint32, uint32) { - buf := []byte(s) - ptr := &buf[0] - unsafePtr := uintptr(unsafe.Pointer(ptr)) - return uint32(unsafePtr), uint32(len(buf)) + if len(s) == 0 { + return 0, 0 + } + + size := C.ulong(len(s)) + ptr := unsafe.Pointer(C.malloc(size)) + + copy(unsafe.Slice((*byte)(ptr), size), []byte(s)) + + return uint32(uintptr(ptr)), uint32(len(s)) } diff --git a/examples/allocation/tinygo/testdata/greet.wasm b/examples/allocation/tinygo/testdata/greet.wasm index 12efdc1fa2d..bd91540c7de 100755 Binary files a/examples/allocation/tinygo/testdata/greet.wasm and b/examples/allocation/tinygo/testdata/greet.wasm differ diff --git a/internal/integration_test/vs/bench_allocation.go b/internal/integration_test/vs/bench_allocation.go index 1c8d893c788..921343f0a43 100644 --- a/internal/integration_test/vs/bench_allocation.go +++ b/internal/integration_test/vs/bench_allocation.go @@ -51,13 +51,24 @@ func allocationCall(m Module, _ int) error { } // Now, we can call "greet", which reads the string we wrote to memory! - if err = m.CallI32I32_V(testCtx, "greet", namePtr, nameSize); err != nil { - return err + ptrSize, fnErr := m.CallI32I32_I64(testCtx, "greet", namePtr, nameSize) + if fnErr != nil { + return fnErr } // This pointer was allocated by Rust, but owned by Go, So, we have to // deallocate it when finished - return m.CallI32_V(testCtx, "free", namePtr) + if err := m.CallI32_V(testCtx, "free", namePtr); err != nil { + return err + } + + // This pointer was allocated with malloc. So, we have to deallocate it + // when finished + if err := m.CallI32_V(testCtx, "free", uint32(ptrSize>>32)); err != nil { + return err + } + + return nil } func RunTestAllocation(t *testing.T, runtime func() Runtime) { diff --git a/internal/integration_test/vs/runtime.go b/internal/integration_test/vs/runtime.go index ef4c12fbea4..d3d6ea57cab 100644 --- a/internal/integration_test/vs/runtime.go +++ b/internal/integration_test/vs/runtime.go @@ -37,6 +37,7 @@ type Runtime interface { type Module interface { CallI32_I32(ctx context.Context, funcName string, param uint32) (uint32, error) CallI32I32_V(ctx context.Context, funcName string, x, y uint32) error + CallI32I32_I64(ctx context.Context, funcName string, x, y uint32) (uint64, error) CallI32_V(ctx context.Context, funcName string, param uint32) error CallV_V(ctx context.Context, funcName string) error CallI64_I64(ctx context.Context, funcName string, param uint64) (uint64, error) @@ -183,6 +184,18 @@ func (m *wazeroModule) CallI32I32_V(ctx context.Context, funcName string, x, y u return } +func (m *wazeroModule) CallI32I32_I64(ctx context.Context, funcName string, x, y uint32) (uint64, error) { + results, err := m.funcs[funcName].Call(ctx, uint64(x), uint64(y)) + if err != nil { + return 0, err + } + if len(results) > 0 { + return results[0], nil + } + + return 0, nil +} + func (m *wazeroModule) CallI32_V(ctx context.Context, funcName string, param uint32) (err error) { _, err = m.funcs[funcName].Call(ctx, uint64(param)) return