We get undefined behavior, at least in code that re-slices, unless capacity is set. This ensures the buffer under the string has a capacity set to the same length as its size. This also shows more explicitly the problems in TinyGo (ex some type mismatch you have to ignore until fixed). Signed-off-by: Adrian Cole <adrian@tetrate.io>
77 lines
2.2 KiB
Go
77 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"unsafe"
|
|
)
|
|
|
|
// main is required for TinyGo to compile to Wasm.
|
|
func main() {}
|
|
|
|
// greet prints a greeting to the console.
|
|
func greet(name string) {
|
|
log(fmt.Sprint("wasm >> ", greeting(name)))
|
|
}
|
|
|
|
// log a message to the console using _log.
|
|
func log(message string) {
|
|
ptr, size := stringToPtr(message)
|
|
_log(ptr, size)
|
|
}
|
|
|
|
// _log is a WebAssembly import which prints a string (linear memory offset,
|
|
// byteCount) to the console.
|
|
//
|
|
// Note: In TinyGo "//export" on a func is actually an import!
|
|
//go:wasm-module env
|
|
//export log
|
|
func _log(ptr uint32, size uint32)
|
|
|
|
// greeting gets a greeting for the name.
|
|
func greeting(name string) string {
|
|
return fmt.Sprint("Hello, ", name, "!")
|
|
}
|
|
|
|
// _greet is a WebAssembly export that accepts a string pointer (linear memory
|
|
// offset) and calls greet.
|
|
//export greet
|
|
func _greet(ptr, size uint32) {
|
|
name := ptrToString(ptr, size)
|
|
greet(name)
|
|
}
|
|
|
|
// _greet is a WebAssembly export that accepts a string pointer (linear memory
|
|
// offset) and returns a pointer/size pair packed into a uint64.
|
|
//
|
|
// Note: This uses a uint64 instead of two result values for compatibility with
|
|
// WebAssembly 1.0.
|
|
//export greeting
|
|
func _greeting(ptr, size uint32) (ptrSize uint64) {
|
|
name := ptrToString(ptr, size)
|
|
g := greeting(name)
|
|
ptr, size = stringToPtr(g)
|
|
return (uint64(ptr) << uint64(32)) | uint64(size)
|
|
}
|
|
|
|
// ptrToString returns a string from WebAssembly compatible numeric types
|
|
// representing its pointer and length.
|
|
func ptrToString(ptr uint32, size uint32) string {
|
|
// Get a slice view of the underlying bytes in the stream. We use SliceHeader, not StringHeader
|
|
// 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
|
|
// compatible with WebAssembly numeric types.
|
|
func stringToPtr(s string) (uint32, uint32) {
|
|
buf := []byte(s)
|
|
ptr := &buf[0]
|
|
unsafePtr := uintptr(unsafe.Pointer(ptr))
|
|
return uint32(unsafePtr), uint32(len(buf))
|
|
}
|