155 строки
3,3 КиБ
Go
155 строки
3,3 КиБ
Go
//go:build tinygo.wasm
|
|
|
|
package runtime_wasi
|
|
|
|
import (
|
|
"reflect"
|
|
"runtime"
|
|
"strconv"
|
|
"testing"
|
|
"unsafe"
|
|
)
|
|
|
|
//export malloc
|
|
func libc_malloc(size uintptr) unsafe.Pointer
|
|
|
|
//export free
|
|
func libc_free(ptr unsafe.Pointer)
|
|
|
|
//export calloc
|
|
func libc_calloc(nmemb, size uintptr) unsafe.Pointer
|
|
|
|
//export realloc
|
|
func libc_realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer
|
|
|
|
func getFilledBuffer_malloc() uintptr {
|
|
ptr := libc_malloc(5)
|
|
fillPanda(ptr)
|
|
return uintptr(ptr)
|
|
}
|
|
|
|
func getFilledBuffer_calloc() uintptr {
|
|
ptr := libc_calloc(2, 5)
|
|
fillPanda(ptr)
|
|
*(*byte)(unsafe.Add(ptr, 5)) = 'b'
|
|
*(*byte)(unsafe.Add(ptr, 6)) = 'e'
|
|
*(*byte)(unsafe.Add(ptr, 7)) = 'a'
|
|
*(*byte)(unsafe.Add(ptr, 8)) = 'r'
|
|
*(*byte)(unsafe.Add(ptr, 9)) = 's'
|
|
return uintptr(ptr)
|
|
}
|
|
|
|
func getFilledBuffer_realloc() uintptr {
|
|
origPtr := getFilledBuffer_malloc()
|
|
ptr := libc_realloc(unsafe.Pointer(origPtr), 9)
|
|
*(*byte)(unsafe.Add(ptr, 5)) = 'b'
|
|
*(*byte)(unsafe.Add(ptr, 6)) = 'e'
|
|
*(*byte)(unsafe.Add(ptr, 7)) = 'a'
|
|
*(*byte)(unsafe.Add(ptr, 8)) = 'r'
|
|
return uintptr(ptr)
|
|
}
|
|
|
|
func getFilledBuffer_reallocNil() uintptr {
|
|
ptr := libc_realloc(nil, 5)
|
|
fillPanda(ptr)
|
|
return uintptr(ptr)
|
|
}
|
|
|
|
func fillPanda(ptr unsafe.Pointer) {
|
|
*(*byte)(unsafe.Add(ptr, 0)) = 'p'
|
|
*(*byte)(unsafe.Add(ptr, 1)) = 'a'
|
|
*(*byte)(unsafe.Add(ptr, 2)) = 'n'
|
|
*(*byte)(unsafe.Add(ptr, 3)) = 'd'
|
|
*(*byte)(unsafe.Add(ptr, 4)) = 'a'
|
|
}
|
|
|
|
func checkFilledBuffer(t *testing.T, ptr uintptr, content string) {
|
|
t.Helper()
|
|
buf := *(*string)(unsafe.Pointer(&reflect.StringHeader{
|
|
Data: ptr,
|
|
Len: uintptr(len(content)),
|
|
}))
|
|
if buf != content {
|
|
t.Errorf("expected %q, got %q", content, buf)
|
|
}
|
|
}
|
|
|
|
func TestMallocFree(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
getBuffer func() uintptr
|
|
content string
|
|
}{
|
|
{
|
|
name: "malloc",
|
|
getBuffer: getFilledBuffer_malloc,
|
|
content: "panda",
|
|
},
|
|
{
|
|
name: "calloc",
|
|
getBuffer: getFilledBuffer_calloc,
|
|
content: "pandabears",
|
|
},
|
|
{
|
|
name: "realloc",
|
|
getBuffer: getFilledBuffer_realloc,
|
|
content: "pandabear",
|
|
},
|
|
{
|
|
name: "realloc nil",
|
|
getBuffer: getFilledBuffer_reallocNil,
|
|
content: "panda",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
tt := tc
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
bufPtr := tt.getBuffer()
|
|
// Don't use defer to free the buffer as it seems to cause the GC to track it.
|
|
|
|
// Churn GC, the pointer should still be valid until free is called.
|
|
for i := 0; i < 1000; i++ {
|
|
a := "hello" + strconv.Itoa(i)
|
|
// Some conditional logic to ensure optimization doesn't remove the loop completely.
|
|
if len(a) < 0 {
|
|
break
|
|
}
|
|
runtime.GC()
|
|
}
|
|
|
|
checkFilledBuffer(t, bufPtr, tt.content)
|
|
|
|
libc_free(unsafe.Pointer(bufPtr))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMallocEmpty(t *testing.T) {
|
|
ptr := libc_malloc(0)
|
|
if ptr != nil {
|
|
t.Errorf("expected nil pointer, got %p", ptr)
|
|
}
|
|
}
|
|
|
|
func TestCallocEmpty(t *testing.T) {
|
|
ptr := libc_calloc(0, 1)
|
|
if ptr != nil {
|
|
t.Errorf("expected nil pointer, got %p", ptr)
|
|
}
|
|
ptr = libc_calloc(1, 0)
|
|
if ptr != nil {
|
|
t.Errorf("expected nil pointer, got %p", ptr)
|
|
}
|
|
}
|
|
|
|
func TestReallocEmpty(t *testing.T) {
|
|
ptr := libc_malloc(1)
|
|
if ptr == nil {
|
|
t.Error("expected pointer but was nil")
|
|
}
|
|
ptr = libc_realloc(ptr, 0)
|
|
if ptr != nil {
|
|
t.Errorf("expected nil pointer, got %p", ptr)
|
|
}
|
|
}
|