Compare commits

...

95 Commits

Author SHA1 Message Date
Ayke van Laethem 2dd06c4378 [DO NOT MERGE] testing Windows CI failures 2024-11-19 11:09:48 +01:00
Ayke van Laethem e12da15f7d cgo: support function-like macros
This is needed for code like this:

    #define __WASI_ERRNO_INVAL (UINT16_C(28))
    #define EINVAL __WASI_ERRNO_INVAL
2024-11-18 18:35:20 +01:00
Ayke van Laethem c4867c8743 cgo: define idents referenced only from macros 2024-11-18 18:35:20 +01:00
Ayke van Laethem 8068419854 runtime: heapptr only needs to be initialized once
There is no need to initialize it twice.
2024-11-18 17:14:55 +01:00
Ayke van Laethem 9b3eb3fe59 ci: run at least some tests on older Go/LLVM versions
These should make sure basic functionality is still working.
Using the `-short` flag to avoid taking too long to run all tests (and
to install all the necessary emulators), and because some targets might
not work in older Go/LLVM versions (such as WASI).

This does _not_ run tests and checks against expected IR, because LLVM
IR changes a lot across versions.
2024-11-18 16:42:14 +01:00
leongross 1dcccf87b5 linux: add runtime.fcntl function
This is needed for the internal/syscall/unix package.

Signed-off-by: leongross <leon.gross@9elements.com>
2024-11-18 14:44:06 +01:00
Ayke van Laethem d51ef253a9 builder: whitelist temporary directory env var for Clang invocation
It looks like this breaks on Windows:
https://github.com/tinygo-org/tinygo/issues/4557
I haven't confirmed this is indeed the problem, but it would make sense.
And passing through the temporary directory seems like a good idea
regardless, there's not much that could break due to that.
2024-11-15 18:27:48 +01:00
Ayke van Laethem 6d4dfcf72f compiler, runtime: move constants into shared package
Use a single package for certain constants that must be the same between
the compiler and the runtime.

While just using the same values in both places works, this is much more
obvious and harder to mess up. It also avoids the need for comments
pointing to the other location the constant is defined. And having it in
code makes it possible for IDEs to analyze the source.

In the future, more such constants and maybe algorithms can be added.
2024-11-15 10:11:57 +01:00
sivchari ac9f72be61 support to parse devl version
Signed-off-by: sivchari <shibuuuu5@gmail.com>
2024-11-15 09:21:47 +01:00
Ayke van Laethem 258dac2324 wasm: tidy up wasm_exec.js a bit 2024-11-14 11:02:54 -08:00
Ayke van Laethem b7d91e2f33 ci: use TinyGo version in artifact files
This avoids needing to rename them ourselves (which is kinda annoying)
and also avoids mistakes in the process.
2024-11-14 10:38:56 +01:00
Ayke van Laethem 860697257b runtime: optimize findHead
This is similar to https://github.com/tinygo-org/tinygo/pull/3899, but
smaller and hopefully just as efficient.

Thanks to @HattoriHanzo031 for starting this work, benchmarking, and for
improving the performance of the code even further.
2024-11-14 09:28:25 +01:00
Damian Gryski 91563cff1f targets/wasm_exec: call process.exit() when go.run() returns 2024-11-14 09:16:25 +01:00
Ben Krieger 4f96a50fd0 reflect: fix Copy of non-pointer array with size > 64bits 2024-11-13 10:28:33 -07:00
Ben Krieger e98fea0bba reflect: add Value.Clear; support anytype->interface{}, Slice->(*)Array in Value.Convert 2024-11-13 10:28:33 -07:00
Ayke van Laethem c728e031df goenv: read git hash embedded in the binary
The git hash that's part of `tinygo version` is now read from the binary
itself instead of embedding it with a `-ldflags` flag. This means it is
also present when building TinyGo using `go build` or `go install`.
2024-11-10 08:59:22 +01:00
Ayke van Laethem ceb7891986 wasm: correctly return from run() in wasm_exec.js
Instead of hanging forever, it should return the exit code from os.Exit.
2024-11-08 11:55:38 +01:00
Ayke van Laethem 04a7baec3e wasm: support //go:wasmexport functions after a call to time.Sleep
This fixes a bug where `//go:wasmexport` functions would not be allowed
anymore after a call to `time.Sleep` (when using `-buildmode=default`).
2024-11-08 11:55:38 +01:00
Ayke van Laethem 8ff97bdedd runtime: remove unnecessary check for negative sleepTicks duration
This is now fixed for every target in the previous commit.

Also see: https://github.com/tinygo-org/tinygo/pull/4239
2024-11-07 15:37:18 +01:00
Ayke van Laethem a6c4287b4d runtime: don't call sleepTicks with a negative duration
There are rare cases where this can happen, see for example
https://github.com/tinygo-org/tinygo/issues/4568
2024-11-07 15:37:18 +01:00
leongross f9f439ad49 os: implement StartProcess
Signed-off-by: leongross <leon.gross@9elements.com>
2024-11-07 09:45:47 +01:00
Randy Reddig c02a8141c7 internal/wasm-tools, syscall: update to wasm-tools-go@v0.3.1 (#4577)
* internal/wasm-tools, internal/cm: update wasm-tools-go to v0.3.1 and regenerate bindings

* syscall: use new (cm.Result).Result() method instead of OK() and Err()
2024-11-04 14:32:17 -08:00
sago35 5862b481a4 goenv: update to new v0.35.0 development version 2024-11-01 09:14:09 +01:00
Joonas Bergius 1648fe8ef2 runtime/trace: stub all public methods
Signed-off-by: Joonas Bergius <joonas@cosmonic.com>
2024-11-01 09:12:59 +01:00
Ayke van Laethem 449eefdb19 compiler: allow deferred panic
This is rare, but apparently some programs do this:

    defer panic("...")

This is emitted in the IR as a builtin function.
2024-11-01 09:11:38 +01:00
Ayke van Laethem 058f62ac08 main: parse extldflags early so we can report the error message
This avoids some weird behavior when the -extldflags flag cannot be
parsed by TinyGo.
2024-11-01 09:10:49 +01:00
Ayke van Laethem 4e49ba597d interrupt: fix bug in interrupt lowering
The alignment wasn't set, so defaulted to 4 (for a 32-bit int). LLVM saw
this, and therefore assumed that a ptrtoint of the pointer would have
had the lowest bits unset. That's an entirely valid optimization, except
that we are using these globals for arbitrary values (and aren't
actually using these globals).

Fixed by setting alignment to 1. It works, though long-term we should
maybe find a different solution for this.
2024-11-01 09:10:25 +01:00
Ayke van Laethem 4b706ae25c test: show output even when a test binary didn't exit cleanly
This was a problem on wasm, where node would exit with a non-zero exit
code when there was a panic.
2024-11-01 08:49:00 +01:00
Ayke van Laethem 1ac26d3d2f test: run TestWasmExportJS tests in parallel 2024-11-01 08:48:19 +01:00
Randy Reddig 0edeaf657f tinygo: revise and simplify wasmtime argument handling (#4555) 2024-10-28 17:57:24 +01:00
Ayke van Laethem 76d5b3d786 sync: don't use volatile in Mutex
Volatile loads/stors are only useful for communication with interrupts
or for memory-mapped I/O. They do not provide any sort of safety for
sync.Mutex, while making it *appear* as if it is more safe.

  * `sync.Mutex` cannot be used safely inside interrupts, because any
    blocking calls (including `Lock`) will cause a runtime panic.
  * For multithreading, `volatile` is also the wrong choice. Atomic
    operations should be used instead, and the current code would not
    work for multithreaded programs anyway.
2024-10-28 16:43:28 +01:00
Ayke van Laethem 915132645e ci: remove 'shell: bash' lines from MacOS build
Unlike Windows, we can just use the default shell here.
2024-10-28 10:22:05 +01:00
Ayke van Laethem 2a76ceb7dd all: version v0.34.0 2024-10-25 18:22:40 +01:00
Ayke van Laethem 69263e7319 GNUmakefile: do not use the -v flag in go test
This makes it easier to find what actually went wrong in CI.
This flag was added in #4431, I think it was unintentional.
2024-10-25 17:01:35 +02:00
Damian Gryski 9a6397b325 runtime: bump markStackSize
Every time we overflow the stack, we have to do a full rescan of the heap.  Making this larger
means fewer overflows and thus fewer secondary+ heap scans.
2024-10-25 16:24:01 +02:00
sago35 b8420e78bb machine/usb/adc/midi: fix PitchBend 2024-10-25 06:25:49 +01:00
sago35 f0d523f778 machine/usb/adc/midi: clarify operator precedence 2024-10-25 06:25:49 +01:00
Damian Gryski 6e6507bf77 runtime: add gc layout info for some basic types 2024-10-24 13:07:17 +02:00
Ayke van Laethem b8fe75a9dd runtime: add support for os/signal
This adds support for enabling and listening to signals on Linux and
MacOS.
2024-10-23 12:25:27 +01:00
Ayke van Laethem 0f95b4102d wasm: use precise GC for WebAssembly (including WASI)
With a few small modifications, all the problems with `-gc=precise` in
WebAssembly seem to have been fixed.

I didn't do any performance measurements, but this is supposed to
improve GC performance.
2024-10-23 09:13:30 +01:00
Randy Reddig 24c11d4ba5 compiler: conform to latest iteration of wasm types proposal (#4501)
compiler: align with current wasm types proposal

https://github.com/golang/go/issues/66984

- Remove int and uint as allowed types in params, results, pointers, or struct fields
- Only allow small integers in pointers, arrays, or struct fields
- enforce structs.HostLayout usage per wasm types proposal
https://github.com/golang/go/issues/66984
- require go1.23 for structs.HostLayout
- use an interface to check if GoVersion() exists
This permits TinyGo to compile with Go 1.21.
- use goenv.Compare instead of WantGoVersion
- testdata/wasmexport: use int32 instead of int
- compiler/testdata: add structs.HostLayout
- compiler/testdata: improve tests for structs.HostLayout
2024-10-22 18:05:04 +02:00
Daniel Esteban 3dcac3b539 Add sponsor button to key repositories 2024-10-22 12:32:15 +01:00
Ayke van Laethem e615c25319 targets: add WaveShare ESP-C3-32S-Kit
I've had this board for a while now, but never added proper TinyGo
support. So here is a PR to do just that.
2024-10-22 11:21:48 +01:00
Ayke van Laethem b2fbbeb771 esp32c3: add smoke tests for a few boards
These boards probably haven't been working since the addition of I2C,
because they were missing some constants in the machine package.
2024-10-22 09:23:31 +01:00
Ayke van Laethem bcfe751f62 fe310: support GPIO PinInput
This is needed to support switching between input and output.
2024-10-21 18:30:31 +01:00
Randy Reddig a191326ea8 goenv: parse patch version, add func Compare to compare two Go version strings (#4536)
goenv: parse patch version, add func Compare to compare two Go version strings
* Parse tests
* add Compare function to compare two Go version strings
* goenv, builder: parse patch version in Go version string
2024-10-21 16:01:59 +02:00
Damian Gryski 23d3a31107 runtime: use unsafe.Slice for leveldb code 2024-10-19 15:16:30 -07:00
Damian Gryski 0dfa57ea04 runtime: use unsafe.Slice in tsip code 2024-10-19 15:16:30 -07:00
Ayke van Laethem 2f9e39e21c runtime: remove minSched hack for wasm
I am not entirely sure what it's doing (it seems related to js.FuncOf),
but tests still seem to pass when this code is removed. So let's remove
it.
2024-10-19 16:00:45 +01:00
Ayke van Laethem cd2bb8333d wasm: add test for js.FuncOf
While there are some browser tests, Node.js is just a lot better for
testing this kind of stuff because it's much faster and we don't need a
browser for this.
2024-10-19 16:00:45 +01:00
Ayke van Laethem 5e3c816373 ci: use macos-13 instead of macos-12 for amd64 builds
The macos-12 runner is being deprecated, so we have to switch to a new
runner: https://github.com/actions/runner-images/issues/10721

The next one is macos-13, which is still amd64.
2024-10-19 13:36:55 +01:00
Damian Gryski 951e50c06f compiler: mark stringFromRunes as nocapture/readonly 2024-10-19 11:02:54 +01:00
Damian Gryski 40c9c66c1d compiler: mark stringFromBytes as nocapture/readonly to help escape analysis
Fixes #4525
2024-10-19 11:02:54 +01:00
leongross 01dac8ba8e os/file_unix: add runtime function net.NewFile stub
Signed-off-by: leongross <leon.gross@9elements.com>
2024-10-18 18:37:49 +01:00
Ayke van Laethem ac5f84e3d7 builder: check for Go toolchain version used to compile TinyGo
This shows a much better error message for issues like this one:
https://github.com/NixOS/nixpkgs/pull/341170#issuecomment-2359237471

The new error message would be:

    cannot compile with Go toolchain version go1.23 (TinyGo was built using toolchain version go1.21.4)
2024-10-18 17:43:17 +02:00
Ayke van Laethem 9583439be4 loader: make sure we always return an error even without type errors
This issue was originally reported here:
https://github.com/NixOS/nixpkgs/pull/341170#issuecomment-2359237471

The fix here isn't a great fix, it turns the error message from this:

    # runtime/interrupt

into this:

    # runtime/interrupt
    package requires newer Go version go1.23

...so not great, because it doesn't show the real error message (which
is that TinyGo wasn't compiled with the right Go version). But at least
it gives a hint in the right direction.

It's difficult to test for this specific case, so I've left out testing
in this case (boo!)
2024-10-18 17:43:17 +02:00
Ayke van Laethem 2690b243ea main: make sure typecheck errors are correctly reported 2024-10-18 17:43:17 +02:00
Ayke van Laethem df724f5827 loader: don't panic when main package is not named 'main'
This can in fact happen in practice, so return an actual error message
instead.
2024-10-18 17:43:17 +02:00
Elias Naur 539cc5c47b transform: optimize range over []byte(string)
Fixes #2700
2024-10-18 17:42:20 +02:00
Ayke van Laethem 45cc5b58cd runtime: disallow defer in interrupts
This often doesn't work because there might not be a current task to
push the defer frame to. It will instead show an unhelpful nil pointer
dereference panic.

We could make this work with a separate defer stack for interrupts (as
if they were newly started goroutines) but that is difficult with
multiple interrupts happening at the same time (we shouldn't jump to a
previous interrupt in `panic()`!). So instead, disable defer altogether
in interrupts and adjust panic/recover accordingly.
2024-10-18 14:50:56 +01:00
Ayke van Laethem c6acaa981d builder: remove environment variables when invoking Clang
This is a problem on Guix, which sets C_INCLUDE_PATH that is not
affected by `-nostdlibinc`. And therefore it affects the build in
unintended ways.

Removing all environmental variables fixes this issue, and perhaps also
other issues caused by Clang being affected by environment variables.
2024-10-18 13:42:02 +01:00
Ayke van Laethem 4ef5109a07 wasm: add //go:wasmexport support to js/wasm
This adds support for //go:wasmexport with `-target=wasm` (in the
browser). This follows the //go:wasmexport proposal, meaning that
blocking functions are not allowed.

Both `-buildmode=default` and `-buildmode=c-shared` are supported. The
latter allows calling exported functions after `go.run()` has returned.
2024-10-18 10:07:21 +01:00
Ayke van Laethem 6016d0c739 main_test: refactor output comparison into separate function
This shouldn't affect anything, just make the code a bit better
(especially for the next commit).
2024-10-18 10:07:21 +01:00
Elias Naur 07d23c9d83 runtime: implement newcoro, coroswitch to support package iter 2024-10-18 10:44:20 +02:00
Randy Reddig d5f195387d internal/{cm,wasi}: regenerate WASI 0.2 bindings with wasm-tools-go v0.3.0 2024-10-17 17:30:33 +01:00
Randy Reddig a0d4ecb607 internal/wasm-tools: update wasm-tools-go to v0.3.0 2024-10-17 17:30:33 +01:00
Ayke van Laethem 505e68057d nix: use LLVM 18 instead of LLVM 17
This should fix some CI issues we're currently having.

Thanks to @sylv-io for discovering that we need to remove LLVM to avoid
an Xtensa backend linker error.
2024-10-17 14:21:58 +01:00
Damian Gryski 87c6e19921 runtime: add HeapAlloc to gc_leaking 2024-10-09 07:01:09 -07:00
deadprogram 62c1555aa8 targets: add bulk memory flags to wasm-unknown target since basically every runtime has it now
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-09 08:18:58 +01:00
Elias Naur e62fc43b05 crypto/x509/internal/macos: add package stub to build crypto/x509 on macOS 2024-10-08 08:54:35 +01:00
Damian Gryski 158be02ef7 builder: fix sizes 2024-10-07 19:23:49 -07:00
Damian Gryski fbb125131d runtime: track Memstats.HeapAlloc for gc_blocks 2024-10-07 19:23:49 -07:00
BCG c77ed8e50e Added mstats fields 2024-10-07 19:23:49 -07:00
Ayke d1b7238a36 Truly ignore //export when //go:wasmexport is used (#4500) 2024-10-05 18:53:38 +02:00
Randy Reddig 453a1d35c3 compiler, runtime: enable go:wasmexport for wasip2 (#4499)
* compiler: prefer go:wasmexport over go:export

* runtime, targets/wasip2: enable -buildmode=c-shared for wasip2

* runtime: rename import from wasi_run to wasiclirun (PR feedback)
2024-10-04 17:36:47 -07:00
Ayke 9da8b5c786 wasm: add //go:wasmexport support (#4451)
This adds support for the `//go:wasmexport` pragma as proposed here:
https://github.com/golang/go/issues/65199

It is currently implemented only for wasip1 and wasm-unknown, but it is
certainly possible to extend it to other targets like GOOS=js and
wasip2.
2024-10-04 15:33:47 -07:00
Damian Gryski 407889864f GNUmakefile: more stdlib packages 2024-10-04 08:09:10 +01:00
Damian Gryski e17daf165d GNUmakefile: add some more passing stdlib tests (#4492) 2024-10-03 16:03:08 -07:00
Ayke van Laethem 666d2bd501 builder: keep wasm temporary files
Don't rename them when -work is set, instead update result.Binary each
time and leave result.Executable be the linker output (as intended:
result.Executable should be the unmodified linker output).
2024-10-03 19:17:12 +01:00
Damian Gryski 9d14489547 TestWebAssembly: use wasm-unknown for panic=trap test 2024-10-02 12:40:07 +02:00
Damian Gryski f3dfe1d49e runtime: seed fastrand() with hardware randomness 2024-10-02 12:40:07 +02:00
Damian Gryski b3e1974c30 runtime: add maps.clone
Fixes #4382
2024-10-02 12:40:07 +02:00
Damian Gryski fa12450552 runtime: fix building with -tags=runtime_memhash_tsip 2024-10-02 12:40:07 +02:00
Damian Gryski b3c040e9f7 runtime: make map iteration less defined
Fixes #3726
2024-10-02 12:40:07 +02:00
leongross 52788e5826 main: rework usage (#4467)
main: rework usage

* remove unused switch case statement and improve/add command specific usage.
* limit help to 80 col width
* remove inconsistent ':'
* remove trailing spaces
* rmove spaces woth tabs
* reworkd build command

Signed-off-by: leongross <leon.gross@9elements.com>
2024-09-28 21:33:18 +02:00
leongross bcd4c6b658 builder: nits
remove unused job state enum, nits/reformatting

Signed-off-by: leongross <leon.gross@9elements.com>
2024-09-24 17:42:30 +01:00
Ayke van Laethem 37460ad60a compiler: support pragmas on generic functions 2024-09-23 09:41:44 +01:00
Ayke van Laethem dcca47f1f6 main: add -ldflags='-extldflags=...' support
This matches upstream Go. Example:

    $ go test -ldflags='-extldflags=-foobar' os
    # os.test
    /usr/local/go1.23.1/pkg/tool/linux_arm64/link: running gcc failed: exit status 1
    /usr/bin/gcc -s -o $WORK/b001/os.test -rdynamic /tmp/go-link-914594215/go.o /tmp/go-link-914594215/000000.o /tmp/go-link-914594215/000001.o /tmp/go-link-914594215/000002.o /tmp/go-link-914594215/000003.o /tmp/go-link-914594215/000004.o /tmp/go-link-914594215/000005.o /tmp/go-link-914594215/000006.o /tmp/go-link-914594215/000007.o /tmp/go-link-914594215/000008.o /tmp/go-link-914594215/000009.o /tmp/go-link-914594215/000010.o /tmp/go-link-914594215/000011.o /tmp/go-link-914594215/000012.o /tmp/go-link-914594215/000013.o /tmp/go-link-914594215/000014.o /tmp/go-link-914594215/000015.o /tmp/go-link-914594215/000016.o /tmp/go-link-914594215/000017.o /tmp/go-link-914594215/000018.o /tmp/go-link-914594215/000019.o /tmp/go-link-914594215/000020.o /tmp/go-link-914594215/000021.o -O2 -g -O2 -g -lresolv -O2 -g -lpthread -foobar
    gcc: error: unrecognized command-line option ‘-foobar’

    FAIL    os [build failed]
    FAIL

And TinyGo, with this patch:

    $ tinygo test -ldflags='-extldflags=-foobar' os
    FAIL    os      0.000s
    ld.lld: error: unknown argument '-foobar'

Also note that Go doesn't support the `-extldflags` directly (which was
previously the case with TinyGo):

    $ go test -extldflags='-foobar' os
    flag provided but not defined: -extldflags
    [...]
2024-09-18 12:43:05 +02:00
Damian Gryski 892efaec97 support -extldflags
Fixes #4320
2024-09-18 08:05:25 +02:00
leongross 84048f299f os/File: add stubs for os.File Deadlines (#4465)
os/File: add stubs for os.File Deadlines
* add os.SetDeadline, os.SetReadDeadline, os.SetWriteDeadline stubs for
posix files.
* deadline: add tests

Signed-off-by: leongross <leon.gross@9elements.com>
2024-09-17 17:26:22 +02:00
leongross a9bf981d92 Cgo add cbytes implementation (rebased version of #3318) (#4470)
cgo: added CBytes implementation
2024-09-17 16:12:57 +02:00
Randy Reddig d4729f92bd targets/wasip2: add wasmtime -S args to support network interfaces 2024-09-17 11:24:13 +02:00
Randy Reddig 5a014dd6a3 tinygo: add relative and absolute --dir options to wasmtime args (#4431)
main: add relative and absolute --dir options to wasmtime args
2024-09-17 09:24:23 +02:00
Ron Evans d948941d82 governance: add initial documentation for project governance (#4457)
governance: add initial attempt to document project governance

Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-09-13 11:24:10 +02:00
archie2x 5abf1e998d Fix #4421: Add -C DIR flag (#4422)
feature: Fix #4421: Add `-C DIR` flag

Signed-off-by: Roger Standridge <9526806+archie2x@users.noreply.github.com>
2024-09-13 07:05:44 +02:00
217 changed files with 7780 additions and 1638 deletions
+2
View File
@@ -90,6 +90,8 @@ commands:
name: Check Go code formatting
command: make fmt-check lint
- run: make gen-device -j4
# TODO: change this to -skip='TestErrors|TestWasm' with Go 1.20
- run: go test -tags=llvm<<parameters.llvm>> -short -run='TestBuild|TestTest|TestGetList|TestTraceback'
- run: make smoketest XTENSA=0
- save_cache:
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
open_collective: tinygo
+13 -11
View File
@@ -16,24 +16,28 @@ jobs:
name: build-macos
strategy:
matrix:
# macos-12: amd64 (oldest supported version as of 05-02-2024)
# macos-13: amd64 (oldest supported version as of 18-10-2024)
# macos-14: arm64 (oldest arm64 version)
os: [macos-12, macos-14]
os: [macos-13, macos-14]
include:
- os: macos-12
- os: macos-13
goarch: amd64
- os: macos-14
goarch: arm64
runs-on: ${{ matrix.os }}
steps:
- name: exit early
run: command-does-not-exist
- name: Install Dependencies
shell: bash
run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Extract TinyGo version
id: version
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
- name: Install Go
uses: actions/setup-go@v5
with:
@@ -72,7 +76,6 @@ jobs:
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
shell: bash
run: |
# fetch LLVM source
rm -rf llvm-project
@@ -100,15 +103,13 @@ jobs:
- name: make gen-device
run: make -j3 gen-device
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-short"
- name: Build TinyGo release tarball
run: make release -j3
- name: Test stdlib packages
run: make tinygo-test
- name: Make release artifact
shell: bash
run: cp -p build/release.tar.gz build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
- name: Publish release artifact
# Note: this release artifact is double-zipped, see:
# https://github.com/actions/upload-artifact/issues/39
@@ -118,10 +119,9 @@ jobs:
# We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v4
with:
name: darwin-${{ matrix.goarch }}-double-zipped
path: build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
path: build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz
- name: Smoke tests
shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo
test-macos-homebrew:
name: homebrew-install
@@ -130,6 +130,8 @@ jobs:
matrix:
version: [16, 17, 18]
steps:
- name: exit early
run: command-does-not-exist
- name: Set up Homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Fix Python symlinks
+26 -14
View File
@@ -19,7 +19,11 @@ jobs:
runs-on: ubuntu-latest
container:
image: golang:1.23-alpine
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: exit early
run: command-does-not-exist
- name: Install apk dependencies
# tar: needed for actions/cache@v4
# git+openssh: needed for checkout (I think?)
@@ -32,6 +36,9 @@ jobs:
uses: actions/checkout@v4
with:
submodules: true
- name: Extract TinyGo version
id: version
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
- name: Cache Go
uses: actions/cache@v4
with:
@@ -120,15 +127,15 @@ jobs:
- name: Build TinyGo release
run: |
make release deb -j3 STATIC=1
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_amd64.deb
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
- name: Publish release artifact
uses: actions/upload-artifact@v4
with:
name: linux-amd64-double-zipped
name: linux-amd64-double-zipped-${{ steps.version.outputs.version }}
path: |
/tmp/tinygo.linux-amd64.tar.gz
/tmp/tinygo_amd64.deb
/tmp/tinygo${{ steps.version.outputs.version }}.linux-amd64.tar.gz
/tmp/tinygo_${{ steps.version.outputs.version }}_amd64.deb
test-linux-build:
# Test the binaries built in the build-linux job by running the smoke tests.
runs-on: ubuntu-latest
@@ -152,11 +159,11 @@ jobs:
- name: Download release artifact
uses: actions/download-artifact@v4
with:
name: linux-amd64-double-zipped
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
- name: Extract release tarball
run: |
mkdir -p ~/lib
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
tar -C ~/lib -xf tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
- run: make tinygo-test-wasip1-fast
- run: make tinygo-test-wasip2-fast
@@ -166,6 +173,8 @@ jobs:
# potential bugs.
runs-on: ubuntu-latest
steps:
- name: exit early
run: command-does-not-exist
- name: Checkout
uses: actions/checkout@v4
with:
@@ -297,6 +306,9 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get TinyGo version
id: version
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
- name: Install apt dependencies
run: |
sudo apt-get update
@@ -381,11 +393,11 @@ jobs:
- name: Download amd64 release
uses: actions/download-artifact@v4
with:
name: linux-amd64-double-zipped
name: linux-amd64-double-zipped-${{ needs.build-linux.outputs.version }}
- name: Extract amd64 release
run: |
mkdir -p build/release
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
tar -xf tinygo${{ needs.build-linux.outputs.version }}.linux-amd64.tar.gz -C build/release tinygo
- name: Modify release
run: |
cp -p build/tinygo build/release/tinygo/bin
@@ -393,12 +405,12 @@ jobs:
- name: Create ${{ matrix.goarch }} release
run: |
make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
cp -p build/release.tar.gz /tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
cp -p build/release.deb /tmp/tinygo_${{ matrix.libc }}.deb
cp -p build/release.tar.gz /tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
cp -p build/release.deb /tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
- name: Publish release artifact
uses: actions/upload-artifact@v4
with:
name: linux-${{ matrix.goarch }}-double-zipped
name: linux-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }}
path: |
/tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
/tmp/tinygo_${{ matrix.libc }}.deb
/tmp/tinygo${{ steps.version.outputs.version }}.linux-${{ matrix.goarch }}.tar.gz
/tmp/tinygo_${{ steps.version.outputs.version }}_${{ matrix.libc }}.deb
+7
View File
@@ -15,6 +15,13 @@ jobs:
nix-test:
runs-on: ubuntu-latest
steps:
- name: exit early
run: command-does-not-exist
- name: Uninstall system LLVM
# Hack to work around issue where we still include system headers for
# some reason.
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
run: sudo apt-get remove llvm-18
- name: Checkout
uses: actions/checkout@v4
- name: Pull musl
+2
View File
@@ -15,6 +15,8 @@ jobs:
permissions:
pull-requests: write
steps:
- name: exit early
run: command-does-not-exist
# Prepare, install tools
- name: Add GOBIN to $PATH
run: |
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
# Extract the version string from the source code, to be stored in a variable.
grep 'const version' goenv/version.go | sed 's/^const version = "\(.*\)"$/version=\1/g'
+52 -26
View File
@@ -14,6 +14,8 @@ concurrency:
jobs:
build-windows:
runs-on: windows-2022
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.4
@@ -21,22 +23,30 @@ jobs:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
- uses: brechtm/setup-scoop@v2
with:
scoop_update: 'false'
- name: Install Dependencies
shell: bash
run: |
scoop install ninja binaryen
#- uses: brechtm/setup-scoop@v2
# with:
# scoop_update: 'false'
#- name: Install Dependencies
# shell: bash
# run: |
# scoop install ninja binaryen
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
cache: true
- name: submodules
shell: bash
run: git submodule update --init lib/mingw-w64
- name: Extract TinyGo version
id: version
shell: bash
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
- name: command
shell: bash
run: go env
#- name: Install Go
# uses: actions/setup-go@v5
# with:
# go-version: '1.23'
# cache: true
- name: Restore cached LLVM source
uses: actions/cache/restore@v4
id: cache-llvm-source
@@ -85,6 +95,25 @@ jobs:
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Restore Go cache
uses: actions/cache/restore@v4
with:
key: go-cache-v2
path: |
C:/Users/runneradmin/AppData/Local/go-build
C:/Users/runneradmin/go/pkg/mod
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-short -run=TestBuild -v"
- name: Save Go cache
uses: actions/cache/save@v4
with:
key: go-cache-v2
path: |
C:/Users/runneradmin/AppData/Local/go-build
C:/Users/runneradmin/go/pkg/mod
- name: exit
run: command-does-not-exist
- name: Cache wasi-libc sysroot
uses: actions/cache@v4
id: cache-wasi-libc
@@ -99,16 +128,13 @@ jobs:
scoop install wasmtime@14.0.4
- name: make gen-device
run: make -j3 gen-device
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-short"
- name: Build TinyGo release tarball
shell: bash
run: make build/release -j4
- name: Make release artifact
shell: bash
working-directory: build/release
run: 7z -tzip a release.zip tinygo
run: 7z -tzip a tinygo${{ steps.version.outputs.version }}.windows-amd64.zip tinygo
- name: Publish release artifact
# Note: this release artifact is double-zipped, see:
# https://github.com/actions/upload-artifact/issues/39
@@ -118,8 +144,8 @@ jobs:
# We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v4
with:
name: windows-amd64-double-zipped
path: build/release/release.zip
name: windows-amd64-double-zipped-${{ steps.version.outputs.version }}
path: build/release/tinygo${{ steps.version.outputs.version }}.windows-amd64.zip
smoke-test-windows:
runs-on: windows-2022
@@ -148,12 +174,12 @@ jobs:
- name: Download TinyGo build
uses: actions/download-artifact@v4
with:
name: windows-amd64-double-zipped
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
path: build/
- name: Unzip TinyGo build
shell: bash
working-directory: build
run: 7z x release.zip -r
run: 7z x tinygo*.windows-amd64.zip -r
- name: Smoke tests
shell: bash
run: make smoketest TINYGO=$(PWD)/build/tinygo/bin/tinygo
@@ -178,12 +204,12 @@ jobs:
- name: Download TinyGo build
uses: actions/download-artifact@v4
with:
name: windows-amd64-double-zipped
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
path: build/
- name: Unzip TinyGo build
shell: bash
working-directory: build
run: 7z x release.zip -r
run: 7z x tinygo*.windows-amd64.zip -r
- name: Test stdlib packages
run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo
@@ -214,11 +240,11 @@ jobs:
- name: Download TinyGo build
uses: actions/download-artifact@v4
with:
name: windows-amd64-double-zipped
name: windows-amd64-double-zipped-${{ needs.build-windows.outputs.version }}
path: build/
- name: Unzip TinyGo build
shell: bash
working-directory: build
run: 7z x release.zip -r
run: 7z x tinygo*.windows-amd64.zip -r
- name: Test stdlib packages on wasip1
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
+50
View File
@@ -1,3 +1,53 @@
0.34.0
---
* **general**
- fix `GOOS=wasip1` for `tinygo test`
- add `-C DIR` flag
- add initial documentation for project governance
- add `-ldflags='-extldflags=...'` support
- improve usage message with `tinygo help` and when passing invalid parameters
* **compiler**
- `builder`: remove environment variables when invoking Clang, to avoid the environment changing the behavior
- `builder`: check for the Go toolchain version used to compile TinyGo
- `cgo`: add `C.CBytes` implementation
- `compiler`: fix passing weirdly-padded structs as parameters to new goroutines
- `compiler`: support pragmas on generic functions
- `compiler`: do not let the slice buffer escape when casting a `[]byte` or `[]rune` to a string, to help escape analysis
- `compiler`: conform to the latest iteration of the wasm types proposal
- `loader`: don't panic when main package is not named 'main'
- `loader`: make sure we always return type checker errors even without type errors
- `transform`: optimize range over `[]byte(string)`
* **standard library**
- `crypto/x509`: add package stub to build crypto/x509 on macOS
- `machine/usb/adc/midi`: fix `PitchBend`
- `os`: add `Truncate` stub for baremetal
- `os`: add stubs for `os.File` deadlines
- `os`: add internal `net.newUnixFile` for the net package
- `runtime`: stub runtime_{Before,After}Exec for linkage
- `runtime`: randomize map accesses
- `runtime`: support `maps.Clone`
- `runtime`: add more fields to `MemStats`
- `runtime`: implement newcoro, coroswitch to support package iter
- `runtime`: disallow defer in interrupts
- `runtime`: add support for os/signal on Linux and MacOS
- `runtime`: add gc layout info for some basic types to help the precise GC
- `runtime`: bump GC mark stack size to avoid excessive heap rescans
* **targets**
- `darwin`: use Go standard library syscall package instead of a custom one
- `fe310`: support GPIO `PinInput`
- `mips`: fix compiler crash with GOMIPS=softfloat and defer
- `mips`: add big-endian (GOARCH=mips) support
- `mips`: use MIPS32 (instead of MIPS32R2) as the instruction set for wider compatibility
- `wasi`: add relative and absolute --dir options to wasmtime args
- `wasip2`: add wasmtime -S args to support network interfaces
- `wasm`: add `//go:wasmexport` support (for all WebAssembly targets)
- `wasm`: use precise instead of conservative GC for WebAssembly (including WASI)
- `wasm-unknown`: add bulk memory flags since basically every runtime has it now
* **boards**
- add RAKwireless RAK4631
- add WaveShare ESP-C3-32S-Kit
0.33.0
---
+45 -6
View File
@@ -191,7 +191,7 @@ gen-device: gen-device-stm32
endif
gen-device-avr:
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
#@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
@@ -263,7 +263,7 @@ endif
.PHONY: wasi-libc
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
#@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
cd lib/wasi-libc && $(MAKE) -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC="$(CLANG)" AR=$(LLVM_AR) NM=$(LLVM_NM)
# Generate WASI syscall bindings
@@ -291,9 +291,9 @@ endif
tinygo: ## Build the TinyGo compiler
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc check-nodejs-version
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" $(GOTESTPKGS)
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" .
test: #wasi-libc check-nodejs-version
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -race -buildmode exe -tags "byollvm osusergo" .
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
TEST_PACKAGES_SLOW = \
@@ -303,26 +303,34 @@ TEST_PACKAGES_SLOW = \
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows
TEST_PACKAGES_FAST = \
cmp \
compress/lzw \
compress/zlib \
container/heap \
container/list \
container/ring \
crypto/des \
crypto/ecdsa \
crypto/elliptic \
crypto/md5 \
crypto/rc4 \
crypto/sha1 \
crypto/sha256 \
crypto/sha512 \
database/sql/driver \
debug/macho \
embed/internal/embedtest \
encoding \
encoding/ascii85 \
encoding/asn1 \
encoding/base32 \
encoding/base64 \
encoding/csv \
encoding/hex \
go/ast \
go/format \
go/scanner \
go/version \
hash \
hash/adler32 \
hash/crc64 \
@@ -357,11 +365,17 @@ endif
# archive/zip requires os.ReadAt, which is not yet supported on windows
# bytes requires mmap
# compress/flate appears to hang on wasi
# crypto/aes fails on wasi, needs panic()/recover()
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
# image requires recover(), which is not yet supported on wasi
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
# mime: fail on wasi; neds panic()/recover()
# mime/multipart: needs wasip1 syscall.FDFLAG_NONBLOCK
# mime/quotedprintable requires syscall.Faccessat
# net/mail: needs wasip1 syscall.FDFLAG_NONBLOCK
# net/ntextproto: needs wasip1 syscall.FDFLAG_NONBLOCK
# regexp/syntax: fails on wasip1; needs panic()/recover()
# strconv requires recover() which is not yet supported on wasi
# text/tabwriter requires recover(), which is not yet supported on wasi
# text/template/parse requires recover(), which is not yet supported on wasi
@@ -371,14 +385,20 @@ endif
TEST_PACKAGES_LINUX := \
archive/zip \
compress/flate \
crypto/aes \
crypto/hmac \
debug/dwarf \
debug/plan9obj \
image \
io/ioutil \
mime \
mime/multipart \
mime/quotedprintable \
net \
net/mail \
net/textproto \
os/user \
regexp/syntax \
strconv \
text/tabwriter \
text/template/parse
@@ -483,8 +503,17 @@ tinygo-baremetal:
# regression test for #2666: e.g. encoding/hex must pass on baremetal
$(TINYGO) test -target cortex-m-qemu encoding/hex
.PHONY: testchdir
testchdir:
# test 'build' command with{,out} -C argument
$(TINYGO) build -C tests/testing/chdir chdir.go && rm tests/testing/chdir/chdir
$(TINYGO) build ./tests/testing/chdir/chdir.go && rm chdir
# test 'run' command with{,out} -C argument
EXPECT_DIR=$(PWD)/tests/testing/chdir $(TINYGO) run -C tests/testing/chdir chdir.go
EXPECT_DIR=$(PWD) $(TINYGO) run ./tests/testing/chdir/chdir.go
.PHONY: smoketest
smoketest:
smoketest: testchdir
$(TINYGO) version
$(TINYGO) targets > /dev/null
# regression test for #2892
@@ -806,12 +835,20 @@ ifneq ($(XTENSA), 0)
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
@$(MD5SUM) test.bin
endif
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32c3 examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32-c3-devkit-rust-1 examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=makerfabs-esp32c3spi35 examples/machinetest
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
@@ -889,6 +926,7 @@ endif
@cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/fcntl build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src
@@ -904,6 +942,7 @@ endif
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/process build/release/tinygo/lib/musl/src
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
+32
View File
@@ -0,0 +1,32 @@
TinyGo Team Members
===================
The team of humans who maintain TinyGo.
* **Purpose**: To maintain the community, code, documentation, and tools for the TinyGo compiler.
* **Board**: The group of people who share responsibility for key decisions for the TinyGo organization.
* **Majority Voting**: The board makes decisions by majority vote.
* **Membership**: The board elects its own members.
* **Do-ocracy**: Those who step forward to do a given task propose how it should be done. Then other interested people can make comments.
* **Proof of Work**: Power in decision-making is slightly weighted based on a participant's labor for the community.
* **Initiation**: We need to establish a procedure for how people join the team of maintainers.
* **Transparency**: Important information should be made publicly available, ideally in a way that allows for public comment.
* **Code of Conduct**: Participants agree to abide by the current project Code of Conduct.
## Members
* Ayke van Laethem (@aykevl)
* Daniel Esteban (@conejoninja)
* Ron Evans (@deadprogram)
* Damian Gryski (@dgryski)
* Masaaki Takasago (@sago35)
* Patricio Whittingslow (@soypat)
* Yurii Soldak (@ysoldak)
## Experimental
* **Monthly Meeting**: A monthly meeting for the team and any other interested participants.
Duration: 1 hour
Facilitation: @deadprogram
Schedule: See https://github.com/tinygo-org/tinygo/wiki/Meetings for more information
+22 -16
View File
@@ -197,6 +197,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
ABI: config.ABI(),
GOOS: config.GOOS(),
GOARCH: config.GOARCH(),
BuildMode: config.BuildMode(),
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
SizeLevel: sizeLevel,
@@ -649,6 +650,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
result.Binary = result.Executable // final file
ldflags := append(config.LDFlags(), "-o", result.Executable)
if config.Options.BuildMode == "c-shared" {
if !strings.HasPrefix(config.Triple(), "wasm32-") {
return result, fmt.Errorf("buildmode c-shared is only supported on wasm at the moment")
}
ldflags = append(ldflags, "--no-entry")
}
// Add compiler-rt dependency if needed. Usually this is a simple load from
// a cache.
if config.Target.RTLib == "compiler-rt" {
@@ -823,19 +831,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
args = append(args, "--asyncify")
}
exeunopt := result.Executable
if config.Options.Work {
// Keep the work direction around => don't overwrite the .wasm binary with the optimized version
exeunopt += ".pre-wasm-opt"
os.Rename(result.Executable, exeunopt)
}
inputFile := result.Binary
result.Binary = result.Executable + ".wasmopt"
args = append(args,
opt,
"-g",
exeunopt,
"--output", result.Executable,
inputFile,
"--output", result.Binary,
)
wasmopt := goenv.Get("WASMOPT")
@@ -865,13 +867,15 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// wasm-tools component embed -w wasi:cli/command
// $$(tinygo env TINYGOROOT)/lib/wasi-cli/wit/ main.wasm -o embedded.wasm
componentEmbedInputFile := result.Binary
result.Binary = result.Executable + ".wasm-component-embed"
args := []string{
"component",
"embed",
"-w", witWorld,
witPackage,
result.Executable,
"-o", result.Executable,
componentEmbedInputFile,
"-o", result.Binary,
}
wasmtools := goenv.Get("WASMTOOLS")
@@ -884,15 +888,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
err := cmd.Run()
if err != nil {
return fmt.Errorf("wasm-tools failed: %w", err)
return fmt.Errorf("`wasm-tools component embed` failed: %w", err)
}
// wasm-tools component new embedded.wasm -o component.wasm
componentNewInputFile := result.Binary
result.Binary = result.Executable + ".wasm-component-new"
args = []string{
"component",
"new",
result.Executable,
"-o", result.Executable,
componentNewInputFile,
"-o", result.Binary,
}
if config.Options.PrintCommands != nil {
@@ -904,7 +910,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
err = cmd.Run()
if err != nil {
return fmt.Errorf("wasm-tools failed: %w", err)
return fmt.Errorf("`wasm-tools component new` failed: %w", err)
}
}
-12
View File
@@ -3,7 +3,6 @@ package builder
import (
"errors"
"fmt"
"os"
"os/exec"
"runtime"
"strings"
@@ -76,14 +75,3 @@ func LookupCommand(name string) (string, error) {
}
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
}
func execCommand(name string, args ...string) error {
name, err := LookupCommand(name)
if err != nil {
return err
}
cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
+22 -4
View File
@@ -2,6 +2,7 @@ package builder
import (
"fmt"
"runtime"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
@@ -23,20 +24,37 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
spec.OpenOCDCommands = options.OpenOCDCommands
}
major, minor, err := goenv.GetGorootVersion()
// Version range supported by TinyGo.
const minorMin = 19
const minorMax = 23
// Check that we support this Go toolchain version.
gorootMajor, gorootMinor, err := goenv.GetGorootVersion()
if err != nil {
return nil, err
}
if major != 1 || minor < 19 || minor > 23 {
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
// Note: when this gets updated, also update the Go compatibility matrix:
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
return nil, fmt.Errorf("requires go version 1.19 through 1.23, got go%d.%d", major, minor)
return nil, fmt.Errorf("requires go version 1.19 through 1.23, got go%d.%d", gorootMajor, gorootMinor)
}
// Check that the Go toolchain version isn't too new, if we haven't been
// compiled with the latest Go version.
// This may be a bit too aggressive: if the newer version doesn't change the
// Go language we will most likely be able to compile it.
buildMajor, buildMinor, _, err := goenv.Parse(runtime.Version())
if err != nil {
return nil, err
}
if buildMajor != 1 || buildMinor < gorootMinor {
return nil, fmt.Errorf("cannot compile with Go toolchain version go%d.%d (TinyGo was built using toolchain version %s)", gorootMajor, gorootMinor, runtime.Version())
}
return &compileopts.Config{
Options: options,
Target: spec,
GoMinorVersion: minor,
GoMinorVersion: gorootMinor,
TestConfig: options.TestConfig,
}, nil
}
+6 -16
View File
@@ -17,14 +17,6 @@ import (
// concurrency or performance issues.
const jobRunnerDebug = false
type jobState uint8
const (
jobStateQueued jobState = iota // not yet running
jobStateRunning // running
jobStateFinished // finished running
)
// compileJob is a single compiler job, comparable to a single Makefile target.
// It is used to orchestrate various compiler tasks that can be run in parallel
// but that have dependencies and thus have limitations in how they can be run.
@@ -55,12 +47,11 @@ func dummyCompileJob(result string) *compileJob {
// ordered as such in the job dependencies.
func runJobs(job *compileJob, sema chan struct{}) error {
if sema == nil {
// Have a default, if the semaphore isn't set. This is useful for
// tests.
// Have a default, if the semaphore isn't set. This is useful for tests.
sema = make(chan struct{}, runtime.NumCPU())
}
if cap(sema) == 0 {
return errors.New("cannot 0 jobs at a time")
return errors.New("cannot run 0 jobs at a time")
}
// Create a slice of jobs to run, where all dependencies are run in order.
@@ -81,10 +72,10 @@ func runJobs(job *compileJob, sema chan struct{}) error {
waiting := make(map[*compileJob]map[*compileJob]struct{}, len(jobs))
dependents := make(map[*compileJob][]*compileJob, len(jobs))
jidx := make(map[*compileJob]int)
compileJobs := make(map[*compileJob]int)
var ready intHeap
for i, job := range jobs {
jidx[job] = i
compileJobs[job] = i
if len(job.dependencies) == 0 {
// This job is ready to run.
ready.Push(i)
@@ -105,8 +96,7 @@ func runJobs(job *compileJob, sema chan struct{}) error {
// Create a channel to accept notifications of completion.
doneChan := make(chan *compileJob)
// Send each job in the jobs slice to a worker, taking care of job
// dependencies.
// Send each job in the jobs slice to a worker, taking care of job dependencies.
numRunningJobs := 0
var totalTime time.Duration
start := time.Now()
@@ -156,7 +146,7 @@ func runJobs(job *compileJob, sema chan struct{}) error {
delete(wait, completed)
if len(wait) == 0 {
// This job is now ready to run.
ready.Push(jidx[j])
ready.Push(compileJobs[j])
delete(waiting, j)
}
}
+10
View File
@@ -116,6 +116,7 @@ var libMusl = Library{
"env/*.c",
"errno/*.c",
"exit/*.c",
"fcntl/*.c",
"internal/defsysinfo.c",
"internal/libc.c",
"internal/syscall_ret.c",
@@ -128,6 +129,7 @@ var libMusl = Library{
"mman/*.c",
"math/*.c",
"multibyte/*.c",
"signal/" + arch + "/*.s",
"signal/*.c",
"stdio/*.c",
"string/*.c",
@@ -135,12 +137,20 @@ var libMusl = Library{
"thread/*.c",
"time/*.c",
"unistd/*.c",
"process/*.c",
}
if arch == "arm" {
// These files need to be added to the start for some reason.
globs = append([]string{"thread/arm/*.c"}, globs...)
}
if arch != "aarch64" && arch != "mips" {
//aarch64 and mips have no architecture specific code, either they
// are not supported or don't need any?
globs = append([]string{"process/" + arch + "/*.s"}, globs...)
}
var sources []string
seenSources := map[string]struct{}{}
basepath := goenv.Get("TINYGOROOT") + "/lib/musl/src/"
+3 -3
View File
@@ -41,9 +41,9 @@ func TestBinarySize(t *testing.T) {
// This is a small number of very diverse targets that we want to test.
tests := []sizeTest{
// microcontrollers
{"hifive1b", "examples/echo", 4484, 280, 0, 2252},
{"microbit", "examples/serial", 2732, 388, 8, 2256},
{"wioterminal", "examples/pininterrupt", 6016, 1484, 116, 6816},
{"hifive1b", "examples/echo", 4600, 280, 0, 2268},
{"microbit", "examples/serial", 2908, 388, 8, 2272},
{"wioterminal", "examples/pininterrupt", 6140, 1484, 116, 6832},
// TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the
+35 -6
View File
@@ -14,16 +14,45 @@ import (
// runCCompiler invokes a C compiler with the given arguments.
func runCCompiler(flags ...string) error {
// Find the right command to run Clang.
var cmd *exec.Cmd
if hasBuiltinTools {
// Compile this with the internal Clang compiler.
cmd := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
cmd = exec.Command(os.Args[0], append([]string{"clang"}, flags...)...)
} else {
// Compile this with an external invocation of the Clang compiler.
name, err := LookupCommand("clang")
if err != nil {
return err
}
cmd = exec.Command(name, flags...)
}
// Compile this with an external invocation of the Clang compiler.
return execCommand("clang", flags...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// Make sure the command doesn't use any environmental variables.
// Most importantly, it should not use C_INCLUDE_PATH and the like.
cmd.Env = []string{}
// Let some environment variables through. One important one is the
// temporary directory, especially on Windows it looks like Clang breaks if
// the temporary directory has not been set.
// See: https://github.com/tinygo-org/tinygo/issues/4557
// Also see: https://github.com/llvm/llvm-project/blob/release/18.x/llvm/lib/Support/Unix/Path.inc#L1435
for _, env := range os.Environ() {
// We could parse the key and look it up in a map, but since there are
// only a few keys iterating through them is easier and maybe even
// faster.
for _, prefix := range []string{"TMPDIR=", "TMP=", "TEMP=", "TEMPDIR="} {
if strings.HasPrefix(env, prefix) {
cmd.Env = append(cmd.Env, env)
break
}
}
}
return cmd.Run()
}
// link invokes a linker with the given name and flags.
+10 -3
View File
@@ -162,6 +162,13 @@ func __GoBytes(unsafe.Pointer, uintptr) []byte
func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname C.__CBytes runtime.cgo_CBytes
func __CBytes([]byte) unsafe.Pointer
func CBytes(b []byte) unsafe.Pointer {
return C.__CBytes(b)
}
`
// Process extracts `import "C"` statements from the AST, parses the comment
@@ -218,7 +225,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
switch decl := decl.(type) {
case *ast.FuncDecl:
switch decl.Name.Name {
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes":
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes", "CBytes", "__CBytes":
// Adjust the name to have a "C." prefix so it is correctly
// resolved.
decl.Name.Name = "C." + decl.Name.Name
@@ -1141,7 +1148,7 @@ func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) st
if alias := cgoAliases["C."+name]; alias != "" {
return alias
}
node := f.getASTDeclNode(name, found, iscall)
node := f.getASTDeclNode(name, found)
if node, ok := node.(*ast.FuncDecl); ok {
if !iscall {
return node.Name.Name + "$funcaddr"
@@ -1153,7 +1160,7 @@ func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) st
// getASTDeclNode will declare the given C AST node (if not already defined) and
// returns it.
func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) ast.Node {
func (f *cgoFile) getASTDeclNode(name string, found clangCursor) ast.Node {
if node, ok := f.defined[name]; ok {
// Declaration was found in the current file, so return it immediately.
return node
+136 -4
View File
@@ -54,8 +54,72 @@ func init() {
}
// parseConst parses the given string as a C constant.
func parseConst(pos token.Pos, fset *token.FileSet, value string) (ast.Expr, *scanner.Error) {
t := newTokenizer(pos, fset, value)
func parseConst(pos token.Pos, fset *token.FileSet, value string, params []ast.Expr, callerPos token.Pos, f *cgoFile) (ast.Expr, *scanner.Error) {
t := newTokenizer(pos, fset, value, f)
// If params is non-nil (could be a zero length slice), this const is
// actually a function-call like expression from another macro.
// This means we have to parse a string like "(a, b) (a+b)".
// We do this by parsing the parameters at the start and then treating the
// following like a normal constant expression.
if params != nil {
// Parse opening paren.
if t.curToken != token.LPAREN {
return nil, unexpectedToken(t, token.LPAREN)
}
t.Next()
// Parse parameters (identifiers) and closing paren.
var paramIdents []string
for i := 0; ; i++ {
if i == 0 && t.curToken == token.RPAREN {
// No parameters, break early.
t.Next()
break
}
// Read the parameter name.
if t.curToken != token.IDENT {
return nil, unexpectedToken(t, token.IDENT)
}
paramIdents = append(paramIdents, t.curValue)
t.Next()
// Read the next token: either a continuation (comma) or end of list
// (rparen).
if t.curToken == token.RPAREN {
// End of parameter list.
t.Next()
break
} else if t.curToken == token.COMMA {
// Comma, so there will be another parameter name.
t.Next()
} else {
return nil, &scanner.Error{
Pos: t.fset.Position(t.curPos),
Msg: "unexpected token " + t.curToken.String() + " inside macro parameters, expected ',' or ')'",
}
}
}
// Report an error if there is a mismatch in parameter length.
// The error is reported at the location of the closing paren from the
// caller location.
if len(params) != len(paramIdents) {
return nil, &scanner.Error{
Pos: t.fset.Position(callerPos),
Msg: fmt.Sprintf("unexpected number of parameters: expected %d, got %d", len(paramIdents), len(params)),
}
}
// Assign values to the parameters.
// These parameter names are closer in 'scope' than other identifiers so
// will be used first when parsing an identifier.
for i, name := range paramIdents {
t.params[name] = params[i]
}
}
expr, err := parseConstExpr(t, precedenceLowest)
t.Next()
if t.curToken != token.EOF {
@@ -96,6 +160,68 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) {
}
func parseIdent(t *tokenizer) (ast.Expr, *scanner.Error) {
// If the identifier is one of the parameters of this function-like macro,
// use the parameter value.
if val, ok := t.params[t.curValue]; ok {
return val, nil
}
if t.f != nil {
// Check whether this identifier is actually a macro "call" with
// parameters. In that case, we should parse the parameters and pass it
// on to a new invocation of parseConst.
if t.peekToken == token.LPAREN {
if cursor, ok := t.f.names[t.curValue]; ok && t.f.isFunctionLikeMacro(cursor) {
// We know the current and peek tokens (the peek one is the '('
// token). So skip ahead until the current token is the first
// unknown token.
t.Next()
t.Next()
// Parse the list of parameters until ')' (rparen) is found.
params := []ast.Expr{}
for i := 0; ; i++ {
if i == 0 && t.curToken == token.RPAREN {
break
}
x, err := parseConstExpr(t, precedenceLowest)
if err != nil {
return nil, err
}
params = append(params, x)
t.Next()
if t.curToken == token.COMMA {
t.Next()
} else if t.curToken == token.RPAREN {
break
} else {
return nil, &scanner.Error{
Pos: t.fset.Position(t.curPos),
Msg: "unexpected token " + t.curToken.String() + ", ',' or ')'",
}
}
}
// Evaluate the macro value and use it as the identifier value.
rparen := t.curPos
pos, text := t.f.getMacro(cursor)
return parseConst(pos, t.fset, text, params, rparen, t.f)
}
}
// Normally the name is something defined in the file (like another
// macro) which we get the declaration from using getASTDeclName.
// This ensures that names that are only referenced inside a macro are
// still getting defined.
if cursor, ok := t.f.names[t.curValue]; ok {
return &ast.Ident{
NamePos: t.curPos,
Name: t.f.getASTDeclName(t.curValue, cursor, false),
}, nil
}
}
// t.f is nil during testing. This is a fallback.
return &ast.Ident{
NamePos: t.curPos,
Name: "C." + t.curValue,
@@ -164,21 +290,25 @@ func unexpectedToken(t *tokenizer, expected token.Token) *scanner.Error {
// tokenizer reads C source code and converts it to Go tokens.
type tokenizer struct {
f *cgoFile
curPos, peekPos token.Pos
fset *token.FileSet
curToken, peekToken token.Token
curValue, peekValue string
buf string
params map[string]ast.Expr
}
// newTokenizer initializes a new tokenizer, positioned at the first token in
// the string.
func newTokenizer(start token.Pos, fset *token.FileSet, buf string) *tokenizer {
func newTokenizer(start token.Pos, fset *token.FileSet, buf string, f *cgoFile) *tokenizer {
t := &tokenizer{
f: f,
peekPos: start,
fset: fset,
buf: buf,
peekToken: token.ILLEGAL,
params: make(map[string]ast.Expr),
}
// Parse the first two tokens (cur and peek).
t.Next()
@@ -230,7 +360,7 @@ func (t *tokenizer) Next() {
t.peekValue = t.buf[:2]
t.buf = t.buf[2:]
return
case c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^':
case c == '(' || c == ')' || c == ',' || c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '&' || c == '|' || c == '^':
// Single-character tokens.
// TODO: ++ (increment) and -- (decrement) operators.
switch c {
@@ -238,6 +368,8 @@ func (t *tokenizer) Next() {
t.peekToken = token.LPAREN
case ')':
t.peekToken = token.RPAREN
case ',':
t.peekToken = token.COMMA
case '+':
t.peekToken = token.ADD
case '-':
+1 -1
View File
@@ -59,7 +59,7 @@ func TestParseConst(t *testing.T) {
} {
fset := token.NewFileSet()
startPos := fset.AddFile("", -1, 1000).Pos(0)
expr, err := parseConst(startPos, fset, tc.C)
expr, err := parseConst(startPos, fset, tc.C, nil, token.NoPos, nil)
s := "<invalid>"
if err != nil {
if !strings.HasPrefix(tc.Go, "error: ") {
+59 -39
View File
@@ -63,6 +63,7 @@ long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c);
unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c);
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
unsigned tinygo_clang_Cursor_isMacroFunctionLike(GoCXCursor c);
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
@@ -370,45 +371,8 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
gen.Specs = append(gen.Specs, valueSpec)
return gen, nil
case C.CXCursor_MacroDefinition:
// Extract tokens from the Clang tokenizer.
// See: https://stackoverflow.com/a/19074846/559350
sourceRange := C.tinygo_clang_getCursorExtent(c)
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
var rawTokens *C.CXToken
var numTokens C.unsigned
C.clang_tokenize(tu, sourceRange, &rawTokens, &numTokens)
tokens := unsafe.Slice(rawTokens, numTokens)
// Convert this range of tokens back to source text.
// Ugly, but it works well enough.
sourceBuf := &bytes.Buffer{}
var startOffset int
for i, token := range tokens {
spelling := getString(C.clang_getTokenSpelling(tu, token))
location := C.clang_getTokenLocation(tu, token)
var tokenOffset C.unsigned
C.clang_getExpansionLocation(location, nil, nil, nil, &tokenOffset)
if i == 0 {
// The first token is the macro name itself.
// Skip it (after using its location).
startOffset = int(tokenOffset) + len(name)
} else {
// Later tokens are the macro contents.
for int(tokenOffset) > (startOffset + sourceBuf.Len()) {
// Pad the source text with whitespace (that must have been
// present in the original source as well).
sourceBuf.WriteByte(' ')
}
sourceBuf.WriteString(spelling)
}
}
C.clang_disposeTokens(tu, rawTokens, numTokens)
value := sourceBuf.String()
// Try to convert this #define into a Go constant expression.
tokenPos := token.NoPos
if pos != token.NoPos {
tokenPos = pos + token.Pos(len(name))
}
expr, scannerError := parseConst(tokenPos, f.fset, value)
tokenPos, value := f.getMacro(c)
expr, scannerError := parseConst(tokenPos, f.fset, value, nil, token.NoPos, f)
if scannerError != nil {
f.errors = append(f.errors, *scannerError)
return nil, nil
@@ -488,6 +452,62 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
}
}
// Return whether this is a macro that's also function-like, like this:
//
// #define add(a, b) (a+b)
func (f *cgoFile) isFunctionLikeMacro(c clangCursor) bool {
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_MacroDefinition {
return false
}
return C.tinygo_clang_Cursor_isMacroFunctionLike(c) != 0
}
// Get the macro value: the position in the source file and the string value of
// the macro.
func (f *cgoFile) getMacro(c clangCursor) (pos token.Pos, value string) {
// Extract tokens from the Clang tokenizer.
// See: https://stackoverflow.com/a/19074846/559350
sourceRange := C.tinygo_clang_getCursorExtent(c)
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
var rawTokens *C.CXToken
var numTokens C.unsigned
C.clang_tokenize(tu, sourceRange, &rawTokens, &numTokens)
tokens := unsafe.Slice(rawTokens, numTokens)
defer C.clang_disposeTokens(tu, rawTokens, numTokens)
// Convert this range of tokens back to source text.
// Ugly, but it works well enough.
sourceBuf := &bytes.Buffer{}
var startOffset int
for i, token := range tokens {
spelling := getString(C.clang_getTokenSpelling(tu, token))
location := C.clang_getTokenLocation(tu, token)
var tokenOffset C.unsigned
C.clang_getExpansionLocation(location, nil, nil, nil, &tokenOffset)
if i == 0 {
// The first token is the macro name itself.
// Skip it (after using its location).
startOffset = int(tokenOffset)
} else {
// Later tokens are the macro contents.
for int(tokenOffset) > (startOffset + sourceBuf.Len()) {
// Pad the source text with whitespace (that must have been
// present in the original source as well).
sourceBuf.WriteByte(' ')
}
sourceBuf.WriteString(spelling)
}
}
value = sourceBuf.String()
// Obtain the position of this token. This is the position of the first
// character in the 'value' string and is used to report errors at the
// correct location in the source file.
pos = f.getCursorPosition(c)
return
}
func getString(clangString C.CXString) (s string) {
rawString := C.clang_getCString(clangString)
s = C.GoString(rawString)
+4
View File
@@ -84,3 +84,7 @@ unsigned tinygo_clang_Cursor_isAnonymous(CXCursor c) {
unsigned tinygo_clang_Cursor_isBitField(CXCursor c) {
return clang_Cursor_isBitField(c);
}
unsigned tinygo_clang_Cursor_isMacroFunctionLike(CXCursor c) {
return clang_Cursor_isMacroFunctionLike(c);
}
+7
View File
@@ -24,6 +24,13 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname C.__CBytes runtime.cgo_CBytes
func C.__CBytes([]byte) unsafe.Pointer
func C.CBytes(b []byte) unsafe.Pointer {
return C.__CBytes(b)
}
type (
C.char uint8
C.schar int8
+16
View File
@@ -3,10 +3,26 @@ package main
/*
#define foo 3
#define bar foo
#define unreferenced 4
#define referenced unreferenced
#define fnlike() 5
#define fnlike_val fnlike()
#define square(n) (n*n)
#define square_val square(20)
#define add(a, b) (a + b)
#define add_val add(3, 5)
*/
import "C"
const (
Foo = C.foo
Bar = C.bar
Baz = C.referenced
fnlike = C.fnlike_val
square = C.square_val
add = C.add_val
)
+12
View File
@@ -24,6 +24,13 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname C.__CBytes runtime.cgo_CBytes
func C.__CBytes([]byte) unsafe.Pointer
func C.CBytes(b []byte) unsafe.Pointer {
return C.__CBytes(b)
}
type (
C.char uint8
C.schar int8
@@ -40,3 +47,8 @@ type (
const C.foo = 3
const C.bar = C.foo
const C.unreferenced = 4
const C.referenced = C.unreferenced
const C.fnlike_val = 5
const C.square_val = (20 * 20)
const C.add_val = (3 + 5)
+8
View File
@@ -26,6 +26,11 @@ import "C"
// #warning another warning
import "C"
// #define add(a, b) (a+b)
// #define add_toomuch add(1, 2, 3)
// #define add_toolittle add(1)
import "C"
// Make sure that errors for the following lines won't change with future
// additions to the CGo preamble.
//
@@ -51,4 +56,7 @@ var (
// constants passed by a command line parameter
_ = C.SOME_PARAM_CONST_invalid
_ = C.SOME_PARAM_CONST_valid
_ = C.add_toomuch
_ = C.add_toolittle
)
+11
View File
@@ -7,6 +7,8 @@
// testdata/errors.go:16:33: unexpected token ), expected end of expression
// testdata/errors.go:17:34: unexpected token ), expected end of expression
// -: unexpected token INT, expected end of expression
// testdata/errors.go:30:35: unexpected number of parameters: expected 2, got 3
// testdata/errors.go:31:31: unexpected number of parameters: expected 2, got 1
// Type checking errors after CGo processing:
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
@@ -17,6 +19,8 @@
// testdata/errors.go:114: undefined: C.SOME_CONST_b
// testdata/errors.go:116: undefined: C.SOME_CONST_startspace
// testdata/errors.go:119: undefined: C.SOME_PARAM_CONST_invalid
// testdata/errors.go:122: undefined: C.add_toomuch
// testdata/errors.go:123: undefined: C.add_toolittle
package main
@@ -44,6 +48,13 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname C.__CBytes runtime.cgo_CBytes
func C.__CBytes([]byte) unsafe.Pointer
func C.CBytes(b []byte) unsafe.Pointer {
return C.__CBytes(b)
}
type (
C.char uint8
C.schar int8
+7
View File
@@ -29,6 +29,13 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname C.__CBytes runtime.cgo_CBytes
func C.__CBytes([]byte) unsafe.Pointer
func C.CBytes(b []byte) unsafe.Pointer {
return C.__CBytes(b)
}
type (
C.char uint8
C.schar int8
+7
View File
@@ -24,6 +24,13 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname C.__CBytes runtime.cgo_CBytes
func C.__CBytes([]byte) unsafe.Pointer
func C.CBytes(b []byte) unsafe.Pointer {
return C.__CBytes(b)
}
type (
C.char uint8
C.schar int8
+7
View File
@@ -24,6 +24,13 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
//go:linkname C.__CBytes runtime.cgo_CBytes
func C.__CBytes([]byte) unsafe.Pointer
func C.CBytes(b []byte) unsafe.Pointer {
return C.__CBytes(b)
}
type (
C.char uint8
C.schar int8
+13
View File
@@ -33,6 +33,17 @@ func (c *Config) CPU() string {
return c.Target.CPU
}
// The current build mode (like the `-buildmode` command line flag).
func (c *Config) BuildMode() string {
if c.Options.BuildMode != "" {
return c.Options.BuildMode
}
if c.Target.BuildMode != "" {
return c.Target.BuildMode
}
return "default"
}
// Features returns a list of features this CPU supports. For example, for a
// RISC-V processor, that could be "+a,+c,+m". For many targets, an empty list
// will be returned.
@@ -395,6 +406,8 @@ func (c *Config) LDFlags() []string {
if c.Target.LinkerScript != "" {
ldflags = append(ldflags, "-T", c.Target.LinkerScript)
}
ldflags = append(ldflags, c.Options.ExtLDFlags...)
return ldflags
}
+11
View File
@@ -8,6 +8,7 @@ import (
)
var (
validBuildModeOptions = []string{"default", "c-shared"}
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
@@ -26,6 +27,7 @@ type Options struct {
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
Directory string // working dir, leave it unset to use the current working dir
Target string
BuildMode string // -buildmode flag
Opt string
GC string
PanicStrategy string
@@ -56,10 +58,19 @@ type Options struct {
Timeout time.Duration
WITPackage string // pass through to wasm-tools component embed invocation
WITWorld string // pass through to wasm-tools component embed -w option
ExtLDFlags []string
}
// Verify performs a validation on the given options, raising an error if options are not valid.
func (o *Options) Verify() error {
if o.BuildMode != "" {
valid := isInArray(validBuildModeOptions, o.BuildMode)
if !valid {
return fmt.Errorf(`invalid buildmode option '%s': valid values are %s`,
o.BuildMode,
strings.Join(validBuildModeOptions, ", "))
}
}
if o.GC != "" {
valid := isInArray(validGCOptions, o.GC)
if !valid {
+6 -3
View File
@@ -32,6 +32,7 @@ type TargetSpec struct {
GOARCH string `json:"goarch,omitempty"`
SoftFloat bool // used for non-baremetal systems (GOMIPS=softfloat etc)
BuildTags []string `json:"build-tags,omitempty"`
BuildMode string `json:"buildmode,omitempty"` // default build mode (if nothing specified)
GC string `json:"gc,omitempty"`
Scheduler string `json:"scheduler,omitempty"`
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
@@ -390,7 +391,8 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
)
spec.ExtraFiles = append(spec.ExtraFiles,
"src/runtime/os_darwin.c",
"src/runtime/runtime_unix.c")
"src/runtime/runtime_unix.c",
"src/runtime/signal.c")
case "linux":
spec.Linker = "ld.lld"
spec.RTLib = "compiler-rt"
@@ -411,7 +413,8 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
}
spec.ExtraFiles = append(spec.ExtraFiles,
"src/runtime/runtime_unix.c")
"src/runtime/runtime_unix.c",
"src/runtime/signal.c")
case "windows":
spec.Linker = "ld.lld"
spec.Libc = "mingw-w64"
@@ -449,7 +452,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
"--stack-first",
"--no-demangle",
)
spec.Emulator = "wasmtime --dir={tmpDir}::/tmp {}"
spec.Emulator = "wasmtime run --dir={tmpDir}::/tmp {}"
spec.ExtraFiles = append(spec.ExtraFiles,
"src/runtime/asm_tinygowasm.S",
"src/internal/task/task_asyncify_wasm.S",
+13 -3
View File
@@ -17,6 +17,7 @@ import (
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/loader"
"github.com/tinygo-org/tinygo/src/tinygo"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
"tinygo.org/x/go-llvm"
@@ -44,6 +45,7 @@ type Config struct {
ABI string
GOOS string
GOARCH string
BuildMode string
CodeModel string
RelocationModel string
SizeLevel int
@@ -1384,6 +1386,11 @@ func (b *builder) createFunction() {
b.llvmFn.SetLinkage(llvm.InternalLinkage)
b.createFunction()
}
// Create wrapper function that can be called externally.
if b.info.wasmExport != "" {
b.createWasmExport()
}
}
// posser is an interface that's implemented by both ssa.Value and
@@ -1674,6 +1681,10 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
result = b.CreateSelect(cmp, result, arg, "")
}
return result, nil
case "panic":
// This is rare, but happens in "defer panic()".
b.createRuntimeInvoke("_panic", argValues, "")
return llvm.Value{}, nil
case "print", "println":
for i, value := range argValues {
if i >= 1 && callName == "println" {
@@ -1859,10 +1870,9 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
}
return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil
case name == "runtime.panicStrategy":
// These constants are defined in src/runtime/panic.go.
panicStrategy := map[string]uint64{
"print": 1, // panicStrategyPrint
"trap": 2, // panicStrategyTrap
"print": tinygo.PanicStrategyPrint,
"trap": tinygo.PanicStrategyTrap,
}[b.Config.PanicStrategy]
return llvm.ConstInt(b.ctx.Int8Type(), panicStrategy, false), nil
case name == "runtime/interrupt.New":
+253 -20
View File
@@ -7,6 +7,7 @@ import (
"go/token"
"go/types"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
@@ -101,7 +102,7 @@ func (b *builder) createGo(instr *ssa.Go) {
paramBundle := b.emitPointerPack(params)
var stackSize llvm.Value
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, instr.Pos())
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, false, instr.Pos())
if b.AutomaticStackSize {
// The stack size is not known until after linking. Call a dummy
// function that will be replaced with a load from a special ELF
@@ -121,6 +122,147 @@ func (b *builder) createGo(instr *ssa.Go) {
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
}
// Create an exported wrapper function for functions with the //go:wasmexport
// pragma. This wrapper function is quite complex when the scheduler is enabled:
// it needs to start a new goroutine each time the exported function is called.
func (b *builder) createWasmExport() {
pos := b.info.wasmExportPos
if b.info.exported {
// //export really shouldn't be used anymore when //go:wasmexport is
// available, because //go:wasmexport is much better defined.
b.addError(pos, "cannot use //export and //go:wasmexport at the same time")
return
}
const suffix = "#wasmexport"
// Declare the exported function.
paramTypes := b.llvmFnType.ParamTypes()
exportedFnType := llvm.FunctionType(b.llvmFnType.ReturnType(), paramTypes[:len(paramTypes)-1], false)
exportedFn := llvm.AddFunction(b.mod, b.fn.RelString(nil)+suffix, exportedFnType)
b.addStandardAttributes(exportedFn)
llvmutil.AppendToGlobal(b.mod, "llvm.used", exportedFn)
exportedFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.wasmExport))
// Create a builder for this wrapper function.
builder := newBuilder(b.compilerContext, b.ctx.NewBuilder(), b.fn)
defer builder.Dispose()
// Define this function as a separate function in DWARF
if b.Debug {
if b.fn.Syntax() != nil {
// Create debug info file if needed.
pos := b.program.Fset.Position(pos)
builder.difunc = builder.attachDebugInfoRaw(b.fn, exportedFn, suffix, pos.Filename, pos.Line)
}
builder.setDebugLocation(pos)
}
// Create a single basic block inside of it.
bb := llvm.AddBasicBlock(exportedFn, "entry")
builder.SetInsertPointAtEnd(bb)
// Insert an assertion to make sure this //go:wasmexport function is not
// called at a time when it is not allowed (for example, before the runtime
// is initialized).
builder.createRuntimeCall("wasmExportCheckRun", nil, "")
if b.Scheduler == "none" {
// When the scheduler has been disabled, this is really trivial: just
// call the function.
params := exportedFn.Params()
params = append(params, llvm.ConstNull(b.dataPtrType)) // context parameter
retval := builder.CreateCall(b.llvmFnType, b.llvmFn, params, "")
if b.fn.Signature.Results() == nil {
builder.CreateRetVoid()
} else {
builder.CreateRet(retval)
}
} else {
// The scheduler is enabled, so we need to start a new goroutine, wait
// for it to complete, and read the result value.
// Build a function that looks like this:
//
// func foo#wasmexport(param0, param1, ..., paramN) {
// var state *stateStruct
//
// // 'done' must be explicitly initialized ('state' is not zeroed)
// state.done = false
//
// // store the parameters in the state object
// state.param0 = param0
// state.param1 = param1
// ...
// state.paramN = paramN
//
// // create a goroutine and push it to the runqueue
// task.start(uintptr(gowrapper), &state)
//
// // run the scheduler
// runtime.wasmExportRun(&state.done)
//
// // if there is a return value, load it and return
// return state.result
// }
hasReturn := b.fn.Signature.Results() != nil
// Build the state struct type.
// It stores the function parameters, the 'done' flag, and reserves
// space for a return value if needed.
stateFields := exportedFnType.ParamTypes()
numParams := len(stateFields)
stateFields = append(stateFields, b.ctx.Int1Type()) // 'done' field
if hasReturn {
stateFields = append(stateFields, b.llvmFnType.ReturnType())
}
stateStruct := b.ctx.StructType(stateFields, false)
// Allocate the state struct on the stack.
statePtr := builder.CreateAlloca(stateStruct, "status")
// Initialize the 'done' field.
doneGEP := builder.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(numParams), false),
}, "done.gep")
builder.CreateStore(llvm.ConstNull(b.ctx.Int1Type()), doneGEP)
// Store all parameters in the state object.
for i, param := range exportedFn.Params() {
gep := builder.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}, "")
builder.CreateStore(param, gep)
}
// Create a new goroutine and add it to the runqueue.
wrapper := b.createGoroutineStartWrapper(b.llvmFnType, b.llvmFn, "", false, true, pos)
stackSize := llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
taskStartFnType, taskStartFn := builder.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
builder.createCall(taskStartFnType, taskStartFn, []llvm.Value{wrapper, statePtr, stackSize, llvm.Undef(b.dataPtrType)}, "")
// Run the scheduler.
builder.createRuntimeCall("wasmExportRun", []llvm.Value{doneGEP}, "")
// Read the return value (if any) and return to the caller of the
// //go:wasmexport function.
if hasReturn {
gep := builder.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(numParams)+1, false),
}, "")
retval := builder.CreateLoad(b.llvmFnType.ReturnType(), gep, "retval")
builder.CreateRet(retval)
} else {
builder.CreateRetVoid()
}
}
}
// createGoroutineStartWrapper creates a wrapper for the task-based
// implementation of goroutines. For example, to call a function like this:
//
@@ -144,7 +286,7 @@ func (b *builder) createGo(instr *ssa.Go) {
// to last parameter of the function) is used for this wrapper. If hasContext is
// false, the parameter bundle is assumed to have no context parameter and undef
// is passed instead.
func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.Value, prefix string, hasContext bool, pos token.Pos) llvm.Value {
func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.Value, prefix string, hasContext, isWasmExport bool, pos token.Pos) llvm.Value {
var wrapper llvm.Value
b := &builder{
@@ -162,14 +304,18 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
if !fn.IsAFunction().IsNil() {
// See whether this wrapper has already been created. If so, return it.
name := fn.Name()
wrapper = c.mod.NamedFunction(name + "$gowrapper")
wrapperName := name + "$gowrapper"
if isWasmExport {
wrapperName += "-wasmexport"
}
wrapper = c.mod.NamedFunction(wrapperName)
if !wrapper.IsNil() {
return llvm.ConstPtrToInt(wrapper, c.uintptrType)
}
// Create the wrapper.
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapperType)
c.addStandardAttributes(wrapper)
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
wrapper.SetUnnamedAddr(true)
@@ -199,23 +345,110 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
}
// Create the list of params for the call.
paramTypes := fnType.ParamTypes()
if !hasContext {
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
}
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
if !hasContext {
params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter
}
if !isWasmExport {
// Regular 'go' instruction.
// Create the call.
b.CreateCall(fnType, fn, params, "")
// Create the list of params for the call.
paramTypes := fnType.ParamTypes()
if !hasContext {
paramTypes = paramTypes[:len(paramTypes)-1] // strip context parameter
}
if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{
llvm.Undef(c.dataPtrType),
}, "")
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
if !hasContext {
params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter
}
// Create the call.
b.CreateCall(fnType, fn, params, "")
if c.Scheduler == "asyncify" {
b.CreateCall(deadlockType, deadlock, []llvm.Value{
llvm.Undef(c.dataPtrType),
}, "")
}
} else {
// Goroutine started from a //go:wasmexport pragma.
// The function looks like this:
//
// func foo$gowrapper-wasmexport(state *stateStruct) {
// // load values
// param0 := state.params[0]
// param1 := state.params[1]
//
// // call wrapped functions
// result := foo(param0, param1, ...)
//
// // store result value (if there is any)
// state.result = result
//
// // finish exported function
// state.done = true
// runtime.wasmExportExit()
// }
//
// The state object here looks like:
//
// struct state {
// param0
// param1
// param* // etc
// done bool
// result returnType
// }
returnType := fnType.ReturnType()
hasReturn := returnType != b.ctx.VoidType()
statePtr := wrapper.Param(0)
// Create the state struct (it must match the type in createWasmExport).
stateFields := fnType.ParamTypes()
numParams := len(stateFields) - 1
stateFields = stateFields[:numParams:numParams] // strip 'context' parameter
stateFields = append(stateFields, c.ctx.Int1Type()) // 'done' bool
if hasReturn {
stateFields = append(stateFields, returnType)
}
stateStruct := b.ctx.StructType(stateFields, false)
// Extract parameters from the state object, and call the function
// that's being wrapped.
var callParams []llvm.Value
for i := 0; i < numParams; i++ {
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
}, "")
param := b.CreateLoad(stateFields[i], gep, "")
callParams = append(callParams, param)
}
callParams = append(callParams, llvm.ConstNull(c.dataPtrType)) // add 'context' parameter
result := b.CreateCall(fnType, fn, callParams, "")
// Store the return value back into the shared state.
// Unlike regular goroutines, these special //go:wasmexport
// goroutines can return a value.
if hasReturn {
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), uint64(numParams)+1, false),
}, "result.ptr")
b.CreateStore(result, gep)
}
// Mark this function as having finished executing.
// This is important so the runtime knows the exported function
// didn't block.
doneGEP := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), uint64(numParams), false),
}, "done.gep")
b.CreateStore(llvm.ConstInt(b.ctx.Int1Type(), 1, false), doneGEP)
// Call back into the runtime. This will exit the goroutine, switch
// back to the scheduler, which will in turn return from the
// //go:wasmexport function.
b.createRuntimeCall("wasmExportExit", nil, "")
}
} else {
@@ -297,5 +530,5 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
}
// Return a ptrtoint of the wrapper, not the function itself.
return b.CreatePtrToInt(wrapper, c.uintptrType, "")
return llvm.ConstPtrToInt(wrapper, c.uintptrType)
}
+3
View File
@@ -41,6 +41,8 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// Create a new global of type runtime/interrupt.handle. Globals of this
// type are lowered in the interrupt lowering pass.
// It must have an alignment of 1, otherwise LLVM thinks a ptrtoint of the
// global has the lower bits unset.
globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
globalLLVMType := b.getLLVMType(globalType)
globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
@@ -48,6 +50,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
global.SetVisibility(llvm.HiddenVisibility)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetAlignment(1)
initializer := llvm.ConstNull(globalLLVMType)
initializer = b.CreateInsertValue(initializer, funcContext, 0, "")
initializer = b.CreateInsertValue(initializer, funcPtr, 1, "")
+5 -11
View File
@@ -6,17 +6,11 @@ import (
"go/token"
"go/types"
"github.com/tinygo-org/tinygo/src/tinygo"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// constants for hashmap algorithms; must match src/runtime/hashmap.go
const (
hashmapAlgorithmBinary = iota
hashmapAlgorithmString
hashmapAlgorithmInterface
)
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
// initializing an appropriately sized object.
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
@@ -24,20 +18,20 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
keyType := mapType.Key().Underlying()
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
var llvmKeyType llvm.Type
var alg uint64 // must match values in src/runtime/hashmap.go
var alg uint64
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// String keys.
llvmKeyType = b.getLLVMType(keyType)
alg = hashmapAlgorithmString
alg = uint64(tinygo.HashmapAlgorithmString)
} else if hashmapIsBinaryKey(keyType) {
// Trivially comparable keys.
llvmKeyType = b.getLLVMType(keyType)
alg = hashmapAlgorithmBinary
alg = uint64(tinygo.HashmapAlgorithmBinary)
} else {
// All other keys. Implemented as map[interface{}]valueType for ease of
// implementation.
llvmKeyType = b.getLLVMRuntimeType("_interface")
alg = hashmapAlgorithmInterface
alg = uint64(tinygo.HashmapAlgorithmInterface)
}
keySize := b.targetData.TypeAllocSize(llvmKeyType)
valueSize := b.targetData.TypeAllocSize(llvmValueType)
+182 -103
View File
@@ -12,6 +12,7 @@ import (
"strings"
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
@@ -23,15 +24,17 @@ import (
// The linkName value contains a valid link name, even if //go:linkname is not
// present.
type functionInfo struct {
wasmModule string // go:wasm-module
wasmName string // wasm-export-name or wasm-import-name in the IR
linkName string // go:linkname, go:export - the IR function name
section string // go:section - object file section name
exported bool // go:export, CGo
interrupt bool // go:interrupt
nobounds bool // go:nobounds
variadic bool // go:variadic (CGo only)
inline inlineType // go:inline
wasmModule string // go:wasm-module
wasmName string // wasm-export-name or wasm-import-name in the IR
wasmExport string // go:wasmexport is defined (export is unset, this adds an exported wrapper)
wasmExportPos token.Pos // position of //go:wasmexport comment
linkName string // go:linkname, go:export - the IR function name
section string // go:section - object file section name
exported bool // go:export, CGo
interrupt bool // go:interrupt
nobounds bool // go:nobounds
variadic bool // go:variadic (CGo only)
inline inlineType // go:inline
}
type inlineType int
@@ -170,6 +173,12 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
case "runtime.stringFromBytes":
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
case "runtime.stringFromRunes":
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
case "runtime.trackPointer":
// This function is necessary for tracking pointers on the stack in a
// portable way (see gc_stack_portable.go). Indicate to the optimizer
@@ -241,8 +250,22 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
// Pick the default linkName.
linkName: f.RelString(nil),
}
// Check for a few runtime functions that are treated specially.
if info.linkName == "runtime.wasmEntryReactor" && c.BuildMode == "c-shared" {
info.linkName = "_initialize"
info.wasmName = "_initialize"
info.exported = true
}
if info.linkName == "runtime.wasmEntryCommand" && c.BuildMode == "default" {
info.linkName = "_start"
info.wasmName = "_start"
info.exported = true
}
// Check for //go: pragmas, which may change the link name (among others).
c.parsePragmas(&info, f)
c.functionInfos[f] = info
return info
}
@@ -250,126 +273,164 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
// //export or //go:noinline.
func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
if f.Syntax() == nil {
syntax := f.Syntax()
if f.Origin() != nil {
syntax = f.Origin().Syntax()
}
if syntax == nil {
return
}
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
// Read all pragmas of this function.
var pragmas []*ast.Comment
hasWasmExport := false
if decl, ok := syntax.(*ast.FuncDecl); ok && decl.Doc != nil {
for _, comment := range decl.Doc.List {
text := comment.Text
if strings.HasPrefix(text, "//export ") {
// Rewrite '//export' to '//go:export' for compatibility with
// gc.
text = "//go:" + text[2:]
if strings.HasPrefix(text, "//go:") || strings.HasPrefix(text, "//export ") {
pragmas = append(pragmas, comment)
if strings.HasPrefix(comment.Text, "//go:wasmexport ") {
hasWasmExport = true
}
}
if !strings.HasPrefix(text, "//go:") {
}
}
// Parse each pragma.
for _, comment := range pragmas {
parts := strings.Fields(comment.Text)
switch parts[0] {
case "//export", "//go:export":
if len(parts) != 2 {
continue
}
if hasWasmExport {
// //go:wasmexport overrides //export.
continue
}
parts := strings.Fields(text)
switch parts[0] {
case "//go:export":
if len(parts) != 2 {
continue
}
info.linkName = parts[1]
info.wasmName = info.linkName
info.exported = true
case "//go:interrupt":
if hasUnsafeImport(f.Pkg.Pkg) {
info.interrupt = true
}
case "//go:wasm-module":
// Alternative comment for setting the import module.
// This is deprecated, use //go:wasmimport instead.
if len(parts) != 2 {
continue
}
info.wasmModule = parts[1]
case "//go:wasmimport":
// Import a WebAssembly function, for example a WASI function.
// Original proposal: https://github.com/golang/go/issues/38248
// Allow globally: https://github.com/golang/go/issues/59149
if len(parts) != 3 {
continue
}
c.checkWasmImport(f, comment.Text)
info.exported = true
info.wasmModule = parts[1]
info.wasmName = parts[2]
case "//go:inline":
info.inline = inlineHint
case "//go:noinline":
info.linkName = parts[1]
info.wasmName = info.linkName
info.exported = true
case "//go:interrupt":
if hasUnsafeImport(f.Pkg.Pkg) {
info.interrupt = true
}
case "//go:wasm-module":
// Alternative comment for setting the import module.
// This is deprecated, use //go:wasmimport instead.
if len(parts) != 2 {
continue
}
info.wasmModule = parts[1]
case "//go:wasmimport":
// Import a WebAssembly function, for example a WASI function.
// Original proposal: https://github.com/golang/go/issues/38248
// Allow globally: https://github.com/golang/go/issues/59149
if len(parts) != 3 {
continue
}
if f.Blocks != nil {
// Defined functions cannot be exported.
c.addError(f.Pos(), "can only use //go:wasmimport on declarations")
continue
}
c.checkWasmImportExport(f, comment.Text)
info.exported = true
info.wasmModule = parts[1]
info.wasmName = parts[2]
case "//go:wasmexport":
if f.Blocks == nil {
c.addError(f.Pos(), "can only use //go:wasmexport on definitions")
continue
}
if len(parts) != 2 {
c.addError(f.Pos(), fmt.Sprintf("expected one parameter to //go:wasmimport, not %d", len(parts)-1))
continue
}
name := parts[1]
if name == "_start" || name == "_initialize" {
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow %#v", name))
continue
}
if c.BuildMode != "c-shared" && f.RelString(nil) == "main.main" {
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow main.main to be exported with -buildmode=%s", c.BuildMode))
continue
}
if c.archFamily() != "wasm32" {
c.addError(f.Pos(), "//go:wasmexport is only supported on wasm")
}
c.checkWasmImportExport(f, comment.Text)
info.wasmExport = name
info.wasmExportPos = comment.Slash
case "//go:inline":
info.inline = inlineHint
case "//go:noinline":
info.inline = inlineNone
case "//go:linkname":
if len(parts) != 3 || parts[1] != f.Name() {
continue
}
// Only enable go:linkname when the package imports "unsafe".
// This is a slightly looser requirement than what gc uses: gc
// requires the file to import "unsafe", not the package as a
// whole.
if hasUnsafeImport(f.Pkg.Pkg) {
info.linkName = parts[2]
}
case "//go:section":
// Only enable go:section when the package imports "unsafe".
// go:section also implies go:noinline since inlining could
// move the code to a different section than that requested.
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
info.section = parts[1]
info.inline = inlineNone
case "//go:linkname":
if len(parts) != 3 || parts[1] != f.Name() {
continue
}
// Only enable go:linkname when the package imports "unsafe".
// This is a slightly looser requirement than what gc uses: gc
// requires the file to import "unsafe", not the package as a
// whole.
if hasUnsafeImport(f.Pkg.Pkg) {
info.linkName = parts[2]
}
case "//go:section":
// Only enable go:section when the package imports "unsafe".
// go:section also implies go:noinline since inlining could
// move the code to a different section than that requested.
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
info.section = parts[1]
info.inline = inlineNone
}
case "//go:nobounds":
// Skip bounds checking in this function. Useful for some
// runtime functions.
// This is somewhat dangerous and thus only imported in packages
// that import unsafe.
if hasUnsafeImport(f.Pkg.Pkg) {
info.nobounds = true
}
case "//go:variadic":
// The //go:variadic pragma is emitted by the CGo preprocessing
// pass for C variadic functions. This includes both explicit
// (with ...) and implicit (no parameters in signature)
// functions.
if strings.HasPrefix(f.Name(), "C.") {
// This prefix cannot naturally be created, it must have
// been created as a result of CGo preprocessing.
info.variadic = true
}
}
case "//go:nobounds":
// Skip bounds checking in this function. Useful for some
// runtime functions.
// This is somewhat dangerous and thus only imported in packages
// that import unsafe.
if hasUnsafeImport(f.Pkg.Pkg) {
info.nobounds = true
}
case "//go:variadic":
// The //go:variadic pragma is emitted by the CGo preprocessing
// pass for C variadic functions. This includes both explicit
// (with ...) and implicit (no parameters in signature)
// functions.
if strings.HasPrefix(f.Name(), "C.") {
// This prefix cannot naturally be created, it must have
// been created as a result of CGo preprocessing.
info.variadic = true
}
}
}
}
// Check whether this function cannot be used in //go:wasmimport. It will add an
// error if this is the case.
// Check whether this function can be used in //go:wasmimport or
// //go:wasmexport. It will add an error if this is not the case.
//
// The list of allowed types is based on this proposal:
// https://github.com/golang/go/issues/59149
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
func (c *compilerContext) checkWasmImportExport(f *ssa.Function, pragma string) {
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" || c.pkg.Path() == "syscall" {
// The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers).
return
}
if f.Blocks != nil {
// Defined functions cannot be exported.
c.addError(f.Pos(), "can only use //go:wasmimport on declarations")
return
}
if f.Signature.Results().Len() > 1 {
c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma))
} else if f.Signature.Results().Len() == 1 {
result := f.Signature.Results().At(0)
if !isValidWasmType(result.Type(), siteResult) {
if !c.isValidWasmType(result.Type(), siteResult) {
c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String()))
}
}
for _, param := range f.Params {
// Check whether the type is allowed.
// Only a very limited number of types can be mapped to WebAssembly.
if !isValidWasmType(param.Type(), siteParam) {
if !c.isValidWasmType(param.Type(), siteParam) {
c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String()))
}
}
@@ -382,13 +443,15 @@ func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
//
// This previously reflected the additional restrictions documented here:
// https://github.com/golang/go/issues/59149
func isValidWasmType(typ types.Type, site wasmSite) bool {
func (c *compilerContext) isValidWasmType(typ types.Type, site wasmSite) bool {
switch typ := typ.Underlying().(type) {
case *types.Basic:
switch typ.Kind() {
case types.Bool:
return true
case types.Int, types.Uint, types.Int8, types.Uint8, types.Int16, types.Uint16, types.Int32, types.Uint32, types.Int64, types.Uint64:
case types.Int8, types.Uint8, types.Int16, types.Uint16:
return site == siteIndirect
case types.Int32, types.Uint32, types.Int64, types.Uint64:
return true
case types.Float32, types.Float64:
return true
@@ -399,19 +462,35 @@ func isValidWasmType(typ types.Type, site wasmSite) bool {
return site == siteParam || site == siteIndirect
}
case *types.Array:
return site == siteIndirect && isValidWasmType(typ.Elem(), siteIndirect)
return site == siteIndirect && c.isValidWasmType(typ.Elem(), siteIndirect)
case *types.Struct:
if site != siteIndirect {
return false
}
// Structs with no fields do not need structs.HostLayout
if typ.NumFields() == 0 {
return true
}
hasHostLayout := true // default to true before detecting Go version
// (*types.Package).GoVersion added in go1.21
if gv, ok := any(c.pkg).(interface{ GoVersion() string }); ok {
if goenv.Compare(gv.GoVersion(), "go1.23") >= 0 {
hasHostLayout = false // package structs added in go1.23
}
}
for i := 0; i < typ.NumFields(); i++ {
if !isValidWasmType(typ.Field(i).Type(), siteIndirect) {
ftyp := typ.Field(i).Type()
if ftyp.String() == "structs.HostLayout" {
hasHostLayout = true
continue
}
if !c.isValidWasmType(ftyp, siteIndirect) {
return false
}
}
return true
return hasHostLayout
case *types.Pointer:
return isValidWasmType(typ.Elem(), siteIndirect)
return c.isValidWasmType(typ.Elem(), siteIndirect)
}
return false
}
+37 -7
View File
@@ -1,6 +1,9 @@
package main
import "unsafe"
import (
"structs"
"unsafe"
)
//go:wasmimport modulename empty
func empty()
@@ -14,31 +17,37 @@ func implementation() {
type Uint uint32
type S struct {
_ structs.HostLayout
a [4]uint32
b uintptr
c int
d float32
e float64
}
//go:wasmimport modulename validparam
func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintptr, g string, h *int32, i *S)
func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintptr, g string, h *int32, i *S, j *struct{}, k *[8]uint8)
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [4]uint32
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type struct{a int}
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type chan struct{}
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type func()
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type uint
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [8]int
//
//go:wasmimport modulename invalidparam
func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func())
func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func(), f int, g uint, h [8]int)
// ERROR: //go:wasmimport modulename invalidparam_no_hostlayout: unsupported parameter type *struct{int}
// ERROR: //go:wasmimport modulename invalidparam_no_hostlayout: unsupported parameter type *struct{string}
//
//go:wasmimport modulename invalidparam_no_hostlayout
func invalidparam_no_hostlayout(a *struct{ int }, b *struct{ string })
//go:wasmimport modulename validreturn_int32
func validreturn_int32() int32
//go:wasmimport modulename validreturn_int
func validreturn_int() int
//go:wasmimport modulename validreturn_ptr_int32
func validreturn_ptr_int32() *int32
@@ -48,6 +57,12 @@ func validreturn_ptr_string() *string
//go:wasmimport modulename validreturn_ptr_struct
func validreturn_ptr_struct() *S
//go:wasmimport modulename validreturn_ptr_struct
func validreturn_ptr_empty_struct() *struct{}
//go:wasmimport modulename validreturn_ptr_array
func validreturn_ptr_array() *[8]uint8
//go:wasmimport modulename validreturn_unsafe_pointer
func validreturn_unsafe_pointer() unsafe.Pointer
@@ -56,11 +71,26 @@ func validreturn_unsafe_pointer() unsafe.Pointer
//go:wasmimport modulename manyreturns
func manyreturns() (int32, int32)
// ERROR: //go:wasmimport modulename invalidreturn_int: unsupported result type int
//
//go:wasmimport modulename invalidreturn_int
func invalidreturn_int() int
// ERROR: //go:wasmimport modulename invalidreturn_int: unsupported result type uint
//
//go:wasmimport modulename invalidreturn_int
func invalidreturn_uint() uint
// ERROR: //go:wasmimport modulename invalidreturn_func: unsupported result type func()
//
//go:wasmimport modulename invalidreturn_func
func invalidreturn_func() func()
// ERROR: //go:wasmimport modulename invalidreturn_pointer_array_int: unsupported result type *[8]int
//
//go:wasmimport modulename invalidreturn_pointer_array_int
func invalidreturn_pointer_array_int() *[8]int
// ERROR: //go:wasmimport modulename invalidreturn_slice_byte: unsupported result type []byte
//
//go:wasmimport modulename invalidreturn_slice_byte
+16
View File
@@ -48,6 +48,22 @@ func inlineFunc() {
func noinlineFunc() {
}
type Int interface {
int8 | int16
}
// Same for generic functions (but the compiler may miss the pragma due to it
// being generic).
//
//go:noinline
func noinlineGenericFunc[T Int]() {
}
func useGeneric() {
// Make sure the generic function above is instantiated.
noinlineGenericFunc[int8]()
}
// This function should have the specified section.
//
//go:section .special_function_section
+13
View File
@@ -48,6 +48,19 @@ entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.useGeneric(ptr %context) unnamed_addr #2 {
entry:
call void @"main.noinlineGenericFunc[int8]"(ptr undef)
ret void
}
; Function Attrs: noinline nounwind
define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #5 {
entry:
ret void
}
; Function Attrs: noinline nounwind
define hidden void @main.functionInSection(ptr %context) unnamed_addr #5 section ".special_function_section" {
entry:
+2
View File
@@ -24,6 +24,8 @@ func TestErrors(t *testing.T) {
{name: "cgo"},
{name: "compiler"},
{name: "interp"},
{name: "invalidmain"},
{name: "invalidname"},
{name: "linker-flashoverflow", target: "cortex-m-qemu"},
{name: "linker-ramoverflow", target: "cortex-m-qemu"},
{name: "linker-undefined", target: "darwin/arm64"},
Generated
+4 -4
View File
@@ -20,16 +20,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1703068421,
"narHash": "sha256-WSw5Faqlw75McIflnl5v7qVD/B3S2sLh+968bpOGrWA=",
"lastModified": 1728500571,
"narHash": "sha256-dOymOQ3AfNI4Z337yEwHGohrVQb4yPODCW9MDUyAc4w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d65bceaee0fb1e64363f7871bc43dc1c6ecad99f",
"rev": "d51c28603def282a24fa034bcb007e2bcb5b5dd0",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-23.11",
"ref": "nixos-24.05",
"type": "indirect"
}
},
+6 -6
View File
@@ -35,7 +35,7 @@
inputs = {
# Use a recent stable release, but fix the version to make it reproducible.
# This version should be updated from time to time.
nixpkgs.url = "nixpkgs/nixos-23.11";
nixpkgs.url = "nixpkgs/nixos-24.05";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
@@ -49,11 +49,11 @@
buildInputs = [
# These dependencies are required for building tinygo (go install).
go
llvmPackages_17.llvm
llvmPackages_17.libclang
llvmPackages_18.llvm
llvmPackages_18.libclang
# Additional dependencies needed at runtime, for building and/or
# flashing.
llvmPackages_17.lld
llvmPackages_18.lld
avrdude
binaryen
# Additional dependencies needed for on-chip debugging.
@@ -68,7 +68,7 @@
# Without setting these explicitly, Homebrew versions might be used
# or the default `ar` and `nm` tools might be used (which don't
# support wasi).
export CLANG="clang-17 -resource-dir ${llvmPackages_17.clang.cc.lib}/lib/clang/17"
export CLANG="clang-18 -resource-dir ${llvmPackages_18.clang.cc.lib}/lib/clang/18"
export LLVM_AR=llvm-ar
export LLVM_NM=llvm-nm
@@ -77,7 +77,7 @@
export MD5SUM=md5sum
# Ugly hack to make the Clang resources directory available.
export GOFLAGS="\"-ldflags=-X github.com/tinygo-org/tinygo/goenv.clangResourceDir=${llvmPackages_17.clang.cc.lib}/lib/clang/17\" -tags=llvm17"
export GOFLAGS="\"-ldflags=-X github.com/tinygo-org/tinygo/goenv.clangResourceDir=${llvmPackages_18.clang.cc.lib}/lib/clang/18\" -tags=llvm18"
'';
};
}
+1
View File
@@ -14,6 +14,7 @@ require (
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-tty v0.0.4
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
github.com/tetratelabs/wazero v1.6.0
go.bug.st/serial v1.6.0
golang.org/x/net v0.26.0
golang.org/x/sys v0.21.0
+2 -5
View File
@@ -12,7 +12,6 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
@@ -45,19 +44,18 @@ github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3px
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5 h1:1SoBaSPudixRecmlHXb/GxmaD3fLMtHIDN13QujwQuc=
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g=
github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -76,6 +74,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8 h1:bLsZXRUBavt++CJlMN7sppNziqu3LyamESLhFJcpqFQ=
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
+65 -17
View File
@@ -4,29 +4,37 @@ import (
"errors"
"fmt"
"io"
"runtime/debug"
"strings"
)
// Version of TinyGo.
// Update this value before release of new version of software.
const version = "0.34.0-dev"
var (
// This variable is set at build time using -ldflags parameters.
// See: https://stackoverflow.com/a/11355611
GitSha1 string
)
const version = "0.35.0-dev"
// Return TinyGo version, either in the form 0.30.0 or as a development version
// (like 0.30.0-dev-abcd012).
func Version() string {
v := version
if strings.HasSuffix(version, "-dev") && GitSha1 != "" {
v += "-" + GitSha1
if strings.HasSuffix(version, "-dev") {
if hash := readGitHash(); hash != "" {
v += "-" + hash
}
}
return v
}
func readGitHash() string {
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
if setting.Key == "vcs.revision" {
return setting.Value[:8]
}
}
}
return ""
}
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
func GetGorootVersion() (major, minor int, err error) {
@@ -34,27 +42,67 @@ func GetGorootVersion() (major, minor int, err error) {
if err != nil {
return 0, 0, err
}
major, minor, _, err = Parse(s)
return major, minor, err
}
if s == "" || s[:2] != "go" {
return 0, 0, errors.New("could not parse Go version: version does not start with 'go' prefix")
// Parse parses the Go version (like "go1.3.2") in the parameter and return the
// major, minor, and patch version: 1, 3, and 2 in this example.
// If there is an error, (0, 0, 0) and an error will be returned.
func Parse(version string) (major, minor, patch int, err error) {
if strings.HasPrefix(version, "devel ") {
version = strings.Split(strings.TrimPrefix(version, "devel "), version)[0]
}
if version == "" || version[:2] != "go" {
return 0, 0, 0, errors.New("could not parse Go version: version does not start with 'go' prefix")
}
parts := strings.Split(s[2:], ".")
parts := strings.Split(version[2:], ".")
if len(parts) < 2 {
return 0, 0, errors.New("could not parse Go version: version has less than two parts")
return 0, 0, 0, errors.New("could not parse Go version: version has less than two parts")
}
// Ignore the errors, we don't really handle errors here anyway.
var trailing string
n, err := fmt.Sscanf(s, "go%d.%d%s", &major, &minor, &trailing)
if n == 2 && err == io.EOF {
n, err := fmt.Sscanf(version, "go%d.%d.%d%s", &major, &minor, &patch, &trailing)
if n == 2 {
n, err = fmt.Sscanf(version, "go%d.%d%s", &major, &minor, &trailing)
}
if n >= 2 && err == io.EOF {
// Means there were no trailing characters (i.e., not an alpha/beta)
err = nil
}
if err != nil {
return 0, 0, fmt.Errorf("failed to parse version: %s", err)
return 0, 0, 0, fmt.Errorf("failed to parse version: %s", err)
}
return major, minor, patch, nil
}
// Compare compares two Go version strings.
// The result will be 0 if a == b, -1 if a < b, and +1 if a > b.
// If either a or b is not a valid Go version, it is treated as "go0.0"
// and compared lexicographically.
// See [Parse] for more information.
func Compare(a, b string) int {
aMajor, aMinor, aPatch, _ := Parse(a)
bMajor, bMinor, bPatch, _ := Parse(b)
switch {
case aMajor < bMajor:
return -1
case aMajor > bMajor:
return +1
case aMinor < bMinor:
return -1
case aMinor > bMinor:
return +1
case aPatch < bPatch:
return -1
case aPatch > bPatch:
return +1
default:
return strings.Compare(a, b)
}
return
}
// GorootVersionString returns the version string as reported by the Go
+72
View File
@@ -0,0 +1,72 @@
package goenv
import "testing"
func TestParse(t *testing.T) {
tests := []struct {
v string
major int
minor int
patch int
wantErr bool
}{
{"", 0, 0, 0, true},
{"go", 0, 0, 0, true},
{"go1", 0, 0, 0, true},
{"go.0", 0, 0, 0, true},
{"go1.0", 1, 0, 0, false},
{"go1.1", 1, 1, 0, false},
{"go1.23", 1, 23, 0, false},
{"go1.23.5", 1, 23, 5, false},
{"go1.23.5-rc6", 1, 23, 5, false},
{"go2.0", 2, 0, 0, false},
{"go2.0.15", 2, 0, 15, false},
{"devel go1.24-f99f5da18f Thu Nov 14 22:29:26 2024 +0000 darwin/arm64", 1, 24, 0, false},
}
for _, tt := range tests {
t.Run(tt.v, func(t *testing.T) {
major, minor, patch, err := Parse(tt.v)
if err == nil && tt.wantErr {
t.Errorf("Parse(%q): expected err != nil", tt.v)
}
if err != nil && !tt.wantErr {
t.Errorf("Parse(%q): expected err == nil", tt.v)
}
if major != tt.major || minor != tt.minor || patch != tt.patch {
t.Errorf("Parse(%q): expected %d, %d, %d, nil; got %d, %d, %d, %v",
tt.v, tt.major, tt.minor, tt.patch, major, minor, patch, err)
}
})
}
}
func TestCompare(t *testing.T) {
tests := []struct {
a string
b string
want int
}{
{"", "", 0},
{"go0", "go0", 0},
{"go0", "go1", -1},
{"go1", "go0", 1},
{"go1", "go2", -1},
{"go2", "go1", 1},
{"go1.1", "go1.2", -1},
{"go1.2", "go1.1", 1},
{"go1.1.0", "go1.2.0", -1},
{"go1.2.0", "go1.1.0", 1},
{"go1.2.0", "go2.3.0", -1},
{"go1.23.2", "go1.23.10", -1},
{"go0.1.22", "go1.23.101", -1},
}
for _, tt := range tests {
t.Run(tt.a+" "+tt.b, func(t *testing.T) {
got := Compare(tt.a, tt.b)
if got != tt.want {
t.Errorf("Compare(%q, %q): expected %d; got %d",
tt.a, tt.b, tt.want, got)
}
})
}
}
+9 -3
View File
@@ -2,11 +2,17 @@ module github.com/tinygo-org/tinygo/internal/tools
go 1.22.4
require github.com/bytecodealliance/wasm-tools-go v0.2.0
require github.com/bytecodealliance/wasm-tools-go v0.3.1
require (
github.com/coreos/go-semver v0.3.1 // indirect
github.com/urfave/cli/v3 v3.0.0-alpha9 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/regclient/regclient v0.7.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/urfave/cli/v3 v3.0.0-alpha9.2 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/sys v0.26.0 // indirect
)
+29 -10
View File
@@ -1,25 +1,44 @@
github.com/bytecodealliance/wasm-tools-go v0.2.0 h1:JdmiZew7ewHjf+ZGGRE4gZM85Ad/PGW/5I57hepEOjQ=
github.com/bytecodealliance/wasm-tools-go v0.2.0/go.mod h1:2GnJCUlcDrslZ/L6+yYqoUnewDlBvqRS2N/0NW9ro6w=
github.com/bytecodealliance/wasm-tools-go v0.3.1 h1:9Q9PjSzkbiVmkUvZ7nYCfJ02mcQDBalxycA3s8g7kR4=
github.com/bytecodealliance/wasm-tools-go v0.3.1/go.mod h1:vNAQ8DAEp6xvvk+TUHah5DslLEa76f4H6e737OeaxuY=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/olareg/olareg v0.1.0 h1:1dXBOgPrig5N7zoXyIZVQqU0QBo6sD9pbL6UYjY75CA=
github.com/olareg/olareg v0.1.0/go.mod h1:RBuU7JW7SoIIxZKzLRhq8sVtQeAHzCAtRrXEBx2KlM4=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/regclient/regclient v0.7.1 h1:qEsJrTmZd98fZKjueAbrZCSNGU+ifnr6xjlSAs3WOPs=
github.com/regclient/regclient v0.7.1/go.mod h1:+w/BFtJuw0h0nzIw/z2+1FuA2/dVXBzDq4rYmziJpMc=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v3 v3.0.0-alpha9 h1:P0RMy5fQm1AslQS+XCmy9UknDXctOmG/q/FZkUFnJSo=
github.com/urfave/cli/v3 v3.0.0-alpha9/go.mod h1:0kK/RUFHyh+yIKSfWxwheGndfnrvYSmYFVeKCh03ZUc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v3 v3.0.0-alpha9.2 h1:CL8llQj3dGRLVQQzHxS+ZYRLanOuhyK1fXgLKD+qV+Y=
github.com/urfave/cli/v3 v3.0.0-alpha9.2/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+29 -24
View File
@@ -229,30 +229,35 @@ func needsSyscallPackage(buildTags []string) bool {
// means use the TinyGo version.
func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
paths := map[string]bool{
"": true,
"crypto/": true,
"crypto/rand/": false,
"crypto/tls/": false,
"device/": false,
"examples/": false,
"internal/": true,
"internal/abi/": false,
"internal/binary/": false,
"internal/bytealg/": false,
"internal/cm/": false,
"internal/fuzz/": false,
"internal/reflectlite/": false,
"internal/task/": false,
"internal/wasi/": false,
"machine/": false,
"net/": true,
"net/http/": false,
"os/": true,
"reflect/": false,
"runtime/": false,
"sync/": true,
"testing/": true,
"unique/": false,
"": true,
"crypto/": true,
"crypto/rand/": false,
"crypto/tls/": false,
"crypto/x509/": true,
"crypto/x509/internal/": true,
"crypto/x509/internal/macos/": false,
"device/": false,
"examples/": false,
"internal/": true,
"internal/abi/": false,
"internal/binary/": false,
"internal/bytealg/": false,
"internal/cm/": false,
"internal/fuzz/": false,
"internal/reflectlite/": false,
"internal/gclayout": false,
"internal/task/": false,
"internal/wasi/": false,
"machine/": false,
"net/": true,
"net/http/": false,
"os/": true,
"reflect/": false,
"runtime/": false,
"sync/": true,
"testing/": true,
"tinygo/": false,
"unique/": false,
}
if goMinor >= 19 {
+15 -3
View File
@@ -418,8 +418,12 @@ func (p *Package) Check() error {
packageName := p.ImportPath
if p == p.program.MainPkg() {
if p.Name != "main" {
// Sanity check. Should not ever trigger.
panic("expected main package to have name 'main'")
return Errors{p, []error{
scanner.Error{
Pos: p.program.fset.Position(p.Files[0].Name.Pos()),
Msg: fmt.Sprintf("expected main package to have name \"main\", not %#v", p.Name),
},
}}
}
packageName = "main"
}
@@ -428,7 +432,15 @@ func (p *Package) Check() error {
if err, ok := err.(Errors); ok {
return err
}
return Errors{p, typeErrors}
if len(typeErrors) != 0 {
// Got type errors, so return them.
return Errors{p, typeErrors}
}
// This can happen in some weird cases.
// The only case I know is when compiling a Go 1.23 program, with a
// TinyGo version that supports Go 1.23 but is compiled using Go 1.22.
// So this should be pretty rare.
return Errors{p, []error{err}}
}
p.Pkg = typesPkg
+277 -103
View File
@@ -283,46 +283,6 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
// Tests are always run in the package directory.
cmd.Dir = result.MainDir
// wasmtime is the default emulator used for `-target=wasip1`. wasmtime
// is a WebAssembly runtime CLI with WASI enabled by default. However,
// only stdio are allowed by default. For example, while STDOUT routes
// to the host, other files don't. It also does not inherit environment
// variables from the host. Some tests read testdata files, often from
// outside the package directory. Other tests require temporary
// writeable directories. We allow this by adding wasmtime flags below.
if config.EmulatorName() == "wasmtime" {
// At this point, The current working directory is at the package
// directory. Ex. $GOROOT/src/compress/flate for compress/flate.
// buildAndRun has already added arguments for wasmtime, that allow
// read-access to files such as "testdata/huffman-zero.in".
//
// Ex. main(.wasm) --dir=. -- -test.v
// Below adds additional wasmtime flags in case a test reads files
// outside its directory, like "../testdata/e.txt". This allows any
// relative directory up to the module root, even if the test never
// reads any files.
//
// Ex. run --dir=.. --dir=../.. --dir=../../..
var dirs []string
switch config.Target.GOOS {
case "wasip1":
dirs = dirsToModuleRootRel(result.MainDir, result.ModuleRoot)
default:
dirs = dirsToModuleRootAbs(result.MainDir, result.ModuleRoot)
}
args := []string{"run"}
for _, d := range dirs {
args = append(args, "--dir="+d)
}
args = append(args, "--env=PWD="+cmd.Dir)
args = append(args, cmd.Args[1:]...)
cmd.Args = args
}
// Run the test.
start := time.Now()
err = cmd.Run()
@@ -809,9 +769,6 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
// passes command line arguments and environment variables in a way appropriate
// for the given emulator.
func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) (builder.BuildResult, error) {
isSingleFile := strings.HasSuffix(pkgName, ".go")
// Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment,
@@ -824,7 +781,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
needsEnvInVars = true
}
}
var args, emuArgs, env []string
var args, env []string
var extraCmdEnv []string
if needsEnvInVars {
runtimeGlobals := make(map[string]string)
@@ -844,21 +801,6 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
"runtime": runtimeGlobals,
}
}
} else if config.EmulatorName() == "wasmtime" {
for _, v := range environmentVars {
emuArgs = append(emuArgs, "--env", v)
}
if len(cmdArgs) != 0 {
// Use of '--' argument no longer necessary as of Wasmtime v14:
// https://github.com/bytecodealliance/wasmtime/pull/6946
// args = append(args, "--")
args = append(args, cmdArgs...)
}
// Set this for nicer backtraces during tests, but don't override the user.
if _, ok := os.LookupEnv("WASMTIME_BACKTRACE_DETAILS"); !ok {
extraCmdEnv = append(extraCmdEnv, "WASMTIME_BACKTRACE_DETAILS=1")
}
} else {
// Pass environment variables and command line parameters as usual.
// This also works on qemu-aarch64 etc.
@@ -901,27 +843,61 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
return result, err
}
name = emulator[0]
name, emulator = emulator[0], emulator[1:]
// wasmtime is a WebAssembly runtime CLI with WASI enabled by default.
// By default, only stdio is allowed. For example, while STDOUT routes
// to the host, other files don't. It also does not inherit environment
// variables from the host. Some tests read testdata files, often from
// outside the package directory. Other tests require temporary
// writeable directories. We allow this by adding wasmtime flags below.
if name == "wasmtime" {
// Wasmtime needs some special flags to pass environment variables
// and allow reading from the current directory.
switch config.Options.Target {
case "wasip1":
emuArgs = append(emuArgs, "--dir=.")
case "wasip2":
dir := result.MainDir
if isSingleFile {
cwd, _ := os.Getwd()
dir = cwd
}
emuArgs = append(emuArgs, "--dir="+dir)
emuArgs = append(emuArgs, "--env=PWD="+dir)
var emuArgs []string
// Extract the wasmtime subcommand (e.g. "run" or "serve")
if len(emulator) > 1 {
emuArgs = append(emuArgs, emulator[0])
emulator = emulator[1:]
}
wd, _ := os.Getwd()
// Below adds additional wasmtime flags in case a test reads files
// outside its directory, like "../testdata/e.txt". This allows any
// relative directory up to the module root, even if the test never
// reads any files.
if config.TestConfig.CompileTestBinary {
// Set working directory to package dir
wd = result.MainDir
// Add relative dirs (../, ../..) up to module root (for wasip1)
dirs := dirsToModuleRootRel(result.MainDir, result.ModuleRoot)
// Add absolute dirs up to module root (for wasip2)
dirs = append(dirs, dirsToModuleRootAbs(result.MainDir, result.ModuleRoot)...)
for _, d := range dirs {
emuArgs = append(emuArgs, "--dir="+d)
}
} else {
emuArgs = append(emuArgs, "--dir=.")
}
emuArgs = append(emuArgs, "--dir="+wd)
emuArgs = append(emuArgs, "--env=PWD="+wd)
for _, v := range environmentVars {
emuArgs = append(emuArgs, "--env", v)
}
// Set this for nicer backtraces during tests, but don't override the user.
if _, ok := os.LookupEnv("WASMTIME_BACKTRACE_DETAILS"); !ok {
extraCmdEnv = append(extraCmdEnv, "WASMTIME_BACKTRACE_DETAILS=1")
}
emulator = append(emuArgs, emulator...)
}
emuArgs = append(emuArgs, emulator[1:]...)
args = append(emuArgs, args...)
args = append(emulator, args...)
}
var cmd *exec.Cmd
if ctx != nil {
@@ -951,7 +927,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
// Run binary.
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(cmd.Path, cmd.Args...)
config.Options.PrintCommands(cmd.Path, cmd.Args[1:]...)
}
err = run(cmd, result)
if err != nil {
@@ -1256,36 +1232,169 @@ func getBMPPorts() (gdbPort, uartPort string, err error) {
}
}
func usage(command string) {
switch command {
default:
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
fmt.Fprintln(os.Stderr, "version:", goenv.Version())
fmt.Fprintf(os.Stderr, "usage: %s <command> [arguments]\n", os.Args[0])
fmt.Fprintln(os.Stderr, "\ncommands:")
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
fmt.Fprintln(os.Stderr, " run: compile and run immediately")
fmt.Fprintln(os.Stderr, " test: test packages")
fmt.Fprintln(os.Stderr, " flash: compile and flash to the device")
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
fmt.Fprintln(os.Stderr, " lldb: run/flash and immediately enter LLDB")
fmt.Fprintln(os.Stderr, " monitor: open communication port")
fmt.Fprintln(os.Stderr, " ports: list available serial ports")
fmt.Fprintln(os.Stderr, " env: list environment variables used during build")
fmt.Fprintln(os.Stderr, " list: run go list using the TinyGo root")
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+goenv.Get("GOCACHE")+")")
fmt.Fprintln(os.Stderr, " targets: list targets")
fmt.Fprintln(os.Stderr, " info: show info for specified target")
fmt.Fprintln(os.Stderr, " version: show version")
fmt.Fprintln(os.Stderr, " help: print this help text")
const (
usageBuild = `Build compiles the packages named by the import paths, along with their
dependencies, but it does not install the results. The output binary is
specified using the -o parameter. The generated file type depends on the
extension:
.o:
Create a relocatable object file. You can use this option if you
don't want to use the TinyGo build system or want to do other custom
things.
.ll:
Create textual LLVM IR, after optimization. This is mainly useful
for debugging.
.bc:
Create LLVM bitcode, after optimization. This may be useful for
debugging or for linking into other programs using LTO.
.hex:
Create an Intel HEX file to flash it to a microcontroller.
.bin:
Similar, but create a binary file.
.wasm:
Compile and link a WebAssembly file.
(all other) Compile and link the program into a regular executable. For
microcontrollers, it is common to use the .elf file extension to indicate a
linked ELF file is generated. For Linux, it is common to build binaries with no
extension at all.`
usageRun = `Run the program, either directly on the host or in an emulated environment
(depending on -target).`
usageFlash = `Flash the program to a microcontroller. Some common flags are described below.
-target={name}:
Specifies the type of microcontroller that is used. The name of the
microcontroller is given on the individual pages for each board type
listed under Microcontrollers
(https://tinygo.org/docs/reference/microcontrollers/).
Examples: "arduino-nano", "d1mini", "xiao".
-monitor:
Start the serial monitor (see below) immediately after
flashing. However, some microcontrollers need a split second
or two to configure the serial port after flashing, and
using the "-monitor" flag can fail because the serial
monitor starts too quickly. In that case, use the "tinygo
monitor" command explicitly.`
usageMonitor = `Start the serial monitor on the serial port that is connected to the
microcontroller. If there is only a single board attached to the host computer,
the default values for various options should be sufficient. In other
situations, particularly if you have multiple microcontrollers attached, some
parameters may need to be overridden using the following flags:
-port={port}:
If there are multiple microcontroller attached, an error
message will display a list of potential serial ports. The
appropriate port can be specified by this flag. On Linux,
the port will be something like /dev/ttyUSB0 or /dev/ttyACM1.
On MacOS, the port will look like /dev/cu.usbserial-1420. On
Windows, the port will be something like COM1 or COM31.
-baudrate={rate}:
The default baud rate is 115200. Boards using the AVR
processor (e.g. Arduino Nano, Arduino Mega 2560) use 9600
instead.
-target={name}:
If you have more than one microcontrollers attached, you can
sometimes just specify the target name and let tinygo
monitor figure out the port. Sometimes, this does not work
and you have to explicitly use the -port flag.
The serial monitor intercepts several control characters for its own use instead of sending them
to the microcontroller:
Control-C: terminates the tinygo monitor
Control-Z: suspends the tinygo monitor and drops back into shell
Control-\: terminates the tinygo monitor with a stack trace
Control-S: flow control, suspends output to the console
Control-Q: flow control, resumes output to the console
Control-@: thrown away by tinygo monitor
Note: If you are using os.Stdin on the microcontroller, you may find that a CR
character on the host computer (also known as Enter, ^M, or \r) is transmitted
to the microcontroller without conversion, so os.Stdin returns a \r character
instead of the expected \n (also known as ^J, NL, or LF) to indicate
end-of-line. You may be able to get around this problem by hitting Control-J in
tinygo monitor to transmit the \n end-of-line character.`
usageGdb = `Build the program, optionally flash it to a microcontroller if it is a remote
target, and drop into a GDB shell. From there you can set breakpoints, start the
program with "run" or "continue" ("run" for a local program, continue for
on-chip debugging), single-step, show a backtrace, break and resume the program
with Ctrl-C/"continue", etc. You may need to install extra tools (like openocd
and arm-none-eabi-gdb) to be able to do this. Also, you may need a dedicated
debugger to be able to debug certain boards if no debugger is integrated. Some
boards (like the BBC micro:bit and most professional evaluation boards) have an
integrated debugger.`
usageClean = `Clean the cache directory, normally stored in $HOME/.cache/tinygo. This is not
normally needed.`
usageHelp = `Print a short summary of the available commands, plus a list of command flags.`
usageVersion = `Print the version of the command and the version of the used $GOROOT.`
usageEnv = `Print a list of environment variables that affect TinyGo (as a shell script).
If one or more variable names are given as arguments, env prints the value of
each on a new line.`
usageDefault = `TinyGo is a Go compiler for small places.
version: %s
usage: %s <command> [arguments]
commands:
build: compile packages and dependencies
run: compile and run immediately
test: test packages
flash: compile and flash to the device
gdb: run/flash and immediately enter GDB
lldb: run/flash and immediately enter LLDB
monitor: open communication port
ports: list available serial ports
env: list environment variables used during build
list: run go list using the TinyGo root
clean: empty cache directory (%s)
targets: list targets
info: show info for specified target
version: show version
help: print this help text`
)
var (
commandHelp = map[string]string{
"build": usageBuild,
"run": usageRun,
"flash": usageFlash,
"monitor": usageMonitor,
"gdb": usageGdb,
"clean": usageClean,
"help": usageHelp,
"version": usageVersion,
"env": usageEnv,
}
)
func usage(command string) {
val, ok := commandHelp[command]
if !ok {
fmt.Fprintf(os.Stderr, usageDefault, goenv.Version(), os.Args[0], goenv.Get("GOCACHE"))
if flag.Parsed() {
fmt.Fprintln(os.Stderr, "\nflags:")
flag.PrintDefaults()
}
fmt.Fprintln(os.Stderr, "\nfor more details, see https://tinygo.org/docs/reference/usage/")
} else {
fmt.Fprintln(os.Stderr, val)
}
}
func handleCompilerError(err error) {
@@ -1330,19 +1439,20 @@ func (m globalValuesFlag) Set(value string) error {
// parseGoLinkFlag parses the -ldflags parameter. Its primary purpose right now
// is the -X flag, for setting the value of global string variables.
func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
func parseGoLinkFlag(flagsString string) (map[string]map[string]string, string, error) {
set := flag.NewFlagSet("link", flag.ExitOnError)
globalVarValues := make(globalValuesFlag)
set.Var(globalVarValues, "X", "Set the value of the string variable to the given value.")
extLDFlags := set.String("extldflags", "", "additional flags to pass to external linker")
flags, err := shlex.Split(flagsString)
if err != nil {
return nil, err
return nil, "", err
}
err = set.Parse(flags)
if err != nil {
return nil, err
return nil, "", err
}
return map[string]map[string]string(globalVarValues), nil
return map[string]map[string]string(globalVarValues), *extLDFlags, nil
}
// getListOfPackages returns a standard list of packages for a given list that might
@@ -1392,6 +1502,7 @@ func main() {
var tags buildutil.TagsFlag
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
target := flag.String("target", "", "chip/board name or JSON target specification file")
buildMode := flag.String("buildmode", "", "build mode to use (default, c-shared)")
var stackSize uint64
flag.Func("stack-size", "goroutine stack size (if unknown at compile time)", func(s string) error {
size, err := bytesize.Parse(s)
@@ -1461,6 +1572,7 @@ func main() {
// Early command processing, before commands are interpreted by the Go flag
// library.
handleChdirFlag()
switch command {
case "clang", "ld.lld", "wasm-ld":
err := builder.RunTool(command, os.Args[2:]...)
@@ -1473,7 +1585,7 @@ func main() {
}
flag.CommandLine.Parse(os.Args[2:])
globalVarValues, err := parseGoLinkFlag(*ldflags)
globalVarValues, extLDFlags, err := parseGoLinkFlag(*ldflags)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
@@ -1499,6 +1611,7 @@ func main() {
GOARM: goenv.Get("GOARM"),
GOMIPS: goenv.Get("GOMIPS"),
Target: *target,
BuildMode: *buildMode,
StackSize: stackSize,
Opt: *opt,
GC: *gc,
@@ -1533,6 +1646,14 @@ func main() {
options.PrintCommands = printCommand
}
if extLDFlags != "" {
options.ExtLDFlags, err = shlex.Split(extLDFlags)
if err != nil {
fmt.Fprintln(os.Stderr, "could not parse -extldflags:", err)
os.Exit(1)
}
}
err = options.Verify()
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
@@ -1946,3 +2067,56 @@ type outputEntry struct {
stderr bool
data []byte
}
// handleChdirFlag handles the -C flag before doing anything else.
// The -C flag must be the first flag on the command line, to make it easy to find
// even with commands that have custom flag parsing.
// handleChdirFlag handles the flag by chdir'ing to the directory
// and then removing that flag from the command line entirely.
//
// We have to handle the -C flag this way for two reasons:
//
// 1. Toolchain selection needs to be in the right directory to look for go.mod and go.work.
//
// 2. A toolchain switch later on reinvokes the new go command with the same arguments.
// The parent toolchain has already done the chdir; the child must not try to do it again.
func handleChdirFlag() {
used := 2 // b.c. command at os.Args[1]
if used >= len(os.Args) {
return
}
var dir string
switch a := os.Args[used]; {
default:
return
case a == "-C", a == "--C":
if used+1 >= len(os.Args) {
return
}
dir = os.Args[used+1]
os.Args = slicesDelete(os.Args, used, used+2)
case strings.HasPrefix(a, "-C="), strings.HasPrefix(a, "--C="):
_, dir, _ = strings.Cut(a, "=")
os.Args = slicesDelete(os.Args, used, used+1)
}
if err := os.Chdir(dir); err != nil {
fmt.Fprintln(os.Stderr, "cannot chdir:", err)
os.Exit(1)
}
}
// go1.19 compatibility: lacks slices package
func slicesDelete[S ~[]E, E any](s S, i, j int) S {
_ = s[i:j:len(s)] // bounds check
if i == j {
return s
}
return append(s[:i], s[j:]...)
}
+416 -36
View File
@@ -6,6 +6,8 @@ package main
import (
"bufio"
"bytes"
"context"
"crypto/sha256"
"errors"
"flag"
"io"
@@ -14,16 +16,19 @@ import (
"reflect"
"regexp"
"runtime"
"slices"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/aykevl/go-wasm"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
"github.com/tetratelabs/wazero/sys"
"github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/diagnostics"
"github.com/tinygo-org/tinygo/goenv"
)
@@ -75,6 +80,7 @@ func TestBuild(t *testing.T) {
"oldgo/",
"print.go",
"reflect.go",
"signal.go",
"slice.go",
"sort.go",
"stdlib.go",
@@ -109,10 +115,15 @@ func TestBuild(t *testing.T) {
return
}
t.Run("Host", func(t *testing.T) {
t.Parallel()
runPlatTests(optionsFromTarget("", sema), tests, t)
t.Run("Debugging", func(t *testing.T) {
for i := 0; i < 5; i++ {
t.Run(strconv.Itoa(i), func(t *testing.T) {
options := optionsFromTarget("", sema)
runTest("alias.go", options, t, nil, nil)
})
}
})
return
// Test a few build options.
t.Run("build-options", func(t *testing.T) {
@@ -213,6 +224,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
// isWebAssembly := strings.HasPrefix(spec.Triple, "wasm")
isWASI := strings.HasPrefix(options.Target, "wasi")
isWebAssembly := isWASI || strings.HasPrefix(options.Target, "wasm") || (options.Target == "" && strings.HasPrefix(options.GOARCH, "wasm"))
isBaremetal := options.Target == "simavr" || options.Target == "cortex-m-qemu" || options.Target == "riscv-qemu"
for _, name := range tests {
if options.GOOS == "linux" && (options.GOARCH == "arm" || options.GOARCH == "386") {
@@ -277,6 +289,13 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
continue
}
}
if isWebAssembly || isBaremetal || options.GOOS == "windows" {
switch name {
case "signal.go":
// Signals only work on POSIX-like systems.
continue
}
}
name := name // redefine to avoid race condition
t.Run(name, func(t *testing.T) {
@@ -392,17 +411,13 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
// of the path.
path := TESTDATA + "/" + name
// Get the expected output for this test.
txtpath := path[:len(path)-3] + ".txt"
expectedOutputPath := path[:len(path)-3] + ".txt"
pkgName := "./" + path
if path[len(path)-1] == '/' {
txtpath = path + "out.txt"
expectedOutputPath = path + "out.txt"
options.Directory = path
pkgName = "."
}
expected, err := os.ReadFile(txtpath)
if err != nil {
t.Fatal("could not read expected output file:", err)
}
config, err := builder.NewConfig(&options)
if err != nil {
@@ -411,23 +426,68 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
// Build the test binary.
stdout := &bytes.Buffer{}
_, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
return cmd.Run()
})
_, fileExt := config.EmulatorFormat()
tmpdir := t.TempDir()
result, err := builder.Build(pkgName, fileExt, tmpdir, config)
if err != nil {
w := &bytes.Buffer{}
diagnostics.CreateDiagnostics(err).WriteTo(w, "")
for _, line := range strings.Split(strings.TrimRight(w.String(), "\n"), "\n") {
t.Log(line)
}
t.Fail()
return
t.Fatal("build failed:", err)
}
// putchar() prints CRLF, convert it to LF.
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
data, err := os.ReadFile(result.Executable)
if err != nil {
t.Fatal("failed to read executable:", err)
}
hash := sha256.Sum256(data)
t.Logf("executable hash and size: %d %x", len(data), hash)
for i := 0; i < 100; i++ {
i := i
t.Run(strconv.Itoa(i), func(t *testing.T) {
t.Parallel()
stdout := &bytes.Buffer{}
cmd := exec.Command(result.Executable)
cmd.Stdout = stdout
cmd.Stderr = stdout
err := cmd.Run()
if err != nil {
t.Log("run error:", err)
}
checkOutput(t, expectedOutputPath, stdout.Bytes())
})
}
return
//_, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
// data, err := os.ReadFile(result.Executable)
// if err != nil {
// t.Fatal("failed to read executable:", err)
// }
// hash := sha256.Sum256(data)
// t.Logf("executable hash and size: %d %x", len(data), hash)
// t.Log("command:", cmd)
// err = cmd.Run()
// if err == nil {
// t.Log(" error is nil!")
// } else {
// t.Log(" error:", err)
// }
// return err
//})
//if err != nil {
// w := &bytes.Buffer{}
// diagnostics.CreateDiagnostics(err).WriteTo(w, "")
// for _, line := range strings.Split(strings.TrimRight(w.String(), "\n"), "\n") {
// t.Log(line)
// }
// if stdout.Len() != 0 {
// t.Logf("output:\n%s", stdout.String())
// }
// t.Fail()
// return
//}
actual := stdout.Bytes()
if config.EmulatorName() == "simavr" {
// Strip simavr log formatting.
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
@@ -442,17 +502,12 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
}
// Check whether the command ran successfully.
fail := false
if err != nil {
t.Log("failed to run:", err)
fail = true
} else if !bytes.Equal(expected, actual) {
t.Logf("output did not match (expected %d bytes, got %d bytes):", len(expected), len(actual))
t.Logf(string(Diff("expected", expected, "actual", actual)))
fail = true
t.Error("failed to run:", err)
}
checkOutput(t, expectedOutputPath, actual)
if fail {
if t.Failed() {
r := bufio.NewReader(bytes.NewReader(actual))
for {
line, err := r.ReadString('\n')
@@ -470,20 +525,21 @@ func TestWebAssembly(t *testing.T) {
t.Parallel()
type testCase struct {
name string
target string
panicStrategy string
imports []string
}
for _, tc := range []testCase{
// Test whether there really are no imports when using -panic=trap. This
// tests the bugfix for https://github.com/tinygo-org/tinygo/issues/4161.
{name: "panic-default", imports: []string{"wasi_snapshot_preview1.fd_write"}},
{name: "panic-trap", panicStrategy: "trap", imports: []string{}},
{name: "panic-default", target: "wasip1", imports: []string{"wasi_snapshot_preview1.fd_write", "wasi_snapshot_preview1.random_get"}},
{name: "panic-trap", target: "wasm-unknown", panicStrategy: "trap", imports: []string{}},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
tmpdir := t.TempDir()
options := optionsFromTarget("wasi", sema)
options := optionsFromTarget(tc.target, sema)
options.PanicStrategy = tc.panicStrategy
config, err := builder.NewConfig(&options)
if err != nil {
@@ -515,7 +571,7 @@ func TestWebAssembly(t *testing.T) {
}
}
}
if !slices.Equal(imports, tc.imports) {
if !stringSlicesEqual(imports, tc.imports) {
t.Errorf("import list not as expected!\nexpected: %v\nactual: %v", tc.imports, imports)
}
}
@@ -523,6 +579,330 @@ func TestWebAssembly(t *testing.T) {
}
}
func stringSlicesEqual(s1, s2 []string) bool {
// We can use slices.Equal once we drop support for Go 1.20 (it was added in
// Go 1.21).
if len(s1) != len(s2) {
return false
}
for i, s := range s1 {
if s != s2[i] {
return false
}
}
return true
}
func TestWasmExport(t *testing.T) {
t.Parallel()
type testCase struct {
name string
target string
buildMode string
scheduler string
file string
noOutput bool
command bool // call _start (command mode) instead of _initialize
}
tests := []testCase{
// "command mode" WASI
{
name: "WASIp1-command",
target: "wasip1",
command: true,
},
// "reactor mode" WASI (with -buildmode=c-shared)
{
name: "WASIp1-reactor",
target: "wasip1",
buildMode: "c-shared",
},
// Make sure reactor mode also works without a scheduler.
{
name: "WASIp1-reactor-noscheduler",
target: "wasip1",
buildMode: "c-shared",
scheduler: "none",
file: "wasmexport-noscheduler.go",
},
// Test -target=wasm-unknown with the default build mode (which is
// c-shared).
{
name: "wasm-unknown-reactor",
target: "wasm-unknown",
file: "wasmexport-noscheduler.go",
noOutput: true, // wasm-unknown cannot produce output
},
// Test -target=wasm-unknown with -buildmode=default, which makes it run
// in command mode.
{
name: "wasm-unknown-command",
target: "wasm-unknown",
buildMode: "default",
file: "wasmexport-noscheduler.go",
noOutput: true, // wasm-unknown cannot produce output
command: true,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// Build the wasm binary.
tmpdir := t.TempDir()
options := optionsFromTarget(tc.target, sema)
options.BuildMode = tc.buildMode
options.Scheduler = tc.scheduler
buildConfig, err := builder.NewConfig(&options)
if err != nil {
t.Fatal(err)
}
filename := "wasmexport.go"
if tc.file != "" {
filename = tc.file
}
result, err := builder.Build("testdata/"+filename, ".wasm", tmpdir, buildConfig)
if err != nil {
t.Fatal("failed to build binary:", err)
}
// Read the wasm binary back into memory.
data, err := os.ReadFile(result.Binary)
if err != nil {
t.Fatal("could not read wasm binary: ", err)
}
// Set up the wazero runtime.
output := &bytes.Buffer{}
ctx := context.Background()
r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
defer r.Close(ctx)
config := wazero.NewModuleConfig().
WithStdout(output).WithStderr(output).
WithStartFunctions()
// Prepare for testing.
var mod api.Module
mustCall := func(results []uint64, err error) []uint64 {
if err != nil {
t.Error("failed to run function:", err)
}
return results
}
checkResult := func(name string, results []uint64, expected []uint64) {
if len(results) != len(expected) {
t.Errorf("%s: expected %v but got %v", name, expected, results)
}
for i, result := range results {
if result != expected[i] {
t.Errorf("%s: expected %v but got %v", name, expected, results)
break
}
}
}
runTests := func() {
// Test an exported function without params or return value.
checkResult("hello()", mustCall(mod.ExportedFunction("hello").Call(ctx)), nil)
// Test that we can call an exported function more than once.
checkResult("add(3, 5)", mustCall(mod.ExportedFunction("add").Call(ctx, 3, 5)), []uint64{8})
checkResult("add(7, 9)", mustCall(mod.ExportedFunction("add").Call(ctx, 7, 9)), []uint64{16})
checkResult("add(6, 1)", mustCall(mod.ExportedFunction("add").Call(ctx, 6, 1)), []uint64{7})
// Test that imported functions can call exported functions
// again.
checkResult("reentrantCall(2, 3)", mustCall(mod.ExportedFunction("reentrantCall").Call(ctx, 2, 3)), []uint64{5})
checkResult("reentrantCall(1, 8)", mustCall(mod.ExportedFunction("reentrantCall").Call(ctx, 1, 8)), []uint64{9})
}
// Add wasip1 module.
wasi_snapshot_preview1.MustInstantiate(ctx, r)
// Add custom "tester" module.
callOutside := func(a, b int32) int32 {
results, err := mod.ExportedFunction("add").Call(ctx, uint64(a), uint64(b))
if err != nil {
t.Error("could not call exported add function:", err)
}
return int32(results[0])
}
callTestMain := func() {
runTests()
}
builder := r.NewHostModuleBuilder("tester")
builder.NewFunctionBuilder().WithFunc(callOutside).Export("callOutside")
builder.NewFunctionBuilder().WithFunc(callTestMain).Export("callTestMain")
_, err = builder.Instantiate(ctx)
if err != nil {
t.Fatal(err)
}
// Parse and instantiate the wasm.
mod, err = r.InstantiateWithConfig(ctx, data, config)
if err != nil {
t.Fatal("could not instantiate wasm module:", err)
}
// Initialize the module and run the tests.
if tc.command {
// Call _start (the entry point), which calls
// tester.callTestMain, which then runs all the tests.
_, err := mod.ExportedFunction("_start").Call(ctx)
if err != nil {
if exitErr, ok := err.(*sys.ExitError); ok && exitErr.ExitCode() == 0 {
// Exited with code 0. Nothing to worry about.
} else {
t.Error("failed to run _start:", err)
}
}
} else {
// Run the _initialize call, because this is reactor mode wasm.
mustCall(mod.ExportedFunction("_initialize").Call(ctx))
runTests()
}
// Check that the output matches the expected output.
// (Skip this for wasm-unknown because it can't produce output).
if !tc.noOutput {
checkOutput(t, "testdata/wasmexport.txt", output.Bytes())
}
})
}
}
// Test js.FuncOf (for syscall/js).
// This test might be extended in the future to cover more cases in syscall/js.
func TestWasmFuncOf(t *testing.T) {
// Build the wasm binary.
tmpdir := t.TempDir()
options := optionsFromTarget("wasm", sema)
buildConfig, err := builder.NewConfig(&options)
if err != nil {
t.Fatal(err)
}
result, err := builder.Build("testdata/wasmfunc.go", ".wasm", tmpdir, buildConfig)
if err != nil {
t.Fatal("failed to build binary:", err)
}
// Test the resulting binary using NodeJS.
output := &bytes.Buffer{}
cmd := exec.Command("node", "testdata/wasmfunc.js", result.Binary, buildConfig.BuildMode())
cmd.Stdout = output
cmd.Stderr = output
err = cmd.Run()
if err != nil {
t.Error("failed to run node:", err)
}
checkOutput(t, "testdata/wasmfunc.txt", output.Bytes())
}
// Test //go:wasmexport in JavaScript (using NodeJS).
func TestWasmExportJS(t *testing.T) {
t.Parallel()
type testCase struct {
name string
buildMode string
}
tests := []testCase{
{name: "default"},
{name: "c-shared", buildMode: "c-shared"},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// Build the wasm binary.
tmpdir := t.TempDir()
options := optionsFromTarget("wasm", sema)
options.BuildMode = tc.buildMode
buildConfig, err := builder.NewConfig(&options)
if err != nil {
t.Fatal(err)
}
result, err := builder.Build("testdata/wasmexport-noscheduler.go", ".wasm", tmpdir, buildConfig)
if err != nil {
t.Fatal("failed to build binary:", err)
}
// Test the resulting binary using NodeJS.
output := &bytes.Buffer{}
cmd := exec.Command("node", "testdata/wasmexport.js", result.Binary, buildConfig.BuildMode())
cmd.Stdout = output
cmd.Stderr = output
err = cmd.Run()
if err != nil {
t.Error("failed to run node:", err)
}
checkOutput(t, "testdata/wasmexport.txt", output.Bytes())
})
}
}
// Test whether Go.run() (in wasm_exec.js) normally returns and returns the
// right exit code.
func TestWasmExit(t *testing.T) {
t.Parallel()
type testCase struct {
name string
output string
}
tests := []testCase{
{name: "normal", output: "exit code: 0\n"},
{name: "exit-0", output: "exit code: 0\n"},
{name: "exit-0-sleep", output: "slept\nexit code: 0\n"},
{name: "exit-1", output: "exit code: 1\n"},
{name: "exit-1-sleep", output: "slept\nexit code: 1\n"},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
options := optionsFromTarget("wasm", sema)
buildConfig, err := builder.NewConfig(&options)
if err != nil {
t.Fatal(err)
}
buildConfig.Target.Emulator = "node testdata/wasmexit.js {}"
output := &bytes.Buffer{}
_, err = buildAndRun("testdata/wasmexit.go", buildConfig, output, []string{tc.name}, nil, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
return cmd.Run()
})
if err != nil {
t.Error(err)
}
expected := "wasmexit test: " + tc.name + "\n" + tc.output
checkOutputData(t, []byte(expected), output.Bytes())
})
}
}
// Check whether the output of a test equals the expected output.
func checkOutput(t *testing.T, filename string, actual []byte) {
expectedOutput, err := os.ReadFile(filename)
if err != nil {
t.Fatal("could not read output file:", err)
}
checkOutputData(t, expectedOutput, actual)
}
func checkOutputData(t *testing.T, expectedOutput, actual []byte) {
expectedOutput = bytes.ReplaceAll(expectedOutput, []byte("\r\n"), []byte("\n"))
actual = bytes.ReplaceAll(actual, []byte("\r\n"), []byte("\n"))
if !bytes.Equal(actual, expectedOutput) {
t.Errorf("output did not match (expected %d bytes, got %d bytes):", len(expectedOutput), len(actual))
t.Error(string(Diff("expected", expectedOutput, "actual", actual)))
}
}
func TestTest(t *testing.T) {
t.Parallel()
+185
View File
@@ -0,0 +1,185 @@
package macos
import (
"errors"
"time"
)
// Exported symbols copied from Big Go, but stripped of functionality.
// Allows building of crypto/x509 on macOS.
const (
ErrSecCertificateExpired = -67818
ErrSecHostNameMismatch = -67602
ErrSecNotTrusted = -67843
)
var ErrNoTrustSettings = errors.New("no trust settings found")
var SecPolicyAppleSSL = StringToCFString("1.2.840.113635.100.1.3") // defined by POLICYMACRO
var SecPolicyOid = StringToCFString("SecPolicyOid")
var SecTrustSettingsPolicy = StringToCFString("kSecTrustSettingsPolicy")
var SecTrustSettingsPolicyString = StringToCFString("kSecTrustSettingsPolicyString")
var SecTrustSettingsResultKey = StringToCFString("kSecTrustSettingsResult")
func CFArrayAppendValue(array CFRef, val CFRef) {}
func CFArrayGetCount(array CFRef) int {
return 0
}
func CFDataGetBytePtr(data CFRef) uintptr {
return 0
}
func CFDataGetLength(data CFRef) int {
return 0
}
func CFDataToSlice(data CFRef) []byte {
return nil
}
func CFEqual(a, b CFRef) bool {
return false
}
func CFErrorGetCode(errRef CFRef) int {
return 0
}
func CFNumberGetValue(num CFRef) (int32, error) {
return 0, errors.New("not implemented")
}
func CFRelease(ref CFRef) {}
func CFStringToString(ref CFRef) string {
return ""
}
func ReleaseCFArray(array CFRef) {}
func SecCertificateCopyData(cert CFRef) ([]byte, error) {
return nil, errors.New("not implemented")
}
func SecTrustEvaluateWithError(trustObj CFRef) (int, error) {
return 0, errors.New("not implemented")
}
func SecTrustGetCertificateCount(trustObj CFRef) int {
return 0
}
func SecTrustGetResult(trustObj CFRef, result CFRef) (CFRef, CFRef, error) {
return 0, 0, errors.New("not implemented")
}
func SecTrustSetVerifyDate(trustObj CFRef, dateRef CFRef) error {
return errors.New("not implemented")
}
type CFRef uintptr
func BytesToCFData(b []byte) CFRef {
return 0
}
func CFArrayCreateMutable() CFRef {
return 0
}
func CFArrayGetValueAtIndex(array CFRef, index int) CFRef {
return 0
}
func CFDateCreate(seconds float64) CFRef {
return 0
}
func CFDictionaryGetValueIfPresent(dict CFRef, key CFString) (value CFRef, ok bool) {
return 0, false
}
func CFErrorCopyDescription(errRef CFRef) CFRef {
return 0
}
func CFStringCreateExternalRepresentation(strRef CFRef) (CFRef, error) {
return 0, errors.New("not implemented")
}
func SecCertificateCreateWithData(b []byte) (CFRef, error) {
return 0, errors.New("not implemented")
}
func SecPolicyCreateSSL(name string) (CFRef, error) {
return 0, errors.New("not implemented")
}
func SecTrustCreateWithCertificates(certs CFRef, policies CFRef) (CFRef, error) {
return 0, errors.New("not implemented")
}
func SecTrustEvaluate(trustObj CFRef) (CFRef, error) {
return 0, errors.New("not implemented")
}
func SecTrustGetCertificateAtIndex(trustObj CFRef, i int) (CFRef, error) {
return 0, errors.New("not implemented")
}
func SecTrustSettingsCopyCertificates(domain SecTrustSettingsDomain) (certArray CFRef, err error) {
return 0, errors.New("not implemented")
}
func SecTrustSettingsCopyTrustSettings(cert CFRef, domain SecTrustSettingsDomain) (trustSettings CFRef, err error) {
return 0, errors.New("not implemented")
}
func TimeToCFDateRef(t time.Time) CFRef {
return 0
}
type CFString CFRef
func StringToCFString(s string) CFString {
return 0
}
type OSStatus struct {
// Has unexported fields.
}
func (s OSStatus) Error() string
type SecTrustResultType int32
const (
SecTrustResultInvalid SecTrustResultType = iota
SecTrustResultProceed
SecTrustResultConfirm // deprecated
SecTrustResultDeny
SecTrustResultUnspecified
SecTrustResultRecoverableTrustFailure
SecTrustResultFatalTrustFailure
SecTrustResultOtherError
)
type SecTrustSettingsDomain int32
const (
SecTrustSettingsDomainUser SecTrustSettingsDomain = iota
SecTrustSettingsDomainAdmin
SecTrustSettingsDomainSystem
)
type SecTrustSettingsResult int32
const (
SecTrustSettingsResultInvalid SecTrustSettingsResult = iota
SecTrustSettingsResultTrustRoot
SecTrustSettingsResultTrustAsRoot
SecTrustSettingsResultDeny
SecTrustSettingsResultUnspecified
)
+5
View File
@@ -30,3 +30,8 @@ func (littleEndian) AppendUint16(b []byte, v uint16) []byte {
func (littleEndian) Uint32(b []byte) uint32 {
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
}
func (littleEndian) Uint64(b []byte) uint64 {
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
}
+28 -4
View File
@@ -2,6 +2,11 @@ package cm
import "unsafe"
// AnyInteger is a type constraint for any integer type.
type AnyInteger interface {
~int | ~uint | ~uintptr | ~int8 | ~uint8 | ~int16 | ~uint16 | ~int32 | ~uint32 | ~int64 | ~uint64
}
// Reinterpret reinterprets the bits of type From into type T.
// Will panic if the size of From is smaller than the size of To.
func Reinterpret[T, From any](from From) (to T) {
@@ -19,19 +24,19 @@ func LowerString[S ~string](s S) (*byte, uint32) {
}
// LiftString lifts Core WebAssembly types into a [string].
func LiftString[T ~string, Data unsafe.Pointer | uintptr | *uint8, Len uint | uintptr | uint32 | uint64](data Data, len Len) T {
func LiftString[T ~string, Data unsafe.Pointer | uintptr | *uint8, Len AnyInteger](data Data, len Len) T {
return T(unsafe.String((*uint8)(unsafe.Pointer(data)), int(len)))
}
// LowerList lowers a [List] into a pair of Core WebAssembly types.
func LowerList[L ~struct{ list[T] }, T any](list L) (*T, uint32) {
func LowerList[L AnyList[T], T any](list L) (*T, uint32) {
l := (*List[T])(unsafe.Pointer(&list))
return l.data, uint32(l.len)
}
// LiftList lifts Core WebAssembly types into a [List].
func LiftList[L List[T], T any, Data unsafe.Pointer | uintptr | *T, Len uint | uintptr | uint32 | uint64](data Data, len Len) L {
return L(NewList((*T)(unsafe.Pointer(data)), uint(len)))
func LiftList[L AnyList[T], T any, Data unsafe.Pointer | uintptr | *T, Len AnyInteger](data Data, len Len) L {
return L(NewList((*T)(unsafe.Pointer(data)), len))
}
// BoolToU32 converts a value whose underlying type is [bool] into a [uint32].
@@ -84,6 +89,25 @@ func F64ToU64(v float64) uint64 { return *(*uint64)(unsafe.Pointer(&v)) }
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func U64ToF64(v uint64) float64 { return *(*float64)(unsafe.Pointer(&v)) }
// F32ToU64 maps the bits of a [float32] into a [uint64].
// Used to lower a [float32] into a Core WebAssembly i64 when required by the [Canonical ABI].
//
// [float32]: https://pkg.go.dev/builtin#float32
// [uint64]: https://pkg.go.dev/builtin#uint64
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func F32ToU64(v float32) uint64 { return uint64(*(*uint32)(unsafe.Pointer(&v))) }
// U64ToF32 maps the bits of a [uint64] into a [float32].
// Used to lift a Core WebAssembly i64 into a [float32] when required by the [Canonical ABI].
//
// [uint64]: https://pkg.go.dev/builtin#uint64
// [float32]: https://pkg.go.dev/builtin#float32
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func U64ToF32(v uint64) float32 {
truncated := uint32(v)
return *(*float32)(unsafe.Pointer(&truncated))
}
// PointerToU32 converts a pointer of type *T into a [uint32].
// Used to lower a pointer into a Core WebAssembly i32 as specified in the [Canonical ABI].
//
+11
View File
@@ -0,0 +1,11 @@
//go:build !go1.23
package cm
// HostLayout marks a struct as using host memory layout.
// See [structs.HostLayout] in Go 1.23 or later.
type HostLayout struct {
_ hostLayout // prevent accidental conversion with plain struct{}
}
type hostLayout struct{}
+9
View File
@@ -0,0 +1,9 @@
//go:build go1.23
package cm
import "structs"
// HostLayout marks a struct as using host memory layout.
// See [structs.HostLayout] in Go 1.23 or later.
type HostLayout = structs.HostLayout
+20 -8
View File
@@ -4,14 +4,25 @@ import "unsafe"
// List represents a Component Model list.
// The binary representation of list<T> is similar to a Go slice minus the cap field.
type List[T any] struct{ list[T] }
type List[T any] struct {
_ HostLayout
list[T]
}
// AnyList is a type constraint for generic functions that accept any [List] type.
type AnyList[T any] interface {
~struct {
_ HostLayout
list[T]
}
}
// NewList returns a List[T] from data and len.
func NewList[T any](data *T, len uint) List[T] {
func NewList[T any, Len AnyInteger](data *T, len Len) List[T] {
return List[T]{
list[T]{
list: list[T]{
data: data,
len: len,
len: uintptr(len),
},
}
}
@@ -20,15 +31,16 @@ func NewList[T any](data *T, len uint) List[T] {
// The underlying slice data is not copied, and the resulting List points at the
// same array storage as the slice.
func ToList[S ~[]T, T any](s S) List[T] {
return NewList[T](unsafe.SliceData([]T(s)), uint(len(s)))
return NewList[T](unsafe.SliceData([]T(s)), uintptr(len(s)))
}
// list represents the internal representation of a Component Model list.
// It is intended to be embedded in a [List], so embedding types maintain
// the methods defined on this type.
type list[T any] struct {
_ HostLayout
data *T
len uint
len uintptr
}
// Slice returns a Go slice representing the List.
@@ -42,7 +54,7 @@ func (l list[T]) Data() *T {
}
// Len returns the length of the list.
// TODO: should this return an int instead of a uint?
func (l list[T]) Len() uint {
// TODO: should this return an int instead of a uintptr?
func (l list[T]) Len() uintptr {
return l.len
}
+17 -2
View File
@@ -3,7 +3,10 @@ package cm
// Option represents a Component Model [option<T>] type.
//
// [option<T>]: https://component-model.bytecodealliance.org/design/wit.html#options
type Option[T any] struct{ option[T] }
type Option[T any] struct {
_ HostLayout
option[T]
}
// None returns an [Option] representing the none case,
// equivalent to the zero value.
@@ -14,7 +17,7 @@ func None[T any]() Option[T] {
// Some returns an [Option] representing the some case.
func Some[T any](v T) Option[T] {
return Option[T]{
option[T]{
option: option[T]{
isSome: true,
some: v,
},
@@ -25,6 +28,7 @@ func Some[T any](v T) Option[T] {
// The first byte is a bool representing none or some,
// followed by storage for the associated type T.
type option[T any] struct {
_ HostLayout
isSome bool
some T
}
@@ -42,3 +46,14 @@ func (o *option[T]) Some() *T {
}
return nil
}
// 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.
func (o option[T]) Value() T {
if !o.isSome {
var zero T
return zero
}
return o.some
}
+27 -5
View File
@@ -17,10 +17,22 @@ type BoolResult bool
// Result represents a result sized to hold the Shape type.
// The size of the Shape type must be greater than or equal to the size of OK and Err types.
// For results with two zero-length types, use [BoolResult].
type Result[Shape, OK, Err any] struct{ result[Shape, OK, Err] }
type Result[Shape, OK, Err any] struct {
_ HostLayout
result[Shape, OK, Err]
}
// AnyResult is a type constraint for generic functions that accept any [Result] type.
type AnyResult[Shape, OK, Err any] interface {
~struct {
_ HostLayout
result[Shape, OK, Err]
}
}
// result represents the internal representation of a Component Model result type.
type result[Shape, OK, Err any] struct {
_ HostLayout
isErr bool
_ [0]OK
_ [0]Err
@@ -59,6 +71,16 @@ func (r *result[Shape, OK, Err]) Err() *Err {
return (*Err)(unsafe.Pointer(&r.data))
}
// Result returns (OK, zero value of Err, false) if r represents the OK case,
// or (zero value of OK, Err, true) if r represents the error case.
// This does not have a pointer receiver, so it can be chained.
func (r result[Shape, OK, Err]) Result() (ok OK, err Err, isErr bool) {
if r.isErr {
return ok, *(*Err)(unsafe.Pointer(&r.data)), true
}
return *(*OK)(unsafe.Pointer(&r.data)), err, false
}
// This function is sized so it can be inlined and optimized away.
func (r *result[Shape, OK, Err]) validate() {
var shape Shape
@@ -88,8 +110,8 @@ func (r *result[Shape, OK, Err]) validate() {
// OK returns an OK result with shape Shape and type OK and Err.
// Pass Result[OK, OK, Err] or Result[Err, OK, Err] as the first type argument.
func OK[R ~struct{ result[Shape, OK, Err] }, Shape, OK, Err any](ok OK) R {
var r struct{ result[Shape, OK, Err] }
func OK[R AnyResult[Shape, OK, Err], Shape, OK, Err any](ok OK) R {
var r Result[Shape, OK, Err]
r.validate()
r.isErr = ResultOK
*((*OK)(unsafe.Pointer(&r.data))) = ok
@@ -98,8 +120,8 @@ func OK[R ~struct{ result[Shape, OK, Err] }, Shape, OK, Err any](ok OK) R {
// Err returns an error result with shape Shape and type OK and Err.
// Pass Result[OK, OK, Err] or Result[Err, OK, Err] as the first type argument.
func Err[R ~struct{ result[Shape, OK, Err] }, Shape, OK, Err any](err Err) R {
var r struct{ result[Shape, OK, Err] }
func Err[R AnyResult[Shape, OK, Err], Shape, OK, Err any](err Err) R {
var r Result[Shape, OK, Err]
r.validate()
r.isErr = ResultErr
*((*Err)(unsafe.Pointer(&r.data))) = err
+15
View File
@@ -4,6 +4,7 @@ package cm
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple[T0, T1 any] struct {
_ HostLayout
F0 T0
F1 T1
}
@@ -12,6 +13,7 @@ type Tuple[T0, T1 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple3[T0, T1, T2 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -21,6 +23,7 @@ type Tuple3[T0, T1, T2 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple4[T0, T1, T2, T3 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -31,6 +34,7 @@ type Tuple4[T0, T1, T2, T3 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple5[T0, T1, T2, T3, T4 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -42,6 +46,7 @@ type Tuple5[T0, T1, T2, T3, T4 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple6[T0, T1, T2, T3, T4, T5 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -54,6 +59,7 @@ type Tuple6[T0, T1, T2, T3, T4, T5 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple7[T0, T1, T2, T3, T4, T5, T6 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -67,6 +73,7 @@ type Tuple7[T0, T1, T2, T3, T4, T5, T6 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple8[T0, T1, T2, T3, T4, T5, T6, T7 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -81,6 +88,7 @@ type Tuple8[T0, T1, T2, T3, T4, T5, T6, T7 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple9[T0, T1, T2, T3, T4, T5, T6, T7, T8 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -96,6 +104,7 @@ type Tuple9[T0, T1, T2, T3, T4, T5, T6, T7, T8 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -112,6 +121,7 @@ type Tuple10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -129,6 +139,7 @@ type Tuple11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -147,6 +158,7 @@ type Tuple12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -166,6 +178,7 @@ type Tuple13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any] struct {
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -186,6 +199,7 @@ type Tuple14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any] str
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
@@ -207,6 +221,7 @@ type Tuple15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any] struct {
_ HostLayout
F0 T0
F1 T1
F2 T2
+15 -3
View File
@@ -12,7 +12,18 @@ type Discriminant interface {
// Variant represents a loosely-typed Component Model variant.
// Shape and Align must be non-zero sized types. To create a variant with no associated
// types, use an enum.
type Variant[Tag Discriminant, Shape, Align any] struct{ variant[Tag, Shape, Align] }
type Variant[Tag Discriminant, Shape, Align any] struct {
_ HostLayout
variant[Tag, Shape, Align]
}
// AnyVariant is a type constraint for generic functions that accept any [Variant] type.
type AnyVariant[Tag Discriminant, Shape, Align any] interface {
~struct {
_ HostLayout
variant[Tag, Shape, Align]
}
}
// NewVariant returns a [Variant] with tag of type Disc, storage and GC shape of type Shape,
// aligned to type Align, with a value of type T.
@@ -26,7 +37,7 @@ func NewVariant[Tag Discriminant, Shape, Align any, T any](tag Tag, data T) Vari
// New returns a [Variant] with tag of type Disc, storage and GC shape of type Shape,
// aligned to type Align, with a value of type T.
func New[V ~struct{ variant[Tag, Shape, Align] }, Tag Discriminant, Shape, Align any, T any](tag Tag, data T) V {
func New[V AnyVariant[Tag, Shape, Align], Tag Discriminant, Shape, Align any, T any](tag Tag, data T) V {
validateVariant[Tag, Shape, Align, T]()
var v variant[Tag, Shape, Align]
v.tag = tag
@@ -35,7 +46,7 @@ func New[V ~struct{ variant[Tag, Shape, Align] }, Tag Discriminant, Shape, Align
}
// Case returns a non-nil *T if the [Variant] case is equal to tag, otherwise it returns nil.
func Case[T any, V ~struct{ variant[Tag, Shape, Align] }, Tag Discriminant, Shape, Align any](v *V, tag Tag) *T {
func Case[T any, V AnyVariant[Tag, Shape, Align], Tag Discriminant, Shape, Align any](v *V, tag Tag) *T {
validateVariant[Tag, Shape, Align, T]()
v2 := (*variant[Tag, Shape, Align])(unsafe.Pointer(v))
if v2.tag == tag {
@@ -47,6 +58,7 @@ func Case[T any, V ~struct{ variant[Tag, Shape, Align] }, Tag Discriminant, Shap
// variant is the internal representation of a Component Model variant.
// Shape and Align must be non-zero sized types.
type variant[Tag Discriminant, Shape, Align any] struct {
_ HostLayout
tag Tag
_ [0]Align
data Shape // [unsafe.Sizeof(*(*Shape)(unsafe.Pointer(nil)))]byte
+33
View File
@@ -0,0 +1,33 @@
package gclayout
import "unsafe"
// Internal constants for gc layout
// See runtime/gc_precise.go
var (
NoPtrs unsafe.Pointer
Pointer unsafe.Pointer
String unsafe.Pointer
Slice unsafe.Pointer
)
func init() {
var sizeBits uintptr
switch unsafe.Sizeof(uintptr(0)) {
case 8:
sizeBits = 6
case 4:
sizeBits = 5
case 2:
sizeBits = 4
}
var sizeShift = sizeBits + 1
NoPtrs = unsafe.Pointer(uintptr(0b0<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
Pointer = unsafe.Pointer(uintptr(0b1<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
String = unsafe.Pointer(uintptr(0b01<<sizeShift) | uintptr(0b10<<1) | uintptr(1))
Slice = unsafe.Pointer(uintptr(0b001<<sizeShift) | uintptr(0b11<<1) | uintptr(1))
}
+3
View File
@@ -30,3 +30,6 @@ type Task struct {
// the given function and falls back to the default stack size. It is replaced
// with a load from a special section just before codegen.
func getGoroutineStackSize(fn uintptr) uintptr
//go:linkname runtime_alloc runtime.alloc
func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
+19 -11
View File
@@ -35,11 +35,16 @@ type state struct {
type stackState struct {
// asyncify is the stack pointer of the asyncify stack.
// This starts from the bottom and grows upwards.
asyncifysp uintptr
asyncifysp unsafe.Pointer
// asyncify is stack pointer of the C stack.
// This starts from the top and grows downwards.
csp uintptr
csp unsafe.Pointer
// Pointer to the first (lowest address) of the stack. It must never be
// overwritten. It can be checked from time to time to see whether a stack
// overflow happened in the past.
canaryPtr *uintptr
}
// start creates and starts a new goroutine with the given function and arguments.
@@ -63,12 +68,18 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
s.args = args
// Create a stack.
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
stack := runtime_alloc(stackSize, nil)
// Set up the stack canary, a random number that should be checked when
// switching from the task back to the scheduler. The stack canary pointer
// points to the first word of the stack. If it has changed between now and
// the next stack switch, there was a stack overflow.
s.canaryPtr = (*uintptr)(stack)
*s.canaryPtr = stackCanary
// Calculate stack base addresses.
s.asyncifysp = uintptr(unsafe.Pointer(&stack[0]))
s.csp = uintptr(unsafe.Pointer(&stack[0])) + uintptr(len(stack))*unsafe.Sizeof(uintptr(0))
stack[0] = stackCanary
s.asyncifysp = unsafe.Add(stack, unsafe.Sizeof(uintptr(0)))
s.csp = unsafe.Add(stack, stackSize)
}
//go:linkname runqueuePushBack runtime.runqueuePushBack
@@ -85,14 +96,11 @@ func Current() *Task {
// Pause suspends the current task and returns to the scheduler.
// This function may only be called when running on a goroutine stack, not when running on the system stack.
func Pause() {
// This is mildly unsafe but this is also the only place we can do this.
if *(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) != stackCanary {
if *currentTask.state.canaryPtr != stackCanary {
runtimePanic("stack overflow")
}
currentTask.state.unwind()
*(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) = stackCanary
}
//export tinygo_unwind
@@ -113,7 +121,7 @@ func (t *Task) Resume() {
}
currentTask = prevTask
t.gcData.swap()
if t.state.asyncifysp > t.state.csp {
if uintptr(t.state.asyncifysp) > uintptr(t.state.csp) {
runtimePanic("stack overflow")
}
}
-3
View File
@@ -104,9 +104,6 @@ var startTask [0]uint8
//go:linkname runqueuePushBack runtime.runqueuePushBack
func runqueuePushBack(*Task)
//go:linkname runtime_alloc runtime.alloc
func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
// start creates and starts a new goroutine with the given function and arguments.
// The new goroutine is scheduled to run later.
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,21 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package environment
import (
"internal/cm"
)
// 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])
//go:wasmimport wasi:cli/environment@0.2.0 get-arguments
//go:noescape
func wasmimport_GetArguments(result *cm.List[string])
//go:wasmimport wasi:cli/environment@0.2.0 initial-cwd
//go:noescape
func wasmimport_InitialCWD(result *cm.Option[string])
@@ -26,10 +26,6 @@ func GetEnvironment() (result cm.List[[2]string]) {
return
}
//go:wasmimport wasi:cli/environment@0.2.0 get-environment
//go:noescape
func wasmimport_GetEnvironment(result *cm.List[[2]string])
// GetArguments represents the imported function "get-arguments".
//
// Get the POSIX-style arguments to the program.
@@ -42,10 +38,6 @@ func GetArguments() (result cm.List[string]) {
return
}
//go:wasmimport wasi:cli/environment@0.2.0 get-arguments
//go:noescape
func wasmimport_GetArguments(result *cm.List[string])
// InitialCWD represents the imported function "initial-cwd".
//
// Return a path that programs should use as their initial current working
@@ -58,7 +50,3 @@ func InitialCWD() (result cm.Option[string]) {
wasmimport_InitialCWD(&result)
return
}
//go:wasmimport wasi:cli/environment@0.2.0 initial-cwd
//go:noescape
func wasmimport_InitialCWD(result *cm.Option[string])
+9
View File
@@ -0,0 +1,9 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package exit
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmimport wasi:cli/exit@0.2.0 exit
//go:noescape
func wasmimport_Exit(status0 uint32)
@@ -19,7 +19,3 @@ func Exit(status cm.BoolResult) {
wasmimport_Exit((uint32)(status0))
return
}
//go:wasmimport wasi:cli/exit@0.2.0 exit
//go:noescape
func wasmimport_Exit(status0 uint32)
+17
View File
@@ -0,0 +1,17 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package run
import (
"internal/cm"
)
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmexport wasi:cli/run@0.2.0#run
//export wasi:cli/run@0.2.0#run
func wasmexport_Run() (result0 uint32) {
result := Exports.Run()
result0 = cm.BoolToU32(result)
return
}
@@ -2,15 +2,3 @@
// Package run represents the exported interface "wasi:cli/run@0.2.0".
package run
import (
"internal/cm"
)
//go:wasmexport wasi:cli/run@0.2.0#run
//export wasi:cli/run@0.2.0#run
func wasmexport_Run() (result0 uint32) {
result := Exports.Run()
result0 = cm.BoolToU32(result)
return
}
+9
View File
@@ -0,0 +1,9 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package stderr
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmimport wasi:cli/stderr@0.2.0 get-stderr
//go:noescape
func wasmimport_GetStderr() (result0 uint32)
@@ -8,17 +8,18 @@ import (
"internal/wasi/io/v0.2.0/streams"
)
// OutputStream represents the imported type alias "wasi:cli/stderr@0.2.0#output-stream".
//
// See [streams.OutputStream] for more information.
type OutputStream = streams.OutputStream
// GetStderr represents the imported function "get-stderr".
//
// get-stderr: func() -> output-stream
//
//go:nosplit
func GetStderr() (result streams.OutputStream) {
func GetStderr() (result OutputStream) {
result0 := wasmimport_GetStderr()
result = cm.Reinterpret[streams.OutputStream]((uint32)(result0))
result = cm.Reinterpret[OutputStream]((uint32)(result0))
return
}
//go:wasmimport wasi:cli/stderr@0.2.0 get-stderr
//go:noescape
func wasmimport_GetStderr() (result0 uint32)
+9
View File
@@ -0,0 +1,9 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package stdin
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmimport wasi:cli/stdin@0.2.0 get-stdin
//go:noescape
func wasmimport_GetStdin() (result0 uint32)
@@ -8,17 +8,18 @@ import (
"internal/wasi/io/v0.2.0/streams"
)
// InputStream represents the imported type alias "wasi:cli/stdin@0.2.0#input-stream".
//
// See [streams.InputStream] for more information.
type InputStream = streams.InputStream
// GetStdin represents the imported function "get-stdin".
//
// get-stdin: func() -> input-stream
//
//go:nosplit
func GetStdin() (result streams.InputStream) {
func GetStdin() (result InputStream) {
result0 := wasmimport_GetStdin()
result = cm.Reinterpret[streams.InputStream]((uint32)(result0))
result = cm.Reinterpret[InputStream]((uint32)(result0))
return
}
//go:wasmimport wasi:cli/stdin@0.2.0 get-stdin
//go:noescape
func wasmimport_GetStdin() (result0 uint32)
+9
View File
@@ -0,0 +1,9 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package stdout
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmimport wasi:cli/stdout@0.2.0 get-stdout
//go:noescape
func wasmimport_GetStdout() (result0 uint32)
@@ -8,17 +8,18 @@ import (
"internal/wasi/io/v0.2.0/streams"
)
// OutputStream represents the imported type alias "wasi:cli/stdout@0.2.0#output-stream".
//
// See [streams.OutputStream] for more information.
type OutputStream = streams.OutputStream
// GetStdout represents the imported function "get-stdout".
//
// get-stdout: func() -> output-stream
//
//go:nosplit
func GetStdout() (result streams.OutputStream) {
func GetStdout() (result OutputStream) {
result0 := wasmimport_GetStdout()
result = cm.Reinterpret[streams.OutputStream]((uint32)(result0))
result = cm.Reinterpret[OutputStream]((uint32)(result0))
return
}
//go:wasmimport wasi:cli/stdout@0.2.0 get-stdout
//go:noescape
func wasmimport_GetStdout() (result0 uint32)
@@ -30,7 +30,3 @@ func (self TerminalInput) ResourceDrop() {
wasmimport_TerminalInputResourceDrop((uint32)(self0))
return
}
//go:wasmimport wasi:cli/terminal-input@0.2.0 [resource-drop]terminal-input
//go:noescape
func wasmimport_TerminalInputResourceDrop(self0 uint32)
@@ -0,0 +1,9 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package terminalinput
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmimport wasi:cli/terminal-input@0.2.0 [resource-drop]terminal-input
//go:noescape
func wasmimport_TerminalInputResourceDrop(self0 uint32)
@@ -30,7 +30,3 @@ func (self TerminalOutput) ResourceDrop() {
wasmimport_TerminalOutputResourceDrop((uint32)(self0))
return
}
//go:wasmimport wasi:cli/terminal-output@0.2.0 [resource-drop]terminal-output
//go:noescape
func wasmimport_TerminalOutputResourceDrop(self0 uint32)
@@ -0,0 +1,9 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package terminaloutput
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmimport wasi:cli/terminal-output@0.2.0 [resource-drop]terminal-output
//go:noescape
func wasmimport_TerminalOutputResourceDrop(self0 uint32)
@@ -11,6 +11,11 @@ import (
terminaloutput "internal/wasi/cli/v0.2.0/terminal-output"
)
// TerminalOutput represents the imported type alias "wasi:cli/terminal-stderr@0.2.0#terminal-output".
//
// See [terminaloutput.TerminalOutput] for more information.
type TerminalOutput = terminaloutput.TerminalOutput
// GetTerminalStderr represents the imported function "get-terminal-stderr".
//
// If stderr is connected to a terminal, return a `terminal-output` handle
@@ -19,11 +24,7 @@ import (
// get-terminal-stderr: func() -> option<terminal-output>
//
//go:nosplit
func GetTerminalStderr() (result cm.Option[terminaloutput.TerminalOutput]) {
func GetTerminalStderr() (result cm.Option[TerminalOutput]) {
wasmimport_GetTerminalStderr(&result)
return
}
//go:wasmimport wasi:cli/terminal-stderr@0.2.0 get-terminal-stderr
//go:noescape
func wasmimport_GetTerminalStderr(result *cm.Option[terminaloutput.TerminalOutput])
@@ -0,0 +1,13 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package terminalstderr
import (
"internal/cm"
)
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmimport wasi:cli/terminal-stderr@0.2.0 get-terminal-stderr
//go:noescape
func wasmimport_GetTerminalStderr(result *cm.Option[TerminalOutput])
@@ -11,6 +11,11 @@ import (
terminalinput "internal/wasi/cli/v0.2.0/terminal-input"
)
// TerminalInput represents the imported type alias "wasi:cli/terminal-stdin@0.2.0#terminal-input".
//
// See [terminalinput.TerminalInput] for more information.
type TerminalInput = terminalinput.TerminalInput
// GetTerminalStdin represents the imported function "get-terminal-stdin".
//
// If stdin is connected to a terminal, return a `terminal-input` handle
@@ -19,11 +24,7 @@ import (
// get-terminal-stdin: func() -> option<terminal-input>
//
//go:nosplit
func GetTerminalStdin() (result cm.Option[terminalinput.TerminalInput]) {
func GetTerminalStdin() (result cm.Option[TerminalInput]) {
wasmimport_GetTerminalStdin(&result)
return
}
//go:wasmimport wasi:cli/terminal-stdin@0.2.0 get-terminal-stdin
//go:noescape
func wasmimport_GetTerminalStdin(result *cm.Option[terminalinput.TerminalInput])
@@ -0,0 +1,13 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package terminalstdin
import (
"internal/cm"
)
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmimport wasi:cli/terminal-stdin@0.2.0 get-terminal-stdin
//go:noescape
func wasmimport_GetTerminalStdin(result *cm.Option[TerminalInput])
@@ -11,6 +11,11 @@ import (
terminaloutput "internal/wasi/cli/v0.2.0/terminal-output"
)
// TerminalOutput represents the imported type alias "wasi:cli/terminal-stdout@0.2.0#terminal-output".
//
// See [terminaloutput.TerminalOutput] for more information.
type TerminalOutput = terminaloutput.TerminalOutput
// GetTerminalStdout represents the imported function "get-terminal-stdout".
//
// If stdout is connected to a terminal, return a `terminal-output` handle
@@ -19,11 +24,7 @@ import (
// get-terminal-stdout: func() -> option<terminal-output>
//
//go:nosplit
func GetTerminalStdout() (result cm.Option[terminaloutput.TerminalOutput]) {
func GetTerminalStdout() (result cm.Option[TerminalOutput]) {
wasmimport_GetTerminalStdout(&result)
return
}
//go:wasmimport wasi:cli/terminal-stdout@0.2.0 get-terminal-stdout
//go:noescape
func wasmimport_GetTerminalStdout(result *cm.Option[terminaloutput.TerminalOutput])
@@ -0,0 +1,13 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package terminalstdout
import (
"internal/cm"
)
// This file contains wasmimport and wasmexport declarations for "wasi:cli@0.2.0".
//go:wasmimport wasi:cli/terminal-stdout@0.2.0 get-terminal-stdout
//go:noescape
func wasmimport_GetTerminalStdout(result *cm.Option[TerminalOutput])
@@ -19,6 +19,11 @@ import (
"internal/wasi/io/v0.2.0/poll"
)
// Pollable represents the imported type alias "wasi:clocks/monotonic-clock@0.2.0#pollable".
//
// See [poll.Pollable] for more information.
type Pollable = poll.Pollable
// Instant represents the u64 "wasi:clocks/monotonic-clock@0.2.0#instant".
//
// An instant in time, in nanoseconds. An instant is relative to an
@@ -51,10 +56,6 @@ func Now() (result Instant) {
return
}
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 now
//go:noescape
func wasmimport_Now() (result0 uint64)
// Resolution represents the imported function "resolution".
//
// Query the resolution of the clock. Returns the duration of time
@@ -69,10 +70,6 @@ func Resolution() (result Duration) {
return
}
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 resolution
//go:noescape
func wasmimport_Resolution() (result0 uint64)
// SubscribeInstant represents the imported function "subscribe-instant".
//
// Create a `pollable` which will resolve once the specified instant
@@ -81,17 +78,13 @@ func wasmimport_Resolution() (result0 uint64)
// subscribe-instant: func(when: instant) -> pollable
//
//go:nosplit
func SubscribeInstant(when Instant) (result poll.Pollable) {
func SubscribeInstant(when Instant) (result Pollable) {
when0 := (uint64)(when)
result0 := wasmimport_SubscribeInstant((uint64)(when0))
result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
result = cm.Reinterpret[Pollable]((uint32)(result0))
return
}
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 subscribe-instant
//go:noescape
func wasmimport_SubscribeInstant(when0 uint64) (result0 uint32)
// SubscribeDuration represents the imported function "subscribe-duration".
//
// Create a `pollable` which will resolve once the given duration has
@@ -101,13 +94,9 @@ func wasmimport_SubscribeInstant(when0 uint64) (result0 uint32)
// subscribe-duration: func(when: duration) -> pollable
//
//go:nosplit
func SubscribeDuration(when Duration) (result poll.Pollable) {
func SubscribeDuration(when Duration) (result Pollable) {
when0 := (uint64)(when)
result0 := wasmimport_SubscribeDuration((uint64)(when0))
result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
result = cm.Reinterpret[Pollable]((uint32)(result0))
return
}
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 subscribe-duration
//go:noescape
func wasmimport_SubscribeDuration(when0 uint64) (result0 uint32)
@@ -0,0 +1,21 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package monotonicclock
// This file contains wasmimport and wasmexport declarations for "wasi:clocks@0.2.0".
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 now
//go:noescape
func wasmimport_Now() (result0 uint64)
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 resolution
//go:noescape
func wasmimport_Resolution() (result0 uint64)
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 subscribe-instant
//go:noescape
func wasmimport_SubscribeInstant(when0 uint64) (result0 uint32)
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 subscribe-duration
//go:noescape
func wasmimport_SubscribeDuration(when0 uint64) (result0 uint32)
@@ -18,6 +18,10 @@
// It is intended for reporting the current date and time for humans.
package wallclock
import (
"internal/cm"
)
// DateTime represents the record "wasi:clocks/wall-clock@0.2.0#datetime".
//
// A time and date in seconds plus nanoseconds.
@@ -27,6 +31,7 @@ package wallclock
// nanoseconds: u32,
// }
type DateTime struct {
_ cm.HostLayout
Seconds uint64
Nanoseconds uint32
}
@@ -55,10 +60,6 @@ func Now() (result DateTime) {
return
}
//go:wasmimport wasi:clocks/wall-clock@0.2.0 now
//go:noescape
func wasmimport_Now(result *DateTime)
// Resolution represents the imported function "resolution".
//
// Query the resolution of the clock.
@@ -72,7 +73,3 @@ func Resolution() (result DateTime) {
wasmimport_Resolution(&result)
return
}
//go:wasmimport wasi:clocks/wall-clock@0.2.0 resolution
//go:noescape
func wasmimport_Resolution(result *DateTime)
@@ -0,0 +1,13 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package wallclock
// This file contains wasmimport and wasmexport declarations for "wasi:clocks@0.2.0".
//go:wasmimport wasi:clocks/wall-clock@0.2.0 now
//go:noescape
func wasmimport_Now(result *DateTime)
//go:wasmimport wasi:clocks/wall-clock@0.2.0 resolution
//go:noescape
func wasmimport_Resolution(result *DateTime)
@@ -0,0 +1,13 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package preopens
import (
"internal/cm"
)
// 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]])
@@ -8,6 +8,11 @@ import (
"internal/wasi/filesystem/v0.2.0/types"
)
// Descriptor represents the imported type alias "wasi:filesystem/preopens@0.2.0#descriptor".
//
// See [types.Descriptor] for more information.
type Descriptor = types.Descriptor
// GetDirectories represents the imported function "get-directories".
//
// Return the set of preopened directories, and their path.
@@ -15,11 +20,7 @@ import (
// get-directories: func() -> list<tuple<descriptor, string>>
//
//go:nosplit
func GetDirectories() (result cm.List[cm.Tuple[types.Descriptor, string]]) {
func GetDirectories() (result cm.List[cm.Tuple[Descriptor, string]]) {
wasmimport_GetDirectories(&result)
return
}
//go:wasmimport wasi:filesystem/preopens@0.2.0 get-directories
//go:noescape
func wasmimport_GetDirectories(result *cm.List[cm.Tuple[types.Descriptor, string]])
@@ -8,18 +8,15 @@ import (
"unsafe"
)
// DateTimeShape is used for storage in variant or result types.
type DateTimeShape struct {
shape [unsafe.Sizeof(wallclock.DateTime{})]byte
}
// MetadataHashValueShape is used for storage in variant or result types.
type MetadataHashValueShape struct {
_ cm.HostLayout
shape [unsafe.Sizeof(MetadataHashValue{})]byte
}
// TupleListU8BoolShape is used for storage in variant or result types.
type TupleListU8BoolShape struct {
_ cm.HostLayout
shape [unsafe.Sizeof(cm.Tuple[cm.List[uint8], bool]{})]byte
}
@@ -42,10 +39,12 @@ func lower_NewTimestamp(v NewTimestamp) (f0 uint32, f1 uint64, f2 uint32) {
// DescriptorStatShape is used for storage in variant or result types.
type DescriptorStatShape struct {
_ cm.HostLayout
shape [unsafe.Sizeof(DescriptorStat{})]byte
}
// OptionDirectoryEntryShape is used for storage in variant or result types.
type OptionDirectoryEntryShape struct {
_ cm.HostLayout
shape [unsafe.Sizeof(cm.Option[DirectoryEntry]{})]byte
}
+133
View File
@@ -0,0 +1,133 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
package types
import (
"internal/cm"
)
// This file contains wasmimport and wasmexport declarations for "wasi:filesystem@0.2.0".
//go:wasmimport wasi:filesystem/types@0.2.0 [resource-drop]descriptor
//go:noescape
func wasmimport_DescriptorResourceDrop(self0 uint32)
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.advise
//go:noescape
func wasmimport_DescriptorAdvise(self0 uint32, offset0 uint64, length0 uint64, advice0 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.append-via-stream
//go:noescape
func wasmimport_DescriptorAppendViaStream(self0 uint32, result *cm.Result[OutputStream, OutputStream, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.create-directory-at
//go:noescape
func wasmimport_DescriptorCreateDirectoryAt(self0 uint32, path0 *uint8, path1 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.get-flags
//go:noescape
func wasmimport_DescriptorGetFlags(self0 uint32, result *cm.Result[DescriptorFlags, DescriptorFlags, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.get-type
//go:noescape
func wasmimport_DescriptorGetType(self0 uint32, result *cm.Result[DescriptorType, DescriptorType, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.is-same-object
//go:noescape
func wasmimport_DescriptorIsSameObject(self0 uint32, other0 uint32) (result0 uint32)
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.link-at
//go:noescape
func wasmimport_DescriptorLinkAt(self0 uint32, oldPathFlags0 uint32, oldPath0 *uint8, oldPath1 uint32, newDescriptor0 uint32, newPath0 *uint8, newPath1 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.metadata-hash
//go:noescape
func wasmimport_DescriptorMetadataHash(self0 uint32, result *cm.Result[MetadataHashValueShape, MetadataHashValue, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.metadata-hash-at
//go:noescape
func wasmimport_DescriptorMetadataHashAt(self0 uint32, pathFlags0 uint32, path0 *uint8, path1 uint32, result *cm.Result[MetadataHashValueShape, MetadataHashValue, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.open-at
//go:noescape
func wasmimport_DescriptorOpenAt(self0 uint32, pathFlags0 uint32, path0 *uint8, path1 uint32, openFlags0 uint32, flags0 uint32, result *cm.Result[Descriptor, Descriptor, ErrorCode])
//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])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.read-directory
//go:noescape
func wasmimport_DescriptorReadDirectory(self0 uint32, result *cm.Result[DirectoryEntryStream, DirectoryEntryStream, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.read-via-stream
//go:noescape
func wasmimport_DescriptorReadViaStream(self0 uint32, offset0 uint64, result *cm.Result[InputStream, InputStream, ErrorCode])
//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])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.remove-directory-at
//go:noescape
func wasmimport_DescriptorRemoveDirectoryAt(self0 uint32, path0 *uint8, path1 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.rename-at
//go:noescape
func wasmimport_DescriptorRenameAt(self0 uint32, oldPath0 *uint8, oldPath1 uint32, newDescriptor0 uint32, newPath0 *uint8, newPath1 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.set-size
//go:noescape
func wasmimport_DescriptorSetSize(self0 uint32, size0 uint64, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.set-times
//go:noescape
func wasmimport_DescriptorSetTimes(self0 uint32, dataAccessTimestamp0 uint32, dataAccessTimestamp1 uint64, dataAccessTimestamp2 uint32, dataModificationTimestamp0 uint32, dataModificationTimestamp1 uint64, dataModificationTimestamp2 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.set-times-at
//go:noescape
func wasmimport_DescriptorSetTimesAt(self0 uint32, pathFlags0 uint32, path0 *uint8, path1 uint32, dataAccessTimestamp0 uint32, dataAccessTimestamp1 uint64, dataAccessTimestamp2 uint32, dataModificationTimestamp0 uint32, dataModificationTimestamp1 uint64, dataModificationTimestamp2 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.stat
//go:noescape
func wasmimport_DescriptorStat(self0 uint32, result *cm.Result[DescriptorStatShape, DescriptorStat, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.stat-at
//go:noescape
func wasmimport_DescriptorStatAt(self0 uint32, pathFlags0 uint32, path0 *uint8, path1 uint32, result *cm.Result[DescriptorStatShape, DescriptorStat, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.symlink-at
//go:noescape
func wasmimport_DescriptorSymlinkAt(self0 uint32, oldPath0 *uint8, oldPath1 uint32, newPath0 *uint8, newPath1 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.sync
//go:noescape
func wasmimport_DescriptorSync(self0 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.sync-data
//go:noescape
func wasmimport_DescriptorSyncData(self0 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.unlink-file-at
//go:noescape
func wasmimport_DescriptorUnlinkFileAt(self0 uint32, path0 *uint8, path1 uint32, result *cm.Result[ErrorCode, struct{}, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.write
//go:noescape
func wasmimport_DescriptorWrite(self0 uint32, buffer0 *uint8, buffer1 uint32, offset0 uint64, result *cm.Result[uint64, FileSize, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.write-via-stream
//go:noescape
func wasmimport_DescriptorWriteViaStream(self0 uint32, offset0 uint64, result *cm.Result[OutputStream, OutputStream, ErrorCode])
//go:wasmimport wasi:filesystem/types@0.2.0 [resource-drop]directory-entry-stream
//go:noescape
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])
//go:wasmimport wasi:filesystem/types@0.2.0 filesystem-error-code
//go:noescape
func wasmimport_FilesystemErrorCode(err0 uint32, result *cm.Option[ErrorCode])

Some files were not shown because too many files have changed in this diff Show More