From 27bb9effaf238f3961572e64c4711b42331782fc Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Sun, 15 Jun 2025 18:09:18 +0200 Subject: [PATCH 1/6] Update to Go 1.24 Signed-off-by: Till Schneidereit --- cm/go.mod | 2 +- go.mod | 2 +- go.work | 2 +- testdata/go.mod | 2 +- tests/go.mod | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cm/go.mod b/cm/go.mod index f8e24af7..e704c371 100644 --- a/cm/go.mod +++ b/cm/go.mod @@ -1,3 +1,3 @@ module go.bytecodealliance.org/cm -go 1.23.0 +go 1.24.0 diff --git a/go.mod b/go.mod index 2b1bc920..9e4c690f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module go.bytecodealliance.org -go 1.23.0 +go 1.24.0 require ( github.com/coreos/go-semver v0.3.1 diff --git a/go.work b/go.work index d89b68f7..e00b8604 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.23.0 +go 1.24.0 use ( . diff --git a/testdata/go.mod b/testdata/go.mod index f5c060f3..fb93a0d7 100644 --- a/testdata/go.mod +++ b/testdata/go.mod @@ -1,4 +1,4 @@ // This go.mod file exists to prevent the testdata directory from being included in module distribution. module testdata -go 1.23.0 +go 1.24.0 diff --git a/tests/go.mod b/tests/go.mod index 687ce4f9..2e50cc37 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -1,3 +1,3 @@ module tests -go 1.23.0 +go 1.24.0 From 515bb8066daff75c3b422c4deeba2480ad0328a2 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Sun, 15 Jun 2025 18:10:04 +0200 Subject: [PATCH 2/6] Changed testdata I'm not entirely sure what changed these files, but it's a result of running tests via the Makefile, as best I can tell. Signed-off-by: Till Schneidereit --- testdata/issues/issue325.wit.json | 16 +++- .../gated-include-use-with-stable.wit.json | 96 +++++++++++++++++-- 2 files changed, 105 insertions(+), 7 deletions(-) diff --git a/testdata/issues/issue325.wit.json b/testdata/issues/issue325.wit.json index 20e47b59..f319e452 100644 --- a/testdata/issues/issue325.wit.json +++ b/testdata/issues/issue325.wit.json @@ -63,7 +63,7 @@ } }, "r": { - "type": 1 + "type": 2 } }, "exports": {}, @@ -101,6 +101,20 @@ "feature": "active" } } + }, + { + "name": "r", + "kind": { + "type": 0 + }, + "owner": { + "world": 3 + }, + "stability": { + "unstable": { + "feature": "active" + } + } } ], "packages": [ diff --git a/testdata/wit-parser/gated-include-use-with-stable.wit.json b/testdata/wit-parser/gated-include-use-with-stable.wit.json index 9b10fd9b..83e3d5dc 100644 --- a/testdata/wit-parser/gated-include-use-with-stable.wit.json +++ b/testdata/wit-parser/gated-include-use-with-stable.wit.json @@ -173,13 +173,13 @@ } }, "stable-resource": { - "type": 3 + "type": 6 }, "unversioned-resource": { - "type": 4 + "type": 7 }, "unstable-resource": { - "type": 5 + "type": 8 } }, "exports": {}, @@ -219,13 +219,13 @@ } }, "stable-resource": { - "type": 3 + "type": 9 }, "unversioned-resource": { - "type": 4 + "type": 10 }, "unstable-resource": { - "type": 5 + "type": 11 } }, "exports": {}, @@ -336,6 +336,90 @@ "feature": "active" } } + }, + { + "name": "stable-resource", + "kind": { + "type": 2 + }, + "owner": { + "world": 5 + }, + "stability": { + "unstable": { + "feature": "active" + } + } + }, + { + "name": "unversioned-resource", + "kind": { + "type": 1 + }, + "owner": { + "world": 5 + }, + "stability": { + "unstable": { + "feature": "active" + } + } + }, + { + "name": "unstable-resource", + "kind": { + "type": 0 + }, + "owner": { + "world": 5 + }, + "stability": { + "unstable": { + "feature": "active" + } + } + }, + { + "name": "stable-resource", + "kind": { + "type": 2 + }, + "owner": { + "world": 6 + }, + "stability": { + "unstable": { + "feature": "active" + } + } + }, + { + "name": "unversioned-resource", + "kind": { + "type": 1 + }, + "owner": { + "world": 6 + }, + "stability": { + "unstable": { + "feature": "active" + } + } + }, + { + "name": "unstable-resource", + "kind": { + "type": 0 + }, + "owner": { + "world": 6 + }, + "stability": { + "unstable": { + "feature": "active" + } + } } ], "packages": [ From d4970f2a8d7a4cc008ce41f5c486d47b6b4b4012 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Sun, 15 Jun 2025 18:10:48 +0200 Subject: [PATCH 3/6] Export `realloc` as `cabi_realloc` Otherwise no `cabi_realloc` exists in Go. Signed-off-by: Till Schneidereit --- x/cabi/realloc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/cabi/realloc.go b/x/cabi/realloc.go index 6baf1333..264bd794 100644 --- a/x/cabi/realloc.go +++ b/x/cabi/realloc.go @@ -6,6 +6,7 @@ import "unsafe" // the host-guest boundary. // // Note: the use of uintptr assumes 32-bit pointers when compiled for wasm or wasm32. +//go:wasmexport cabi_realloc func realloc(ptr unsafe.Pointer, size, align, newsize uintptr) unsafe.Pointer { if newsize <= size { return unsafe.Add(ptr, offset(uintptr(ptr), align)) From b0c1cb805377e8436b3b0ada53647ea19c47c316 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Sun, 15 Jun 2025 18:13:56 +0200 Subject: [PATCH 4/6] Use `unsafe.Pointer` instead of types not supported by Go in `wasm{im,ex}port` Go has slightly tighter constraints on `wasmimport` and `wasmexport` than TinyGo, which this commit works around: - It disallows use of `*string` (as opposed to `string`) - It disallows use of `*cm.List` (probably because of the `[any]`, but I'm not sure at all) Signed-off-by: Till Schneidereit --- .../v0.2.0/environment/environment.wasm.go | 8 ++--- .../cli/v0.2.0/environment/environment.wit.go | 7 +++-- .../v0.2.0/preopens/preopens.wasm.go | 4 +-- .../v0.2.0/preopens/preopens.wit.go | 3 +- .../filesystem/v0.2.0/types/types.wasm.go | 7 +++-- .../wasi/filesystem/v0.2.0/types/types.wit.go | 7 +++-- .../wasi/io/v0.2.0/error/error.wasm.go | 6 +++- .../wasi/io/v0.2.0/error/error.wit.go | 3 +- .../wasi/io/v0.2.0/poll/poll.wasm.go | 4 +-- .../generated/wasi/io/v0.2.0/poll/poll.wit.go | 3 +- .../wasi/io/v0.2.0/streams/streams.wasm.go | 5 +-- .../wasi/io/v0.2.0/streams/streams.wit.go | 5 +-- .../random/v0.2.0/insecure/insecure.wasm.go | 4 +-- .../random/v0.2.0/insecure/insecure.wit.go | 3 +- .../wasi/random/v0.2.0/random/random.wasm.go | 4 +-- .../wasi/random/v0.2.0/random/random.wit.go | 3 +- .../wasi/sockets/v0.2.0/udp/udp.wasm.go | 5 +-- .../wasi/sockets/v0.2.0/udp/udp.wit.go | 5 +-- wit/bindgen/generator.go | 31 ++++++++++++++++--- 19 files changed, 77 insertions(+), 40 deletions(-) diff --git a/tests/generated/wasi/cli/v0.2.0/environment/environment.wasm.go b/tests/generated/wasi/cli/v0.2.0/environment/environment.wasm.go index 89bb596b..aea46955 100644 --- a/tests/generated/wasi/cli/v0.2.0/environment/environment.wasm.go +++ b/tests/generated/wasi/cli/v0.2.0/environment/environment.wasm.go @@ -3,19 +3,19 @@ package environment import ( - "go.bytecodealliance.org/cm" + "unsafe" ) // This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0". //go:wasmimport wasi:cli/environment@0.2.0 get-environment //go:noescape -func wasmimport_GetEnvironment(result *cm.List[[2]string]) +func wasmimport_GetEnvironment(result unsafe.Pointer) //go:wasmimport wasi:cli/environment@0.2.0 get-arguments //go:noescape -func wasmimport_GetArguments(result *cm.List[string]) +func wasmimport_GetArguments(result unsafe.Pointer) //go:wasmimport wasi:cli/environment@0.2.0 initial-cwd //go:noescape -func wasmimport_InitialCWD(result *cm.Option[string]) +func wasmimport_InitialCWD(result unsafe.Pointer) diff --git a/tests/generated/wasi/cli/v0.2.0/environment/environment.wit.go b/tests/generated/wasi/cli/v0.2.0/environment/environment.wit.go index 3a3b6247..9c50818e 100644 --- a/tests/generated/wasi/cli/v0.2.0/environment/environment.wit.go +++ b/tests/generated/wasi/cli/v0.2.0/environment/environment.wit.go @@ -5,6 +5,7 @@ package environment import ( "go.bytecodealliance.org/cm" + "unsafe" ) // GetEnvironment represents the imported function "get-environment". @@ -22,7 +23,7 @@ import ( // //go:nosplit func GetEnvironment() (result cm.List[[2]string]) { - wasmimport_GetEnvironment(&result) + wasmimport_GetEnvironment(unsafe.Pointer(&result)) return } @@ -34,7 +35,7 @@ func GetEnvironment() (result cm.List[[2]string]) { // //go:nosplit func GetArguments() (result cm.List[string]) { - wasmimport_GetArguments(&result) + wasmimport_GetArguments(unsafe.Pointer(&result)) return } @@ -47,6 +48,6 @@ func GetArguments() (result cm.List[string]) { // //go:nosplit func InitialCWD() (result cm.Option[string]) { - wasmimport_InitialCWD(&result) + wasmimport_InitialCWD(unsafe.Pointer(&result)) return } diff --git a/tests/generated/wasi/filesystem/v0.2.0/preopens/preopens.wasm.go b/tests/generated/wasi/filesystem/v0.2.0/preopens/preopens.wasm.go index 1bcd416a..6e89a084 100644 --- a/tests/generated/wasi/filesystem/v0.2.0/preopens/preopens.wasm.go +++ b/tests/generated/wasi/filesystem/v0.2.0/preopens/preopens.wasm.go @@ -3,11 +3,11 @@ package preopens import ( - "go.bytecodealliance.org/cm" + "unsafe" ) // This file contains wasmimport and wasmexport declarations for "wasi:filesystem@0.2.0". //go:wasmimport wasi:filesystem/preopens@0.2.0 get-directories //go:noescape -func wasmimport_GetDirectories(result *cm.List[cm.Tuple[Descriptor, string]]) +func wasmimport_GetDirectories(result unsafe.Pointer) diff --git a/tests/generated/wasi/filesystem/v0.2.0/preopens/preopens.wit.go b/tests/generated/wasi/filesystem/v0.2.0/preopens/preopens.wit.go index e03e289b..a9ced7fb 100644 --- a/tests/generated/wasi/filesystem/v0.2.0/preopens/preopens.wit.go +++ b/tests/generated/wasi/filesystem/v0.2.0/preopens/preopens.wit.go @@ -6,6 +6,7 @@ package preopens import ( "go.bytecodealliance.org/cm" "tests/generated/wasi/filesystem/v0.2.0/types" + "unsafe" ) // Descriptor represents the imported type alias "wasi:filesystem/preopens@0.2.0#descriptor". @@ -21,6 +22,6 @@ type Descriptor = types.Descriptor // //go:nosplit func GetDirectories() (result cm.List[cm.Tuple[Descriptor, string]]) { - wasmimport_GetDirectories(&result) + wasmimport_GetDirectories(unsafe.Pointer(&result)) return } diff --git a/tests/generated/wasi/filesystem/v0.2.0/types/types.wasm.go b/tests/generated/wasi/filesystem/v0.2.0/types/types.wasm.go index b97dad84..c9d8cfde 100644 --- a/tests/generated/wasi/filesystem/v0.2.0/types/types.wasm.go +++ b/tests/generated/wasi/filesystem/v0.2.0/types/types.wasm.go @@ -4,6 +4,7 @@ package types import ( "go.bytecodealliance.org/cm" + "unsafe" ) // This file contains wasmimport and wasmexport declarations for "wasi:filesystem@0.2.0". @@ -54,7 +55,7 @@ func wasmimport_DescriptorOpenAt(self0 uint32, pathFlags0 uint32, path0 *uint8, //go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.read //go:noescape -func wasmimport_DescriptorRead(self0 uint32, length0 uint64, offset0 uint64, result *cm.Result[TupleListU8BoolShape, cm.Tuple[cm.List[uint8], bool], ErrorCode]) +func wasmimport_DescriptorRead(self0 uint32, length0 uint64, offset0 uint64, result unsafe.Pointer) //go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.read-directory //go:noescape @@ -66,7 +67,7 @@ func wasmimport_DescriptorReadViaStream(self0 uint32, offset0 uint64, result *cm //go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.readlink-at //go:noescape -func wasmimport_DescriptorReadLinkAt(self0 uint32, path0 *uint8, path1 uint32, result *cm.Result[string, string, ErrorCode]) +func wasmimport_DescriptorReadLinkAt(self0 uint32, path0 *uint8, path1 uint32, result unsafe.Pointer) //go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.remove-directory-at //go:noescape @@ -126,7 +127,7 @@ func wasmimport_DirectoryEntryStreamResourceDrop(self0 uint32) //go:wasmimport wasi:filesystem/types@0.2.0 [method]directory-entry-stream.read-directory-entry //go:noescape -func wasmimport_DirectoryEntryStreamReadDirectoryEntry(self0 uint32, result *cm.Result[OptionDirectoryEntryShape, cm.Option[DirectoryEntry], ErrorCode]) +func wasmimport_DirectoryEntryStreamReadDirectoryEntry(self0 uint32, result unsafe.Pointer) //go:wasmimport wasi:filesystem/types@0.2.0 filesystem-error-code //go:noescape diff --git a/tests/generated/wasi/filesystem/v0.2.0/types/types.wit.go b/tests/generated/wasi/filesystem/v0.2.0/types/types.wit.go index f8252d54..e63b306e 100644 --- a/tests/generated/wasi/filesystem/v0.2.0/types/types.wit.go +++ b/tests/generated/wasi/filesystem/v0.2.0/types/types.wit.go @@ -32,6 +32,7 @@ import ( "go.bytecodealliance.org/cm" wallclock "tests/generated/wasi/clocks/v0.2.0/wall-clock" "tests/generated/wasi/io/v0.2.0/streams" + "unsafe" ) // InputStream represents the imported type alias "wasi:filesystem/types@0.2.0#input-stream". @@ -931,7 +932,7 @@ func (self Descriptor) Read(length FileSize, offset FileSize) (result cm.Result[ self0 := cm.Reinterpret[uint32](self) length0 := (uint64)(length) offset0 := (uint64)(offset) - wasmimport_DescriptorRead((uint32)(self0), (uint64)(length0), (uint64)(offset0), &result) + wasmimport_DescriptorRead((uint32)(self0), (uint64)(length0), (uint64)(offset0), unsafe.Pointer(&result)) return } @@ -992,7 +993,7 @@ func (self Descriptor) ReadViaStream(offset FileSize) (result cm.Result[InputStr func (self Descriptor) ReadLinkAt(path string) (result cm.Result[string, string, ErrorCode]) { self0 := cm.Reinterpret[uint32](self) path0, path1 := cm.LowerString(path) - wasmimport_DescriptorReadLinkAt((uint32)(self0), (*uint8)(path0), (uint32)(path1), &result) + wasmimport_DescriptorReadLinkAt((uint32)(self0), (*uint8)(path0), (uint32)(path1), unsafe.Pointer(&result)) return } @@ -1278,7 +1279,7 @@ func (self DirectoryEntryStream) ResourceDrop() { //go:nosplit func (self DirectoryEntryStream) ReadDirectoryEntry() (result cm.Result[OptionDirectoryEntryShape, cm.Option[DirectoryEntry], ErrorCode]) { self0 := cm.Reinterpret[uint32](self) - wasmimport_DirectoryEntryStreamReadDirectoryEntry((uint32)(self0), &result) + wasmimport_DirectoryEntryStreamReadDirectoryEntry((uint32)(self0), unsafe.Pointer(&result)) return } diff --git a/tests/generated/wasi/io/v0.2.0/error/error.wasm.go b/tests/generated/wasi/io/v0.2.0/error/error.wasm.go index e254b5d8..a849a12c 100644 --- a/tests/generated/wasi/io/v0.2.0/error/error.wasm.go +++ b/tests/generated/wasi/io/v0.2.0/error/error.wasm.go @@ -2,6 +2,10 @@ package ioerror +import ( + "unsafe" +) + // This file contains wasmimport and wasmexport declarations for "wasi:io@0.2.0". //go:wasmimport wasi:io/error@0.2.0 [resource-drop]error @@ -10,4 +14,4 @@ func wasmimport_ErrorResourceDrop(self0 uint32) //go:wasmimport wasi:io/error@0.2.0 [method]error.to-debug-string //go:noescape -func wasmimport_ErrorToDebugString(self0 uint32, result *string) +func wasmimport_ErrorToDebugString(self0 uint32, result unsafe.Pointer) diff --git a/tests/generated/wasi/io/v0.2.0/error/error.wit.go b/tests/generated/wasi/io/v0.2.0/error/error.wit.go index 24bad438..8651c9c3 100644 --- a/tests/generated/wasi/io/v0.2.0/error/error.wit.go +++ b/tests/generated/wasi/io/v0.2.0/error/error.wit.go @@ -5,6 +5,7 @@ package ioerror import ( "go.bytecodealliance.org/cm" + "unsafe" ) // Error represents the imported resource "wasi:io/error@0.2.0#error". @@ -58,6 +59,6 @@ func (self Error) ResourceDrop() { //go:nosplit func (self Error) ToDebugString() (result string) { self0 := cm.Reinterpret[uint32](self) - wasmimport_ErrorToDebugString((uint32)(self0), &result) + wasmimport_ErrorToDebugString((uint32)(self0), unsafe.Pointer(&result)) return } diff --git a/tests/generated/wasi/io/v0.2.0/poll/poll.wasm.go b/tests/generated/wasi/io/v0.2.0/poll/poll.wasm.go index f7c55c3d..4092959a 100644 --- a/tests/generated/wasi/io/v0.2.0/poll/poll.wasm.go +++ b/tests/generated/wasi/io/v0.2.0/poll/poll.wasm.go @@ -3,7 +3,7 @@ package poll import ( - "go.bytecodealliance.org/cm" + "unsafe" ) // This file contains wasmimport and wasmexport declarations for "wasi:io@0.2.0". @@ -22,4 +22,4 @@ func wasmimport_PollableReady(self0 uint32) (result0 uint32) //go:wasmimport wasi:io/poll@0.2.0 poll //go:noescape -func wasmimport_Poll(in0 *Pollable, in1 uint32, result *cm.List[uint32]) +func wasmimport_Poll(in0 *Pollable, in1 uint32, result unsafe.Pointer) diff --git a/tests/generated/wasi/io/v0.2.0/poll/poll.wit.go b/tests/generated/wasi/io/v0.2.0/poll/poll.wit.go index 10c0033b..2364e32b 100644 --- a/tests/generated/wasi/io/v0.2.0/poll/poll.wit.go +++ b/tests/generated/wasi/io/v0.2.0/poll/poll.wit.go @@ -8,6 +8,7 @@ package poll import ( "go.bytecodealliance.org/cm" + "unsafe" ) // Pollable represents the imported resource "wasi:io/poll@0.2.0#pollable". @@ -87,6 +88,6 @@ func (self Pollable) Ready() (result bool) { //go:nosplit func Poll(in cm.List[Pollable]) (result cm.List[uint32]) { in0, in1 := cm.LowerList(in) - wasmimport_Poll((*Pollable)(in0), (uint32)(in1), &result) + wasmimport_Poll((*Pollable)(in0), (uint32)(in1), unsafe.Pointer(&result)) return } diff --git a/tests/generated/wasi/io/v0.2.0/streams/streams.wasm.go b/tests/generated/wasi/io/v0.2.0/streams/streams.wasm.go index eec56645..f7dcd00f 100644 --- a/tests/generated/wasi/io/v0.2.0/streams/streams.wasm.go +++ b/tests/generated/wasi/io/v0.2.0/streams/streams.wasm.go @@ -4,6 +4,7 @@ package streams import ( "go.bytecodealliance.org/cm" + "unsafe" ) // This file contains wasmimport and wasmexport declarations for "wasi:io@0.2.0". @@ -14,7 +15,7 @@ func wasmimport_InputStreamResourceDrop(self0 uint32) //go:wasmimport wasi:io/streams@0.2.0 [method]input-stream.blocking-read //go:noescape -func wasmimport_InputStreamBlockingRead(self0 uint32, len0 uint64, result *cm.Result[cm.List[uint8], cm.List[uint8], StreamError]) +func wasmimport_InputStreamBlockingRead(self0 uint32, len0 uint64, result unsafe.Pointer) //go:wasmimport wasi:io/streams@0.2.0 [method]input-stream.blocking-skip //go:noescape @@ -22,7 +23,7 @@ func wasmimport_InputStreamBlockingSkip(self0 uint32, len0 uint64, result *cm.Re //go:wasmimport wasi:io/streams@0.2.0 [method]input-stream.read //go:noescape -func wasmimport_InputStreamRead(self0 uint32, len0 uint64, result *cm.Result[cm.List[uint8], cm.List[uint8], StreamError]) +func wasmimport_InputStreamRead(self0 uint32, len0 uint64, result unsafe.Pointer) //go:wasmimport wasi:io/streams@0.2.0 [method]input-stream.skip //go:noescape diff --git a/tests/generated/wasi/io/v0.2.0/streams/streams.wit.go b/tests/generated/wasi/io/v0.2.0/streams/streams.wit.go index e3a381b7..f4937aaa 100644 --- a/tests/generated/wasi/io/v0.2.0/streams/streams.wit.go +++ b/tests/generated/wasi/io/v0.2.0/streams/streams.wit.go @@ -13,6 +13,7 @@ import ( "go.bytecodealliance.org/cm" ioerror "tests/generated/wasi/io/v0.2.0/error" "tests/generated/wasi/io/v0.2.0/poll" + "unsafe" ) // Error represents the imported type alias "wasi:io/streams@0.2.0#error". @@ -110,7 +111,7 @@ func (self InputStream) ResourceDrop() { func (self InputStream) BlockingRead(len_ uint64) (result cm.Result[cm.List[uint8], cm.List[uint8], StreamError]) { self0 := cm.Reinterpret[uint32](self) len0 := (uint64)(len_) - wasmimport_InputStreamBlockingRead((uint32)(self0), (uint64)(len0), &result) + wasmimport_InputStreamBlockingRead((uint32)(self0), (uint64)(len0), unsafe.Pointer(&result)) return } @@ -164,7 +165,7 @@ func (self InputStream) BlockingSkip(len_ uint64) (result cm.Result[uint64, uint func (self InputStream) Read(len_ uint64) (result cm.Result[cm.List[uint8], cm.List[uint8], StreamError]) { self0 := cm.Reinterpret[uint32](self) len0 := (uint64)(len_) - wasmimport_InputStreamRead((uint32)(self0), (uint64)(len0), &result) + wasmimport_InputStreamRead((uint32)(self0), (uint64)(len0), unsafe.Pointer(&result)) return } diff --git a/tests/generated/wasi/random/v0.2.0/insecure/insecure.wasm.go b/tests/generated/wasi/random/v0.2.0/insecure/insecure.wasm.go index 498bfe4a..1b36f52b 100644 --- a/tests/generated/wasi/random/v0.2.0/insecure/insecure.wasm.go +++ b/tests/generated/wasi/random/v0.2.0/insecure/insecure.wasm.go @@ -3,14 +3,14 @@ package insecure import ( - "go.bytecodealliance.org/cm" + "unsafe" ) // This file contains wasmimport and wasmexport declarations for "wasi:random@0.2.0". //go:wasmimport wasi:random/insecure@0.2.0 get-insecure-random-bytes //go:noescape -func wasmimport_GetInsecureRandomBytes(len0 uint64, result *cm.List[uint8]) +func wasmimport_GetInsecureRandomBytes(len0 uint64, result unsafe.Pointer) //go:wasmimport wasi:random/insecure@0.2.0 get-insecure-random-u64 //go:noescape diff --git a/tests/generated/wasi/random/v0.2.0/insecure/insecure.wit.go b/tests/generated/wasi/random/v0.2.0/insecure/insecure.wit.go index 295a1ada..f632be13 100644 --- a/tests/generated/wasi/random/v0.2.0/insecure/insecure.wit.go +++ b/tests/generated/wasi/random/v0.2.0/insecure/insecure.wit.go @@ -10,6 +10,7 @@ package insecure import ( "go.bytecodealliance.org/cm" + "unsafe" ) // GetInsecureRandomBytes represents the imported function "get-insecure-random-bytes". @@ -28,7 +29,7 @@ import ( //go:nosplit func GetInsecureRandomBytes(len_ uint64) (result cm.List[uint8]) { len0 := (uint64)(len_) - wasmimport_GetInsecureRandomBytes((uint64)(len0), &result) + wasmimport_GetInsecureRandomBytes((uint64)(len0), unsafe.Pointer(&result)) return } diff --git a/tests/generated/wasi/random/v0.2.0/random/random.wasm.go b/tests/generated/wasi/random/v0.2.0/random/random.wasm.go index 9096457b..ed606a2a 100644 --- a/tests/generated/wasi/random/v0.2.0/random/random.wasm.go +++ b/tests/generated/wasi/random/v0.2.0/random/random.wasm.go @@ -3,14 +3,14 @@ package random import ( - "go.bytecodealliance.org/cm" + "unsafe" ) // This file contains wasmimport and wasmexport declarations for "wasi:random@0.2.0". //go:wasmimport wasi:random/random@0.2.0 get-random-bytes //go:noescape -func wasmimport_GetRandomBytes(len0 uint64, result *cm.List[uint8]) +func wasmimport_GetRandomBytes(len0 uint64, result unsafe.Pointer) //go:wasmimport wasi:random/random@0.2.0 get-random-u64 //go:noescape diff --git a/tests/generated/wasi/random/v0.2.0/random/random.wit.go b/tests/generated/wasi/random/v0.2.0/random/random.wit.go index bb90e7e0..46476979 100644 --- a/tests/generated/wasi/random/v0.2.0/random/random.wit.go +++ b/tests/generated/wasi/random/v0.2.0/random/random.wit.go @@ -10,6 +10,7 @@ package random import ( "go.bytecodealliance.org/cm" + "unsafe" ) // GetRandomBytes represents the imported function "get-random-bytes". @@ -32,7 +33,7 @@ import ( //go:nosplit func GetRandomBytes(len_ uint64) (result cm.List[uint8]) { len0 := (uint64)(len_) - wasmimport_GetRandomBytes((uint64)(len0), &result) + wasmimport_GetRandomBytes((uint64)(len0), unsafe.Pointer(&result)) return } diff --git a/tests/generated/wasi/sockets/v0.2.0/udp/udp.wasm.go b/tests/generated/wasi/sockets/v0.2.0/udp/udp.wasm.go index 83ce8ca7..bfc52f15 100644 --- a/tests/generated/wasi/sockets/v0.2.0/udp/udp.wasm.go +++ b/tests/generated/wasi/sockets/v0.2.0/udp/udp.wasm.go @@ -4,6 +4,7 @@ package udp import ( "go.bytecodealliance.org/cm" + "unsafe" ) // This file contains wasmimport and wasmexport declarations for "wasi:sockets@0.2.0". @@ -70,7 +71,7 @@ func wasmimport_IncomingDatagramStreamResourceDrop(self0 uint32) //go:wasmimport wasi:sockets/udp@0.2.0 [method]incoming-datagram-stream.receive //go:noescape -func wasmimport_IncomingDatagramStreamReceive(self0 uint32, maxResults0 uint64, result *cm.Result[cm.List[IncomingDatagram], cm.List[IncomingDatagram], ErrorCode]) +func wasmimport_IncomingDatagramStreamReceive(self0 uint32, maxResults0 uint64, result unsafe.Pointer) //go:wasmimport wasi:sockets/udp@0.2.0 [method]incoming-datagram-stream.subscribe //go:noescape @@ -86,7 +87,7 @@ func wasmimport_OutgoingDatagramStreamCheckSend(self0 uint32, result *cm.Result[ //go:wasmimport wasi:sockets/udp@0.2.0 [method]outgoing-datagram-stream.send //go:noescape -func wasmimport_OutgoingDatagramStreamSend(self0 uint32, datagrams0 *OutgoingDatagram, datagrams1 uint32, result *cm.Result[uint64, uint64, ErrorCode]) +func wasmimport_OutgoingDatagramStreamSend(self0 uint32, datagrams0 unsafe.Pointer, datagrams1 uint32, result *cm.Result[uint64, uint64, ErrorCode]) //go:wasmimport wasi:sockets/udp@0.2.0 [method]outgoing-datagram-stream.subscribe //go:noescape diff --git a/tests/generated/wasi/sockets/v0.2.0/udp/udp.wit.go b/tests/generated/wasi/sockets/v0.2.0/udp/udp.wit.go index 67c4110f..32ef9787 100644 --- a/tests/generated/wasi/sockets/v0.2.0/udp/udp.wit.go +++ b/tests/generated/wasi/sockets/v0.2.0/udp/udp.wit.go @@ -7,6 +7,7 @@ import ( "go.bytecodealliance.org/cm" "tests/generated/wasi/io/v0.2.0/poll" "tests/generated/wasi/sockets/v0.2.0/network" + "unsafe" ) // Pollable represents the imported type alias "wasi:sockets/udp@0.2.0#pollable". @@ -445,7 +446,7 @@ func (self IncomingDatagramStream) ResourceDrop() { func (self IncomingDatagramStream) Receive(maxResults uint64) (result cm.Result[cm.List[IncomingDatagram], cm.List[IncomingDatagram], ErrorCode]) { self0 := cm.Reinterpret[uint32](self) maxResults0 := (uint64)(maxResults) - wasmimport_IncomingDatagramStreamReceive((uint32)(self0), (uint64)(maxResults0), &result) + wasmimport_IncomingDatagramStreamReceive((uint32)(self0), (uint64)(maxResults0), unsafe.Pointer(&result)) return } @@ -562,7 +563,7 @@ func (self OutgoingDatagramStream) CheckSend() (result cm.Result[uint64, uint64, func (self OutgoingDatagramStream) Send(datagrams cm.List[OutgoingDatagram]) (result cm.Result[uint64, uint64, ErrorCode]) { self0 := cm.Reinterpret[uint32](self) datagrams0, datagrams1 := cm.LowerList(datagrams) - wasmimport_OutgoingDatagramStreamSend((uint32)(self0), (*OutgoingDatagram)(datagrams0), (uint32)(datagrams1), &result) + wasmimport_OutgoingDatagramStreamSend((uint32)(self0), (unsafe.Pointer)(datagrams0), (uint32)(datagrams1), &result) return } diff --git a/wit/bindgen/generator.go b/wit/bindgen/generator.go index 2687b0f6..c586774a 100644 --- a/wit/bindgen/generator.go +++ b/wit/bindgen/generator.go @@ -692,6 +692,10 @@ func (g *generator) typeDefRep(file *gen.File, dir wit.Direction, t *wit.TypeDef } func (g *generator) pointerRep(file *gen.File, dir wit.Direction, p *wit.Pointer) string { + if wit.HasPointer(p.Type) { + file.Import("unsafe") + return "unsafe.Pointer" + } return "*" + g.typeRep(file, dir, p.Type) } @@ -1882,8 +1886,13 @@ func (g *generator) defineImportedFunction(decl *funcDecl) error { t := derefPointer(p.typ) // TODO: this logic is ugly if t != nil && (t == compoundParams.typ || t == compoundResults.typ || p.typ == pointerResult.typ) { - b.WriteRune('&') - b.WriteString(p.name) + if wit.HasPointer(t) { + file.Import("unsafe") + stringio.Write(&b, "unsafe.Pointer(&", p.name, ")") + } else { + b.WriteRune('&') + b.WriteString(p.name) + } } else { b.WriteString(g.cast(file, p.dir, p.typ, p.typ, p.name)) } @@ -2010,7 +2019,12 @@ func (g *generator) defineExportedFunction(decl *funcDecl) error { i := 0 for _, p := range callParams { if i < len(decl.wasmFunc.params) && p.typ == derefPointer(decl.wasmFunc.params[i].typ) { - stringio.Write(wasmFile, p.name, " := *", decl.wasmFunc.params[i].name, "\n") + stringio.Write(wasmFile, p.name, " := *") + if wit.HasPointer(p.typ) { + stringio.Write(wasmFile, "(*", g.typeRep(file, dir, p.typ), ")(", decl.wasmFunc.params[i].name, ")\n") + } else { + stringio.Write(wasmFile, decl.wasmFunc.params[i].name, "\n") + } i++ continue } @@ -2022,6 +2036,8 @@ func (g *generator) defineExportedFunction(decl *funcDecl) error { stringio.Write(wasmFile, p.name, " := ", g.liftType(wasmFile, p.dir, p.typ, input), "\n") i += len(flat) } + } else { + stringio.Write(wasmFile, "params_ := (*", decl.wasmFunc.name, "_params", ")(", compoundParams.name, ")\n") } // Emit call to caller-defined Go function @@ -2059,7 +2075,7 @@ func (g *generator) defineExportedFunction(decl *funcDecl) error { if i > 0 { wasmFile.WriteString(", ") } - stringio.Write(wasmFile, compoundParams.name, ".", fieldName(f.Name, false)) + stringio.Write(wasmFile, "params_.", fieldName(f.Name, false)) } } else { for i, p := range callParams { @@ -2081,7 +2097,12 @@ func (g *generator) defineExportedFunction(decl *funcDecl) error { if i < len(decl.wasmFunc.results) { wr := decl.wasmFunc.results[i] if r.typ == derefPointer(wr.typ) { - stringio.Write(wasmFile, wr.name, " = &", r.name, "\n") + if wit.HasPointer(r.typ) { + wasmFile.Import("unsafe") + stringio.Write(wasmFile, wr.name, " = unsafe.Pointer(&", r.name, ")\n") + } else { + stringio.Write(wasmFile, wr.name, " = &", r.name, "\n") + } i++ continue } From 4604c9ad1f2ffc4988fe66d879145eb2c6bd93f9 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Fri, 20 Jun 2025 12:10:44 +0200 Subject: [PATCH 5/6] Fix and document the cabi_realloc implementation Signed-off-by: Till Schneidereit --- x/cabi/realloc.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/x/cabi/realloc.go b/x/cabi/realloc.go index 264bd794..3b0b81eb 100644 --- a/x/cabi/realloc.go +++ b/x/cabi/realloc.go @@ -2,18 +2,79 @@ package cabi import "unsafe" +// The Go runtime-internal sbrk function. +// Note that this isn't *really* sbrk, and doesn't necessarily result in heap +// growth. Instead, it reserves a block of memory of size n bytes, which then +// isn't used by Go's GC allocator. +// The heap will only actually grown if the requested allocation doesn't fit. +//go:linkname sbrk runtime.sbrk +func sbrk(n uintptr) unsafe.Pointer + +// Returns the memory region from `v` to `v + n` to the Go runtime, enabling +// its use by the garbage collector. +//go:linkname sysFreeOS runtime.sysFreeOS +func sysFreeOS(v unsafe.Pointer, n uintptr) + +var useGCAllocations = false + +func init() { + // Once `init` is called, the runtime has been initialized, and we can + // start using managed memory. + useGCAllocations = true +} + // realloc allocates or reallocates memory for Component Model calls across // the host-guest boundary. // -// Note: the use of uintptr assumes 32-bit pointers when compiled for wasm or wasm32. +// Note: while in Go, as opposed to TinyGo, `uintptr` is 64-bit, `go:wasmexport` +// properly coerces the parameters/return values to and from 32-bit values when +// compiled for wasm or wasm32. +// TODO: file bug for sysReserveAlignedSbrk not always releasing the memlock. //go:wasmexport cabi_realloc func realloc(ptr unsafe.Pointer, size, align, newsize uintptr) unsafe.Pointer { + + // If the Go runtime has not been initialized, we need to + // allocate memory using sbrk directly. + // This happens because the runtime itself does two calls to + // imported functions during initialization, which result in + // calls to `cabi_realloc` before the runtime is fully initialized. + if !useGCAllocations { + if newsize == 0 { + if ptr != nil { + // Free the old pointer if it is not nil. + // Note that in practice, this case isn't ever hit: + // `cabi_realloc` isn't called with a non-nil pointer before the + // Go runtime is fully initialized. + sysFreeOS(ptr, size) + } + + return nil + } + + alignedSize := newsize + offset(newsize, align) + unaligned := sbrk(alignedSize) + off := offset(uintptr(unaligned), align) + newptr := unsafe.Add(unaligned, off) + if ptr != nil && newptr != nil && size > 0 { + // Copy the old data to the new pointer. + // Note that in practice, this case isn't ever hit: + // `cabi_realloc` isn't called with a non-nil pointer. + copy(unsafe.Slice((*byte)(newptr), size), unsafe.Slice((*byte)(ptr), size)) + } + if ptr != nil { + // Free the old pointer if it is not nil. + sysFreeOS(ptr, size) + } + return newptr + } + if newsize <= size { return unsafe.Add(ptr, offset(uintptr(ptr), align)) } newptr := alloc(newsize, align) if size > 0 { copy(unsafe.Slice((*byte)(newptr), newsize), unsafe.Slice((*byte)(ptr), size)) + } return newptr } @@ -29,6 +90,16 @@ func offset(ptr, align uintptr) uintptr { // It attempts to align the allocated memory by allocating a slice of // a type that matches the desired alignment. It aligns to 16 bytes for // values of align other than 1, 2, 4, or 8. +// +// The allocation itself is done using the Go runtime's GC allocator. +// We then return a raw pointer to the underlying memory, without keeping +// any references to the allocated object itself. That would be very bad +// indeed if there was a risk of the GC running before we're done using +// the memory, or before it's rooted by another GC object. +// +// Since WebAssembly is a single-threaded environment, and the use of the +// returned memory is tightly controlled by generated bindings code, neither +// of these issues is a problem in practice. func alloc(size, align uintptr) unsafe.Pointer { switch align { case 1: From b8d70f2b346214580929a1ac0af1417078b62cc0 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Fri, 20 Jun 2025 12:12:06 +0200 Subject: [PATCH 6/6] Correct `isSome` representation in `option` Signed-off-by: Till Schneidereit --- cm/option.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cm/option.go b/cm/option.go index 5b855806..6d10c955 100644 --- a/cm/option.go +++ b/cm/option.go @@ -29,7 +29,7 @@ func Some[T any](v T) Option[T] { // followed by storage for the associated type T. type option[T any] struct { _ HostLayout - isSome uint8 + isSome uint32 some T } @@ -47,6 +47,10 @@ func (o *option[T]) Some() *T { return nil } +func (o option[T]) IsSome() bool { + return o.isSome == 1 +} + // Value returns T if o represents the some case, // or the zero value of T if o represents the none case. // This does not have a pointer receiver, so it can be chained.