examples(allocation): ensure message outlives host call (#1436)

Signed-off-by: Nuno Cruces <ncruces@users.noreply.github.com>
Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
Co-authored-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
This commit is contained in:
Nuno Cruces
2023-05-05 10:21:13 +01:00
committed by GitHub
parent b5198a46ff
commit 38a7a0f730
2 changed files with 12 additions and 19 deletions

View File

@@ -5,7 +5,7 @@ import "C"
import ( import (
"fmt" "fmt"
"reflect" "runtime"
"unsafe" "unsafe"
) )
@@ -21,6 +21,7 @@ func greet(name string) {
func log(message string) { func log(message string) {
ptr, size := stringToPtr(message) ptr, size := stringToPtr(message)
_log(ptr, size) _log(ptr, size)
runtime.KeepAlive(message) // keep message alive until ptr is no longer needed.
} }
// _log is a WebAssembly import which prints a string (linear memory offset, // _log is a WebAssembly import which prints a string (linear memory offset,
@@ -30,7 +31,7 @@ func log(message string) {
// //
//go:wasm-module env //go:wasm-module env
//export log //export log
func _log(ptr uint32, size uint32) func _log(ptr, size uint32)
// greeting gets a greeting for the name. // greeting gets a greeting for the name.
func greeting(name string) string { func greeting(name string) string {
@@ -63,32 +64,24 @@ func _greeting(ptr, size uint32) (ptrSize uint64) {
// ptrToString returns a string from WebAssembly compatible numeric types // ptrToString returns a string from WebAssembly compatible numeric types
// representing its pointer and length. // representing its pointer and length.
func ptrToString(ptr uint32, size uint32) string { func ptrToString(ptr uint32, size uint32) string {
// Get a slice view of the underlying bytes in the stream. We use SliceHeader, not StringHeader return unsafe.String((*byte)(unsafe.Pointer(uintptr(ptr))), size)
// as it allows us to fix the capacity to what was allocated.
return *(*string)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(ptr),
Len: uintptr(size), // Tinygo requires these as uintptrs even if they are int fields.
Cap: uintptr(size), // ^^ See https://github.com/tinygo-org/tinygo/issues/1284
}))
} }
// stringToPtr returns a pointer and size pair for the given string in a way // 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 returned pointer aliases the string hence the string must be kept alive
// until ptr is no longer needed.
func stringToPtr(s string) (uint32, uint32) { func stringToPtr(s string) (uint32, uint32) {
buf := []byte(s) ptr := unsafe.Pointer(unsafe.StringData(s))
ptr := &buf[0] return uint32(uintptr(ptr)), uint32(len(s))
unsafePtr := uintptr(unsafe.Pointer(ptr))
return uint32(unsafePtr), uint32(len(buf))
} }
// stringToLeakedPtr returns a pointer and size pair for the given string in a way // stringToLeakedPtr returns a pointer and size pair for the given string in a way
// compatible with WebAssembly numeric types. The pointer is not automatically // compatible with WebAssembly numeric types.
// managed by TinyGo hence it must be freed by the host. // The pointer is not automatically managed by TinyGo hence it must be freed by the host.
func stringToLeakedPtr(s string) (uint32, uint32) { func stringToLeakedPtr(s string) (uint32, uint32) {
size := C.ulong(len(s)) size := C.ulong(len(s))
ptr := unsafe.Pointer(C.malloc(size)) ptr := unsafe.Pointer(C.malloc(size))
copy(unsafe.Slice((*byte)(ptr), size), s)
copy(unsafe.Slice((*byte)(ptr), size), []byte(s)) return uint32(uintptr(ptr)), uint32(size)
return uint32(uintptr(ptr)), uint32(len(s))
} }

Binary file not shown.