mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 22:58:41 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ff626dcee | |||
| 19e2d12517 | |||
| 6184a6cd35 | |||
| 1fd75ffbda | |||
| 61d36fb3f8 | |||
| b318a941a4 | |||
| 725518f007 | |||
| 04d1261f8a | |||
| 3e2230eadb | |||
| 4f908f4877 | |||
| 319683b863 | |||
| 4af3f618f8 | |||
| 89340f82dc | |||
| 824bf24445 | |||
| 80269b98ba | |||
| 3788b31ba5 | |||
| f4ce11ef37 | |||
| a5f78a3900 | |||
| e409950492 | |||
| 208eb1a817 | |||
| 2920492e0a | |||
| 87a8aafc4f | |||
| f026422b04 | |||
| fb91c7097f | |||
| d7773d3e86 | |||
| b04b690842 | |||
| 8a357af3d8 | |||
| 7ac1ca0ae2 | |||
| 2f3d821b13 | |||
| 6f462fba4c |
@@ -68,7 +68,7 @@ jobs:
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-${{ matrix.os }}-v2
|
||||
key: llvm-build-18-${{ matrix.os }}-v3
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-linux-alpine-v1
|
||||
key: llvm-build-18-linux-alpine-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -223,7 +223,7 @@ jobs:
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-linux-asserts-v1
|
||||
key: llvm-build-18-linux-asserts-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -336,7 +336,7 @@ jobs:
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-linux-${{ matrix.goarch }}-v1
|
||||
key: llvm-build-18-linux-${{ matrix.goarch }}-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
|
||||
@@ -9,7 +9,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
sizediff:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
uses: actions/cache/restore@v4
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-18-windows-v1
|
||||
key: llvm-build-18-windows-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.DS_Store
|
||||
.vscode
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
|
||||
@@ -42,6 +42,3 @@
|
||||
[submodule "lib/wasi-cli"]
|
||||
path = lib/wasi-cli
|
||||
url = https://github.com/WebAssembly/wasi-cli
|
||||
[submodule "src/vendor/github.com/ydnar/wasm-tools-go"]
|
||||
path = src/vendor/github.com/ydnar/wasm-tools-go
|
||||
url = https://github.com/ydnar/wasm-tools-go.git
|
||||
|
||||
+20
-5
@@ -245,7 +245,7 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
$(LLVM_BUILDDIR)/build.ninja:
|
||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;AVR;Mips;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||
|
||||
# Build LLVM.
|
||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||
@@ -257,7 +257,7 @@ ifneq ($(USE_SYSTEM_BINARYEN),1)
|
||||
binaryen: build/wasm-opt$(EXE)
|
||||
build/wasm-opt$(EXE):
|
||||
mkdir -p build
|
||||
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON -DBUILD_TESTS=OFF $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
|
||||
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON -DBUILD_TESTS=OFF -DENABLE_WERROR=OFF $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
|
||||
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
|
||||
endif
|
||||
|
||||
@@ -269,9 +269,16 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||
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
|
||||
WASM_TOOLS_MODULE=github.com/ydnar/wasm-tools-go
|
||||
.PHONY: wasi-syscall
|
||||
wasi-syscall:
|
||||
wit-bindgen-go generate -o ./src/internal -p internal --versioned ./lib/wasi-cli/wit
|
||||
wasi-syscall: wasi-cm
|
||||
go run -modfile ./internal/wasm-tools/go.mod $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit
|
||||
|
||||
# Copy package cm into src/internal/cm
|
||||
.PHONY: wasi-cm
|
||||
wasi-cm:
|
||||
# rm -rf ./src/internal/cm
|
||||
rsync -rv --delete --exclude '*_test.go' $(shell go list -modfile ./internal/wasm-tools/go.mod -m -f {{.Dir}} $(WASM_TOOLS_MODULE))/cm ./src/internal/
|
||||
|
||||
# Check for Node.js used during WASM tests.
|
||||
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
|
||||
@@ -828,6 +835,7 @@ endif
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
@$(MD5SUM) test.hex
|
||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||
GOOS=linux GOARCH=mips $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||
@@ -875,6 +883,7 @@ endif
|
||||
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/mips build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
|
||||
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
|
||||
@@ -946,6 +955,11 @@ release: build/release
|
||||
deb: build/release
|
||||
endif
|
||||
|
||||
.PHONY: tools
|
||||
tools:
|
||||
go generate -C ./internal/tools -tags tools ./
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
go run github.com/mgechev/revive -version
|
||||
# TODO: lint more directories!
|
||||
@@ -954,6 +968,7 @@ lint:
|
||||
# Use 'grep .' to get rid of stray blank line
|
||||
go run github.com/mgechev/revive -config revive.toml compiler/... src/{os,reflect}/*.go | grep -v "should have comment or be unexported" | grep '.' | awk '{print}; END {exit NR>0}'
|
||||
|
||||
.PHONY: spell
|
||||
spell:
|
||||
# Check for typos in comments. Skip git submodules etc.
|
||||
# Check for typos in comments. Skip git submodules etc.
|
||||
go run github.com/client9/misspell/cmd/misspell -i 'ackward,devided,extint,inbetween,programmmer,rela' $$( find . -depth 1 -type d | egrep -w -v 'lib|llvm|src/net' )
|
||||
|
||||
+2
-6
@@ -353,10 +353,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
}
|
||||
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
|
||||
|
||||
// Build the SSA for the given package.
|
||||
ssaPkg := program.Package(pkg.Pkg)
|
||||
ssaPkg.Build()
|
||||
|
||||
// Now create the job to actually build the package. It will exit early
|
||||
// if the package is already compiled.
|
||||
job := &compileJob{
|
||||
@@ -379,7 +375,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
defer mod.Context().Dispose()
|
||||
defer mod.Dispose()
|
||||
if errs != nil {
|
||||
return newMultiError(errs)
|
||||
return newMultiError(errs, pkg.ImportPath)
|
||||
}
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return errors.New("verification error after compiling package " + pkg.ImportPath)
|
||||
@@ -1134,7 +1130,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues m
|
||||
// O0/O1/O2/Os/Oz optimization pipeline).
|
||||
errs := transform.Optimize(mod, config)
|
||||
if len(errs) > 0 {
|
||||
return newMultiError(errs)
|
||||
return newMultiError(errs, "")
|
||||
}
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return errors.New("verification failure after LLVM optimization passes")
|
||||
|
||||
@@ -57,6 +57,8 @@ func TestClangAttributes(t *testing.T) {
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
|
||||
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
|
||||
{GOOS: "linux", GOARCH: "arm64"},
|
||||
{GOOS: "linux", GOARCH: "mips"},
|
||||
{GOOS: "linux", GOARCH: "mipsle"},
|
||||
{GOOS: "darwin", GOARCH: "amd64"},
|
||||
{GOOS: "darwin", GOARCH: "arm64"},
|
||||
{GOOS: "windows", GOARCH: "amd64"},
|
||||
|
||||
+5
-3
@@ -3,7 +3,8 @@ package builder
|
||||
// MultiError is a list of multiple errors (actually: diagnostics) returned
|
||||
// during LLVM IR generation.
|
||||
type MultiError struct {
|
||||
Errs []error
|
||||
ImportPath string
|
||||
Errs []error
|
||||
}
|
||||
|
||||
func (e *MultiError) Error() string {
|
||||
@@ -15,14 +16,15 @@ func (e *MultiError) Error() string {
|
||||
// newMultiError returns a *MultiError if there is more than one error, or
|
||||
// returns that error directly when there is only one. Passing an empty slice
|
||||
// will lead to a panic.
|
||||
func newMultiError(errs []error) error {
|
||||
// The importPath may be passed if this error is for a single package.
|
||||
func newMultiError(errs []error, importPath string) error {
|
||||
switch len(errs) {
|
||||
case 0:
|
||||
panic("attempted to create empty MultiError")
|
||||
case 1:
|
||||
return errs[0]
|
||||
default:
|
||||
return &MultiError{errs}
|
||||
return &MultiError{importPath, errs}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,6 +182,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
if strings.HasPrefix(target, "riscv64-") {
|
||||
args = append(args, "-march=rv64gc")
|
||||
}
|
||||
if strings.HasPrefix(target, "mips") {
|
||||
args = append(args, "-fno-pic")
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
|
||||
|
||||
+8
-1
@@ -7,6 +7,7 @@ import (
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
@@ -219,7 +220,13 @@ func (i simpleImporter) Import(path string) (*types.Package, error) {
|
||||
// formatDiagnostic formats the error message to be an indented comment. It
|
||||
// also fixes Windows path name issues (backward slashes).
|
||||
func formatDiagnostic(err error) string {
|
||||
msg := err.Error()
|
||||
var msg string
|
||||
switch err := err.(type) {
|
||||
case scanner.Error:
|
||||
msg = err.Pos.String() + ": " + err.Msg
|
||||
default:
|
||||
msg = err.Error()
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
// Fix Windows path slashes.
|
||||
msg = strings.ReplaceAll(msg, "testdata\\", "testdata/")
|
||||
|
||||
+6
-2
@@ -195,7 +195,9 @@ func (t *tokenizer) Next() {
|
||||
t.curValue = t.peekValue
|
||||
|
||||
// Parse the next peek token.
|
||||
t.peekPos += token.Pos(len(t.curValue))
|
||||
if t.peekPos != token.NoPos {
|
||||
t.peekPos += token.Pos(len(t.curValue))
|
||||
}
|
||||
for {
|
||||
if len(t.buf) == 0 {
|
||||
t.peekToken = token.EOF
|
||||
@@ -207,7 +209,9 @@ func (t *tokenizer) Next() {
|
||||
// Skip whitespace.
|
||||
// Based on this source, not sure whether it represents C whitespace:
|
||||
// https://en.cppreference.com/w/cpp/string/byte/isspace
|
||||
t.peekPos++
|
||||
if t.peekPos != token.NoPos {
|
||||
t.peekPos++
|
||||
}
|
||||
t.buf = t.buf[1:]
|
||||
case len(t.buf) >= 2 && (string(t.buf[:2]) == "||" || string(t.buf[:2]) == "&&" || string(t.buf[:2]) == "<<" || string(t.buf[:2]) == ">>"):
|
||||
// Two-character tokens.
|
||||
|
||||
+36
-32
@@ -4,6 +4,7 @@ package cgo
|
||||
// modification. It does not touch the AST itself.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
@@ -369,42 +370,45 @@ 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)
|
||||
start := C.clang_getRangeStart(sourceRange)
|
||||
end := C.clang_getRangeEnd(sourceRange)
|
||||
var file, endFile C.CXFile
|
||||
var startOffset, endOffset C.unsigned
|
||||
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||
if file == nil {
|
||||
f.addError(pos, "internal error: could not find file where macro is defined")
|
||||
return nil, nil
|
||||
}
|
||||
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||
if file != endFile {
|
||||
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||
return nil, nil
|
||||
}
|
||||
if startOffset > endOffset {
|
||||
f.addError(pos, "internal error: start offset of macro is after end offset")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// read file contents and extract the relevant byte range
|
||||
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
|
||||
var size C.size_t
|
||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||
if endOffset >= C.uint(size) {
|
||||
f.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||
return nil, nil
|
||||
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)
|
||||
}
|
||||
}
|
||||
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||
if !strings.HasPrefix(source, name) {
|
||||
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||
return nil, nil
|
||||
}
|
||||
value := source[len(name):]
|
||||
C.clang_disposeTokens(tu, rawTokens, numTokens)
|
||||
value := sourceBuf.String()
|
||||
// Try to convert this #define into a Go constant expression.
|
||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
|
||||
tokenPos := token.NoPos
|
||||
if pos != token.NoPos {
|
||||
tokenPos = pos + token.Pos(len(name))
|
||||
}
|
||||
expr, scannerError := parseConst(tokenPos, f.fset, value)
|
||||
if scannerError != nil {
|
||||
f.errors = append(f.errors, *scannerError)
|
||||
return nil, nil
|
||||
|
||||
Vendored
+13
@@ -13,10 +13,14 @@ typedef someType noType; // undefined type
|
||||
#define SOME_CONST_1 5) // invalid const syntax
|
||||
#define SOME_CONST_2 6) // const not used (so no error)
|
||||
#define SOME_CONST_3 1234 // const too large for byte
|
||||
#define SOME_CONST_b 3 ) // const with lots of weird whitespace (to test error locations)
|
||||
# define SOME_CONST_startspace 3)
|
||||
*/
|
||||
//
|
||||
//
|
||||
// #define SOME_CONST_4 8) // after some empty lines
|
||||
// #cgo CFLAGS: -DSOME_PARAM_CONST_invalid=3/+3
|
||||
// #cgo CFLAGS: -DSOME_PARAM_CONST_valid=3+4
|
||||
import "C"
|
||||
|
||||
// #warning another warning
|
||||
@@ -24,6 +28,7 @@ import "C"
|
||||
|
||||
// Make sure that errors for the following lines won't change with future
|
||||
// additions to the CGo preamble.
|
||||
//
|
||||
//line errors.go:100
|
||||
var (
|
||||
// constant too large
|
||||
@@ -38,4 +43,12 @@ var (
|
||||
_ byte = C.SOME_CONST_3
|
||||
|
||||
_ = C.SOME_CONST_4
|
||||
|
||||
_ = C.SOME_CONST_b
|
||||
|
||||
_ = C.SOME_CONST_startspace
|
||||
|
||||
// constants passed by a command line parameter
|
||||
_ = C.SOME_PARAM_CONST_invalid
|
||||
_ = C.SOME_PARAM_CONST_valid
|
||||
)
|
||||
|
||||
Vendored
+9
-2
@@ -1,9 +1,12 @@
|
||||
// CGo errors:
|
||||
// testdata/errors.go:4:2: warning: some warning
|
||||
// testdata/errors.go:11:9: error: unknown type name 'someType'
|
||||
// testdata/errors.go:22:5: warning: another warning
|
||||
// testdata/errors.go:26:5: warning: another warning
|
||||
// testdata/errors.go:13:23: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:19:26: unexpected token ), expected end of expression
|
||||
// testdata/errors.go:21:26: unexpected token ), expected end of expression
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
@@ -11,6 +14,9 @@
|
||||
// testdata/errors.go:108: undefined: C.SOME_CONST_1
|
||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||
// testdata/errors.go:112: undefined: C.SOME_CONST_4
|
||||
// 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
|
||||
|
||||
package main
|
||||
|
||||
@@ -58,3 +64,4 @@ type C.struct_point_t struct {
|
||||
type C.point_t = C.struct_point_t
|
||||
|
||||
const C.SOME_CONST_3 = 1234
|
||||
const C.SOME_PARAM_CONST_valid = 3 + 4
|
||||
|
||||
@@ -212,7 +212,10 @@ func (c *Config) RP2040BootPatch() bool {
|
||||
func MuslArchitecture(triple string) string {
|
||||
arch := strings.Split(triple, "-")[0]
|
||||
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||
arch = "arm"
|
||||
return "arm"
|
||||
}
|
||||
if arch == "mipsel" {
|
||||
return "mips"
|
||||
}
|
||||
return arch
|
||||
}
|
||||
|
||||
+18
-2
@@ -193,6 +193,10 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
|
||||
}
|
||||
case "mips":
|
||||
llvmarch = "mips"
|
||||
case "mipsle":
|
||||
llvmarch = "mipsel"
|
||||
case "wasm":
|
||||
llvmarch = "wasm32"
|
||||
default:
|
||||
@@ -327,6 +331,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
} else { // linux
|
||||
spec.Features = "+fp-armv8,+neon,-fmv,-outline-atomics"
|
||||
}
|
||||
case "mips", "mipsle":
|
||||
spec.CPU = "mips32r2"
|
||||
spec.Features = "+fpxx,+mips32r2,+nooddspreg,-noabicalls"
|
||||
spec.CFlags = append(spec.CFlags, "-fno-pic")
|
||||
case "wasm":
|
||||
spec.CPU = "generic"
|
||||
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
|
||||
@@ -419,8 +427,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
// operating systems so we need separate assembly files.
|
||||
suffix = "_windows"
|
||||
}
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
|
||||
asmGoarch := goarch
|
||||
if goarch == "mips" || goarch == "mipsle" {
|
||||
asmGoarch = "mipsx"
|
||||
}
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+asmGoarch+suffix+".S")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+asmGoarch+suffix+".S")
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
@@ -438,6 +450,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
spec.Emulator = "qemu-arm {}"
|
||||
case "arm64":
|
||||
spec.Emulator = "qemu-aarch64 {}"
|
||||
case "mips":
|
||||
spec.Emulator = "qemu-mips {}"
|
||||
case "mipsle":
|
||||
spec.Emulator = "qemu-mipsel {}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +187,18 @@ std z+5, r29
|
||||
ldi r24, 0
|
||||
1:`
|
||||
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
|
||||
case "mips":
|
||||
// $4 flag (zero or non-zero)
|
||||
// $5 defer frame
|
||||
asmString = `
|
||||
.set noat
|
||||
move $$4, $$zero
|
||||
jal 1f
|
||||
1:
|
||||
addiu $$ra, 8
|
||||
sw $$ra, 4($$5)
|
||||
.set at`
|
||||
constraints = "={$4},{$5},~{$1},~{$2},~{$3},~{$5},~{$6},~{$7},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$16},~{$17},~{$18},~{$19},~{$20},~{$21},~{$22},~{$23},~{$24},~{$25},~{$26},~{$27},~{$28},~{$29},~{$30},~{$31},~{$f0},~{$f1},~{$f2},~{$f3},~{$f4},~{$f5},~{$f6},~{$f7},~{$f8},~{$f9},~{$f10},~{$f11},~{$f12},~{$f13},~{$f14},~{$f15},~{$f16},~{$f17},~{$f18},~{$f19},~{$f20},~{$f21},~{$f22},~{$f23},~{$f24},~{$f25},~{$f26},~{$f27},~{$f28},~{$f29},~{$f30},~{$f31},~{memory}"
|
||||
case "riscv32":
|
||||
asmString = `
|
||||
la a2, 1f
|
||||
|
||||
+90
-27
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -180,6 +181,15 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
typeFieldTypes := []*types.Var{
|
||||
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
|
||||
}
|
||||
var methods []*types.Func
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
methods = append(methods, ms.At(i).Obj().(*types.Func))
|
||||
}
|
||||
methodSetType := types.NewStruct([]*types.Var{
|
||||
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
|
||||
types.NewVar(token.NoPos, nil, "methods", types.NewArray(types.Typ[types.UnsafePointer], int64(len(methods)))),
|
||||
}, nil)
|
||||
methodSetValue := c.getMethodSetValue(methods)
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
@@ -196,6 +206,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(pkgname)+1+len(name)+1))),
|
||||
)
|
||||
case *types.Chan:
|
||||
@@ -214,6 +225,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
|
||||
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
case *types.Array:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
@@ -238,12 +250,13 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uint32]),
|
||||
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
|
||||
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
case *types.Interface:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
// TODO: methods
|
||||
case *types.Signature:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
@@ -294,6 +307,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Underlying()), // underlying
|
||||
pkgPathPtr, // pkgpath pointer
|
||||
methodSetValue, // methods
|
||||
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
|
||||
}
|
||||
metabyte |= 1 << 5 // "named" flag
|
||||
@@ -323,6 +337,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||
c.getTypeCode(typ.Elem()),
|
||||
methodSetValue, // methods
|
||||
}
|
||||
case *types.Array:
|
||||
typeFields = []llvm.Value{
|
||||
@@ -404,9 +419,12 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
}))
|
||||
}
|
||||
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
|
||||
typeFields = append(typeFields, methodSetValue)
|
||||
case *types.Interface:
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
// TODO: methods
|
||||
typeFields = []llvm.Value{
|
||||
c.getTypeCode(types.NewPointer(typ)),
|
||||
methodSetValue,
|
||||
}
|
||||
case *types.Signature:
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
// TODO: params, return values, etc
|
||||
@@ -685,7 +703,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
assertedType := b.getLLVMType(expr.AssertedType)
|
||||
|
||||
actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
|
||||
commaOk := llvm.Value{}
|
||||
var commaOk llvm.Value
|
||||
|
||||
if intf, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||
if intf.Empty() {
|
||||
@@ -693,17 +711,16 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
// This type assertion always succeeds, so we can just set commaOk to true.
|
||||
commaOk = llvm.ConstInt(b.ctx.Int1Type(), 1, true)
|
||||
} else {
|
||||
// Type assert on interface type with methods.
|
||||
// This is a call to an interface type assert function.
|
||||
// The interface lowering pass will define this function by filling it
|
||||
// with a type switch over all concrete types that implement this
|
||||
// interface, and returning whether it's one of the matched types.
|
||||
// This is very different from how interface asserts are implemented in
|
||||
// the main Go compiler, where the runtime checks whether the type
|
||||
// implements each method of the interface. See:
|
||||
// Type assert using interface type with methods.
|
||||
// This is implemented using a runtime call, which checks that the
|
||||
// type implements each method of the interface.
|
||||
// For comparison, here is how the Go compiler does this (which is
|
||||
// very similar):
|
||||
// https://research.swtch.com/interfaces
|
||||
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
|
||||
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
||||
commaOk = b.createRuntimeCall("typeImplementsMethodSet", []llvm.Value{
|
||||
actualTypeNum,
|
||||
b.getInterfaceMethodSet(intf),
|
||||
}, "")
|
||||
}
|
||||
} else {
|
||||
name, _ := getTypeCodeName(expr.AssertedType)
|
||||
@@ -780,20 +797,66 @@ func (c *compilerContext) getMethodsString(itf *types.Interface) string {
|
||||
return strings.Join(methods, "; ")
|
||||
}
|
||||
|
||||
// getInterfaceImplementsFunc returns a declared function that works as a type
|
||||
// switch. The interface lowering pass will define this function.
|
||||
func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value {
|
||||
s, _ := getTypeCodeName(assertedType.Underlying())
|
||||
fnName := s + ".$typeassert"
|
||||
llvmFn := c.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.dataPtrType}, false)
|
||||
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||
c.addStandardDeclaredAttributes(llvmFn)
|
||||
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
|
||||
// Get the global that contains an interface method set, creating it if needed.
|
||||
func (c *compilerContext) getInterfaceMethodSet(t *types.Interface) llvm.Value {
|
||||
s, _ := getTypeCodeName(t)
|
||||
methodSetName := s + "$itfmethods"
|
||||
methodSet := c.mod.NamedFunction(methodSetName)
|
||||
if methodSet.IsNil() {
|
||||
var methods []*types.Func
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
methods = append(methods, t.Method(i))
|
||||
}
|
||||
if len(methods) == 0 {
|
||||
// This *should* be unreachable: the caller checks whether the
|
||||
// interface is empty before creating a method set.
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
methodSetValue := c.getMethodSetValue(methods)
|
||||
methodSet = llvm.AddGlobal(c.mod, methodSetValue.Type(), methodSetName)
|
||||
methodSet.SetInitializer(methodSetValue)
|
||||
methodSet.SetGlobalConstant(true)
|
||||
methodSet.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
methodSet.SetAlignment(c.targetData.ABITypeAlignment(methodSetValue.Type()))
|
||||
methodSet.SetUnnamedAddr(true)
|
||||
}
|
||||
return llvmFn
|
||||
|
||||
return methodSet
|
||||
}
|
||||
|
||||
// Get the method set value that is used in a number of type structs.
|
||||
func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
|
||||
// Create a sorted list of method names.
|
||||
var methodRefNames []string
|
||||
for _, method := range methods {
|
||||
name := method.Name()
|
||||
if !token.IsExported(name) {
|
||||
name = method.Pkg().Path() + "." + name
|
||||
}
|
||||
s, _ := getTypeCodeName(method.Type())
|
||||
methodRefNames = append(methodRefNames, "reflect/types.signature:"+name+":"+s)
|
||||
}
|
||||
sort.Strings(methodRefNames)
|
||||
|
||||
// Turn this slice of strings in a slice of global variables.
|
||||
var methodRefValues []llvm.Value
|
||||
for _, name := range methodRefNames {
|
||||
value := c.mod.NamedGlobal(name)
|
||||
if value.IsNil() {
|
||||
value = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), name)
|
||||
value.SetInitializer(llvm.ConstNull(c.ctx.Int8Type()))
|
||||
value.SetGlobalConstant(true)
|
||||
value.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
value.SetAlignment(1)
|
||||
}
|
||||
methodRefValues = append(methodRefValues, value)
|
||||
}
|
||||
|
||||
return c.ctx.ConstStruct([]llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, uint64(len(methodRefValues)), false),
|
||||
llvm.ConstArray(c.dataPtrType, methodRefValues),
|
||||
}, false)
|
||||
}
|
||||
|
||||
// getInvokeFunction returns the thunk to call the given interface method. The
|
||||
|
||||
@@ -429,6 +429,9 @@ func (c *compilerContext) archFamily() string {
|
||||
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||
return "arm"
|
||||
}
|
||||
if arch == "mipsel" {
|
||||
return "mips"
|
||||
}
|
||||
return arch
|
||||
}
|
||||
|
||||
|
||||
+93
-1
@@ -12,7 +12,8 @@ import (
|
||||
|
||||
// createRawSyscall creates a system call with the provided system call number
|
||||
// and returns the result as a single integer (the system call result). The
|
||||
// result is not further interpreted.
|
||||
// result is not further interpreted (with the exception of MIPS to use the same
|
||||
// return value everywhere).
|
||||
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
num := b.getValue(call.Args[0], getPos(call))
|
||||
switch {
|
||||
@@ -137,6 +138,97 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
|
||||
case (b.GOARCH == "mips" || b.GOARCH == "mipsle") && b.GOOS == "linux":
|
||||
// Implement the system call convention for Linux.
|
||||
// Source: syscall(2) man page and musl:
|
||||
// https://git.musl-libc.org/cgit/musl/tree/arch/mips/syscall_arch.h
|
||||
// Also useful:
|
||||
// https://web.archive.org/web/20220529105937/https://www.linux-mips.org/wiki/Syscall
|
||||
// The syscall number goes in r2, the result also in r2.
|
||||
// Register r7 is both an input paramter and an output parameter: if it
|
||||
// is non-zero, the system call failed and r2 is the error code.
|
||||
// The code below implements the O32 syscall ABI, not the N32 ABI. It
|
||||
// could implement both at the same time if needed (like what appears to
|
||||
// be done in musl) by forcing arg5-arg7 into the right registers but
|
||||
// letting the compiler decide the registers should result in _slightly_
|
||||
// faster and smaller code.
|
||||
args := []llvm.Value{num}
|
||||
argTypes := []llvm.Type{b.uintptrType}
|
||||
constraints := "={$2},={$7},0"
|
||||
syscallParams := call.Args[1:]
|
||||
if len(syscallParams) > 7 {
|
||||
// There is one syscall that uses 7 parameters: sync_file_range.
|
||||
// But only 7, not more. Go however only has Syscall6 and Syscall9.
|
||||
// Therefore, we can ignore the remaining parameters.
|
||||
syscallParams = syscallParams[:7]
|
||||
}
|
||||
for i, arg := range syscallParams {
|
||||
constraints += "," + [...]string{
|
||||
"{$4}", // arg1
|
||||
"{$5}", // arg2
|
||||
"{$6}", // arg3
|
||||
"1", // arg4, error return
|
||||
"r", // arg5 on the stack
|
||||
"r", // arg6 on the stack
|
||||
"r", // arg7 on the stack
|
||||
}[i]
|
||||
llvmValue := b.getValue(arg, getPos(call))
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
// Create assembly code.
|
||||
// Parameters beyond the first 4 are passed on the stack instead of in
|
||||
// registers in the O32 syscall ABI.
|
||||
// We need ".set noat" because LLVM might pick register $1 ($at) as the
|
||||
// register for a parameter and apparently this is not allowed on MIPS
|
||||
// unless you use this specific pragma.
|
||||
asm := "syscall"
|
||||
switch len(syscallParams) {
|
||||
case 5:
|
||||
asm = "" +
|
||||
".set noat\n" +
|
||||
"subu $$sp, $$sp, 32\n" +
|
||||
"sw $7, 16($$sp)\n" + // arg5
|
||||
"syscall\n" +
|
||||
"addu $$sp, $$sp, 32\n" +
|
||||
".set at\n"
|
||||
case 6:
|
||||
asm = "" +
|
||||
".set noat\n" +
|
||||
"subu $$sp, $$sp, 32\n" +
|
||||
"sw $7, 16($$sp)\n" + // arg5
|
||||
"sw $8, 20($$sp)\n" + // arg6
|
||||
"syscall\n" +
|
||||
"addu $$sp, $$sp, 32\n" +
|
||||
".set at\n"
|
||||
case 7:
|
||||
asm = "" +
|
||||
".set noat\n" +
|
||||
"subu $$sp, $$sp, 32\n" +
|
||||
"sw $7, 16($$sp)\n" + // arg5
|
||||
"sw $8, 20($$sp)\n" + // arg6
|
||||
"sw $9, 24($$sp)\n" + // arg7
|
||||
"syscall\n" +
|
||||
"addu $$sp, $$sp, 32\n" +
|
||||
".set at\n"
|
||||
}
|
||||
constraints += ",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}"
|
||||
returnType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false)
|
||||
fnType := llvm.FunctionType(returnType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, true, 0, false)
|
||||
call := b.CreateCall(fnType, target, args, "")
|
||||
resultCode := b.CreateExtractValue(call, 0, "") // r2
|
||||
errorFlag := b.CreateExtractValue(call, 1, "") // r7
|
||||
// Pseudocode to return the result with the same convention as other
|
||||
// archs:
|
||||
// return (errorFlag != 0) ? -resultCode : resultCode;
|
||||
// At least on QEMU with the O32 ABI, the error code is always positive.
|
||||
zero := llvm.ConstInt(b.uintptrType, 0, false)
|
||||
isError := b.CreateICmp(llvm.IntNE, errorFlag, zero, "")
|
||||
negativeResult := b.CreateSub(zero, resultCode, "")
|
||||
result := b.CreateSelect(isError, negativeResult, resultCode, "")
|
||||
return result, nil
|
||||
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -22,7 +22,7 @@ target triple = "wasm32-unknown-wasi"
|
||||
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
||||
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
Vendored
+31
-31
@@ -7,15 +7,19 @@ target triple = "wasm32-unknown-wasi"
|
||||
%runtime._string = type { ptr, i32 }
|
||||
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:named:error", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
@"reflect/types.signature:Error:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] } }, align 4
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
@"reflect/types.signature:String:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
@"interface:{Error:func:{}{basic:string}}$itfmethods" = linkonce_odr unnamed_addr constant { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, align 4
|
||||
@"interface:{String:func:{}{basic:string}}$itfmethods" = linkonce_odr unnamed_addr constant { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] }, align 4
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
@@ -32,8 +36,8 @@ entry:
|
||||
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
|
||||
}
|
||||
|
||||
@@ -41,8 +45,8 @@ entry:
|
||||
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
|
||||
}
|
||||
|
||||
@@ -50,8 +54,8 @@ entry:
|
||||
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
|
||||
}
|
||||
|
||||
@@ -59,15 +63,15 @@ entry:
|
||||
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
|
||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #5
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -82,7 +86,7 @@ declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
||||
%0 = call i1 @runtime.typeImplementsMethodSet(ptr %itf.typecode, ptr nonnull @"interface:{Error:func:{}{basic:string}}$itfmethods", ptr undef) #5
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -92,12 +96,12 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
|
||||
declare i1 @runtime.typeImplementsMethodSet(ptr, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
||||
%0 = call i1 @runtime.typeImplementsMethodSet(ptr %itf.typecode, ptr nonnull @"interface:{String:func:{}{basic:string}}$itfmethods", ptr undef) #5
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -107,34 +111,30 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7
|
||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #5
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5
|
||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
|
||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #5
|
||||
%1 = extractvalue %runtime._string %0, 0
|
||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._string %0
|
||||
}
|
||||
|
||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
|
||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #4
|
||||
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
|
||||
attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #6 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #7 = { nounwind }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #5 = { nounwind }
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
// Package diagnostics formats compiler errors and prints them in a consistent
|
||||
// way.
|
||||
package diagnostics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/builder"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/interp"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
)
|
||||
|
||||
// A single diagnostic.
|
||||
type Diagnostic struct {
|
||||
Pos token.Position
|
||||
Msg string
|
||||
}
|
||||
|
||||
// One or multiple errors of a particular package.
|
||||
// It can also represent whole-program errors (like linker errors) that can't
|
||||
// easily be connected to a single package.
|
||||
type PackageDiagnostic struct {
|
||||
ImportPath string // the same ImportPath as in `go list -json`
|
||||
Diagnostics []Diagnostic
|
||||
}
|
||||
|
||||
// Diagnostics of a whole program. This can include errors belonging to multiple
|
||||
// packages, or just a single package.
|
||||
type ProgramDiagnostic []PackageDiagnostic
|
||||
|
||||
// CreateDiagnostics reads the underlying errors in the error object and creates
|
||||
// a set of diagnostics that's sorted and can be readily printed.
|
||||
func CreateDiagnostics(err error) ProgramDiagnostic {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
// Right now, the compiler will only show errors for the first pacakge that
|
||||
// fails to build. This is likely to change in the future.
|
||||
return ProgramDiagnostic{
|
||||
createPackageDiagnostic(err),
|
||||
}
|
||||
}
|
||||
|
||||
// Create diagnostics for a single package (though, in practice, it may also be
|
||||
// used for whole-program diagnostics in some cases).
|
||||
func createPackageDiagnostic(err error) PackageDiagnostic {
|
||||
// Extract diagnostics for this package.
|
||||
var pkgDiag PackageDiagnostic
|
||||
switch err := err.(type) {
|
||||
case *builder.MultiError:
|
||||
if err.ImportPath != "" {
|
||||
pkgDiag.ImportPath = err.ImportPath
|
||||
}
|
||||
for _, err := range err.Errs {
|
||||
diags := createDiagnostics(err)
|
||||
pkgDiag.Diagnostics = append(pkgDiag.Diagnostics, diags...)
|
||||
}
|
||||
case loader.Errors:
|
||||
if err.Pkg != nil {
|
||||
pkgDiag.ImportPath = err.Pkg.ImportPath
|
||||
}
|
||||
for _, err := range err.Errs {
|
||||
diags := createDiagnostics(err)
|
||||
pkgDiag.Diagnostics = append(pkgDiag.Diagnostics, diags...)
|
||||
}
|
||||
case *interp.Error:
|
||||
pkgDiag.ImportPath = err.ImportPath
|
||||
w := &bytes.Buffer{}
|
||||
fmt.Fprintln(w, err.Error())
|
||||
if len(err.Inst) != 0 {
|
||||
fmt.Fprintln(w, err.Inst)
|
||||
}
|
||||
if len(err.Traceback) > 0 {
|
||||
fmt.Fprintln(w, "\ntraceback:")
|
||||
for _, line := range err.Traceback {
|
||||
fmt.Fprintln(w, line.Pos.String()+":")
|
||||
fmt.Fprintln(w, line.Inst)
|
||||
}
|
||||
}
|
||||
pkgDiag.Diagnostics = append(pkgDiag.Diagnostics, Diagnostic{
|
||||
Msg: w.String(),
|
||||
})
|
||||
default:
|
||||
pkgDiag.Diagnostics = createDiagnostics(err)
|
||||
}
|
||||
|
||||
// Sort these diagnostics by file/line/column.
|
||||
sort.SliceStable(pkgDiag.Diagnostics, func(i, j int) bool {
|
||||
posI := pkgDiag.Diagnostics[i].Pos
|
||||
posJ := pkgDiag.Diagnostics[j].Pos
|
||||
if posI.Filename != posJ.Filename {
|
||||
return posI.Filename < posJ.Filename
|
||||
}
|
||||
if posI.Line != posJ.Line {
|
||||
return posI.Line < posJ.Line
|
||||
}
|
||||
return posI.Column < posJ.Column
|
||||
})
|
||||
|
||||
return pkgDiag
|
||||
}
|
||||
|
||||
// Extract diagnostics from the given error message and return them as a slice
|
||||
// of errors (which in many cases will just be a single diagnostic).
|
||||
func createDiagnostics(err error) []Diagnostic {
|
||||
switch err := err.(type) {
|
||||
case types.Error:
|
||||
return []Diagnostic{
|
||||
{
|
||||
Pos: err.Fset.Position(err.Pos),
|
||||
Msg: err.Msg,
|
||||
},
|
||||
}
|
||||
case scanner.Error:
|
||||
return []Diagnostic{
|
||||
{
|
||||
Pos: err.Pos,
|
||||
Msg: err.Msg,
|
||||
},
|
||||
}
|
||||
case scanner.ErrorList:
|
||||
var diags []Diagnostic
|
||||
for _, err := range err {
|
||||
diags = append(diags, createDiagnostics(*err)...)
|
||||
}
|
||||
return diags
|
||||
case loader.Error:
|
||||
if err.Err.Pos.Filename != "" {
|
||||
// Probably a syntax error in a dependency.
|
||||
return createDiagnostics(err.Err)
|
||||
} else {
|
||||
// Probably an "import cycle not allowed" error.
|
||||
buf := &bytes.Buffer{}
|
||||
fmt.Fprintln(buf, "package", err.ImportStack[0])
|
||||
for i := 1; i < len(err.ImportStack); i++ {
|
||||
pkgPath := err.ImportStack[i]
|
||||
if i == len(err.ImportStack)-1 {
|
||||
// last package
|
||||
fmt.Fprintln(buf, "\timports", pkgPath+": "+err.Err.Error())
|
||||
} else {
|
||||
// not the last pacakge
|
||||
fmt.Fprintln(buf, "\timports", pkgPath)
|
||||
}
|
||||
}
|
||||
return []Diagnostic{
|
||||
{Msg: buf.String()},
|
||||
}
|
||||
}
|
||||
default:
|
||||
return []Diagnostic{
|
||||
{Msg: err.Error()},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write program diagnostics to the given writer with 'wd' as the relative
|
||||
// working directory.
|
||||
func (progDiag ProgramDiagnostic) WriteTo(w io.Writer, wd string) {
|
||||
for _, pkgDiag := range progDiag {
|
||||
pkgDiag.WriteTo(w, wd)
|
||||
}
|
||||
}
|
||||
|
||||
// Write package diagnostics to the given writer with 'wd' as the relative
|
||||
// working directory.
|
||||
func (pkgDiag PackageDiagnostic) WriteTo(w io.Writer, wd string) {
|
||||
if pkgDiag.ImportPath != "" {
|
||||
fmt.Fprintln(w, "#", pkgDiag.ImportPath)
|
||||
}
|
||||
for _, diag := range pkgDiag.Diagnostics {
|
||||
diag.WriteTo(w, wd)
|
||||
}
|
||||
}
|
||||
|
||||
// Write this diagnostic to the given writer with 'wd' as the relative working
|
||||
// directory.
|
||||
func (diag Diagnostic) WriteTo(w io.Writer, wd string) {
|
||||
if diag.Pos == (token.Position{}) {
|
||||
fmt.Fprintln(w, diag.Msg)
|
||||
return
|
||||
}
|
||||
pos := diag.Pos // make a copy
|
||||
if !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
|
||||
// This file is not from the standard library (either the GOROOT or the
|
||||
// TINYGOROOT). Make the path relative, for easier reading. Ignore any
|
||||
// errors in the process (falling back to the absolute path).
|
||||
pos.Filename = tryToMakePathRelative(pos.Filename, wd)
|
||||
}
|
||||
fmt.Fprintf(w, "%s: %s\n", pos, diag.Msg)
|
||||
}
|
||||
|
||||
// try to make the path relative to the current working directory. If any error
|
||||
// occurs, this error is ignored and the absolute path is returned instead.
|
||||
func tryToMakePathRelative(dir, wd string) string {
|
||||
if wd == "" {
|
||||
return dir // working directory not found
|
||||
}
|
||||
relpath, err := filepath.Rel(wd, dir)
|
||||
if err != nil {
|
||||
return dir
|
||||
}
|
||||
return relpath
|
||||
}
|
||||
+35
-15
@@ -2,26 +2,28 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/diagnostics"
|
||||
)
|
||||
|
||||
// Test the error messages of the TinyGo compiler.
|
||||
func TestErrors(t *testing.T) {
|
||||
for _, name := range []string{
|
||||
"cgo",
|
||||
"multi",
|
||||
"compiler",
|
||||
"interp",
|
||||
"loader-importcycle",
|
||||
"loader-invaliddep",
|
||||
"loader-invalidpackage",
|
||||
"loader-nopackage",
|
||||
"optimizer",
|
||||
"syntax",
|
||||
"types",
|
||||
} {
|
||||
@@ -57,26 +59,44 @@ func testErrorMessages(t *testing.T, filename string) {
|
||||
|
||||
// Write error message out as plain text.
|
||||
var buf bytes.Buffer
|
||||
printCompilerError(err, func(v ...interface{}) {
|
||||
fmt.Fprintln(&buf, v...)
|
||||
}, wd)
|
||||
diagnostics.CreateDiagnostics(err).WriteTo(&buf, wd)
|
||||
actual := strings.TrimRight(buf.String(), "\n")
|
||||
|
||||
// Check whether the error is as expected.
|
||||
if canonicalizeErrors(actual) != canonicalizeErrors(expected) {
|
||||
if !matchErrors(t, expected, actual) {
|
||||
t.Errorf("expected error:\n%s\ngot:\n%s", indentText(expected, "> "), indentText(actual, "> "))
|
||||
}
|
||||
}
|
||||
|
||||
func canonicalizeErrors(text string) string {
|
||||
// Fix for Windows: replace all backslashes with forward slashes so that
|
||||
// paths will be the same as on POSIX systems.
|
||||
// (It may also change some other backslashes, but since this is only for
|
||||
// comparing text it should be fine).
|
||||
if runtime.GOOS == "windows" {
|
||||
text = strings.ReplaceAll(text, "\\", "/")
|
||||
func matchErrors(t *testing.T, pattern, actual string) bool {
|
||||
patternLines := strings.Split(pattern, "\n")
|
||||
actualLines := strings.Split(actual, "\n")
|
||||
if len(patternLines) != len(actualLines) {
|
||||
return false
|
||||
}
|
||||
return text
|
||||
for i, patternLine := range patternLines {
|
||||
indices := regexp.MustCompile(`\{\{.*?\}\}`).FindAllStringIndex(patternLine, -1)
|
||||
patternParts := []string{"^"}
|
||||
lastStop := 0
|
||||
for _, startstop := range indices {
|
||||
start := startstop[0]
|
||||
stop := startstop[1]
|
||||
patternParts = append(patternParts,
|
||||
regexp.QuoteMeta(patternLine[lastStop:start]),
|
||||
patternLine[start+2:stop-2])
|
||||
lastStop = stop
|
||||
}
|
||||
patternParts = append(patternParts, regexp.QuoteMeta(patternLine[lastStop:]), "$")
|
||||
pattern := strings.Join(patternParts, "")
|
||||
re, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
t.Fatalf("could not compile regexp for %#v: %v", patternLine, err)
|
||||
}
|
||||
if !re.MatchString(actualLines[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Indent the given text with a given indentation string.
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
//go:build tools
|
||||
|
||||
// Install linter versions specified in go.mod
|
||||
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idom
|
||||
// Install tools specified in go.mod.
|
||||
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idiom.
|
||||
package tools
|
||||
|
||||
import (
|
||||
_ "github.com/client9/misspell"
|
||||
_ "github.com/mgechev/revive"
|
||||
)
|
||||
|
||||
//go:generate go install github.com/client9/misspell/cmd/misspell
|
||||
//go:generate go install github.com/mgechev/revive
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# wasm-tools directory
|
||||
|
||||
This directory has a separate `go.mod` file because the `wasm-tools-go` module requires Go 1.22, while TinyGo itself supports Go 1.19.
|
||||
|
||||
When the minimum Go version for TinyGo is 1.22, this directory can be folded into `internal/tools` and the `go.mod` and `go.sum` files deleted.
|
||||
@@ -0,0 +1,12 @@
|
||||
module github.com/tinygo-org/tinygo/internal/tools
|
||||
|
||||
go 1.22.4
|
||||
|
||||
require github.com/ydnar/wasm-tools-go v0.1.4
|
||||
|
||||
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
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/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/ydnar/wasm-tools-go v0.1.4 h1:+25WqBj0AhLx8OFvZvrs7bQO6L3WtQ7t6JzQEYsXQb8=
|
||||
github.com/ydnar/wasm-tools-go v0.1.4/go.mod h1:lQfv2Tde3tRgZDSYriro0EmdSHzP1mrHPMmYNahSS/g=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build tools
|
||||
|
||||
// Install tools specified in go.mod.
|
||||
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idiom.
|
||||
package tools
|
||||
|
||||
import (
|
||||
_ "github.com/ydnar/wasm-tools-go/cmd/wit-bindgen-go"
|
||||
)
|
||||
|
||||
//go:generate go install github.com/ydnar/wasm-tools-go/cmd/wit-bindgen-go
|
||||
+4
-58
@@ -417,64 +417,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
} else {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
}
|
||||
case strings.HasSuffix(callFn.name, ".$typeassert"):
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
|
||||
}
|
||||
|
||||
// Load various values for the interface implements check below.
|
||||
typecodePtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
// typecodePtr always point to the numMethod field in the type
|
||||
// description struct. The methodSet, when present, comes right
|
||||
// before the numMethod field (the compiler doesn't generate
|
||||
// method sets for concrete types without methods).
|
||||
// Considering that the compiler doesn't emit interface type
|
||||
// asserts for interfaces with no methods (as the always succeed)
|
||||
// then if the offset is zero, this assert must always fail.
|
||||
if typecodePtr.offset() == 0 {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
break
|
||||
}
|
||||
typecodePtrOffset, err := typecodePtr.addOffset(-int64(r.pointerSize))
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSetPtr, err := mem.load(typecodePtrOffset, r.pointerSize).asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
|
||||
numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue())
|
||||
llvmFn := inst.llvmInst.CalledValue()
|
||||
methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods")
|
||||
methodSetString := methodSetAttr.GetStringValue()
|
||||
|
||||
// Make a set of all the methods on the concrete type, for
|
||||
// easier checking in the next step.
|
||||
concreteTypeMethods := map[string]struct{}{}
|
||||
for i := 0; i < numMethods; i++ {
|
||||
methodInfo := r.builder.CreateExtractValue(methodSet, 1, "")
|
||||
name := r.builder.CreateExtractValue(methodInfo, i, "").Name()
|
||||
concreteTypeMethods[name] = struct{}{}
|
||||
}
|
||||
|
||||
// Check whether all interface methods are also in the list
|
||||
// of defined methods calculated above. This is the interface
|
||||
// assert itself.
|
||||
assertOk := uint8(1) // i1 true
|
||||
for _, name := range strings.Split(methodSetString, "; ") {
|
||||
if _, ok := concreteTypeMethods[name]; !ok {
|
||||
// There is a method on the interface that is not
|
||||
// implemented by the type. The assertion will fail.
|
||||
assertOk = 0 // i1 false
|
||||
break
|
||||
}
|
||||
}
|
||||
// If assertOk is still 1, the assertion succeeded.
|
||||
locals[inst.localIndex] = literalValue{assertOk}
|
||||
case callFn.name == "__tinygo_interp_raise_test_error":
|
||||
// Special function that will trigger an error.
|
||||
// This is used to test error reporting.
|
||||
return nil, mem, r.errorAt(inst, errors.New("test error"))
|
||||
case strings.HasSuffix(callFn.name, "$invoke"):
|
||||
// This thunk is the interface method dispatcher: it is called
|
||||
// with all regular parameters and a type code. It will then
|
||||
|
||||
+1
-2
@@ -238,6 +238,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
||||
"internal/": true,
|
||||
"internal/binary/": false,
|
||||
"internal/bytealg/": false,
|
||||
"internal/cm/": false,
|
||||
"internal/fuzz/": false,
|
||||
"internal/reflectlite/": false,
|
||||
"internal/task/": false,
|
||||
@@ -251,8 +252,6 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
||||
"runtime/": false,
|
||||
"sync/": true,
|
||||
"testing/": true,
|
||||
"vendor/": true,
|
||||
"vendor/github.com/": false,
|
||||
}
|
||||
|
||||
if goMinor >= 19 {
|
||||
|
||||
+5
-39
@@ -318,34 +318,18 @@ func (p *Program) MainPkg() *Package {
|
||||
func (p *Program) Parse() error {
|
||||
// Parse all packages.
|
||||
// TODO: do this in parallel.
|
||||
var errors []error
|
||||
for _, pkg := range p.sorted {
|
||||
err := pkg.Parse()
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Typecheck all packages.
|
||||
for _, pkg := range p.sorted {
|
||||
if !pkg.isParsed() || !pkg.allImportsChecked() {
|
||||
if len(errors) == 0 {
|
||||
// Sanity check.
|
||||
// If there are no errors, all packages should have been parsed.
|
||||
panic("unreachable")
|
||||
}
|
||||
continue
|
||||
}
|
||||
err := pkg.Check()
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) != 0 {
|
||||
// TODO: use errors.Join in Go 1.20.
|
||||
return Errors{
|
||||
Errs: errors,
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,12 +354,12 @@ func (p *Package) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||
return parser.ParseFile(p.program.fset, originalPath, data, mode)
|
||||
}
|
||||
|
||||
// Parse parses this package.
|
||||
// Parse parses and typechecks this package.
|
||||
//
|
||||
// Idempotent.
|
||||
func (p *Package) Parse() error {
|
||||
if p.isParsed() {
|
||||
return nil // nothing to do
|
||||
if len(p.Files) != 0 {
|
||||
return nil // nothing to do (?)
|
||||
}
|
||||
|
||||
// Load the AST.
|
||||
@@ -395,24 +379,6 @@ func (p *Package) Parse() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// isParsed returns whether this package has been parsed.
|
||||
func (p *Package) isParsed() bool {
|
||||
// Special case: the unsafe package doesn't have files to parse but does
|
||||
// have p.Pkg set once it is parsed.
|
||||
return len(p.Files) != 0 || p.Pkg != nil
|
||||
}
|
||||
|
||||
// allImportsChecked returns whether all imports of this package have been
|
||||
// type-checked.
|
||||
func (p *Package) allImportsChecked() bool {
|
||||
for _, dep := range p.Imports {
|
||||
if p.program.Packages[dep].Pkg == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Check runs the package through the typechecker. The package must already be
|
||||
// loaded and all dependencies must have been checked already.
|
||||
//
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/scanner"
|
||||
"go/types"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -30,8 +28,8 @@ import (
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/tinygo-org/tinygo/builder"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/diagnostics"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/interp"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
"tinygo.org/x/go-llvm"
|
||||
@@ -347,6 +345,11 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
if testConfig.CompileOnly {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
importPath := strings.TrimSuffix(result.ImportPath, ".test")
|
||||
|
||||
var w io.Writer = stdout
|
||||
@@ -357,7 +360,7 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
|
||||
fmt.Fprintf(w, "? \t%s\t[no test files]\n", err.ImportPath)
|
||||
// Pretend the test passed - it at least didn't fail.
|
||||
return true, nil
|
||||
} else if passed && !testConfig.CompileOnly {
|
||||
} else if passed {
|
||||
fmt.Fprintf(w, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
} else {
|
||||
fmt.Fprintf(w, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
@@ -1072,7 +1075,8 @@ func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
|
||||
continue
|
||||
}
|
||||
fstype := fields[2]
|
||||
if fstype != "vfat" {
|
||||
// chromeos bind mounts use 9p
|
||||
if !(fstype == "vfat" || fstype == "9p") {
|
||||
continue
|
||||
}
|
||||
fspath := strings.ReplaceAll(fields[1], "\\040", " ")
|
||||
@@ -1286,99 +1290,13 @@ func usage(command string) {
|
||||
}
|
||||
}
|
||||
|
||||
// try to make the path relative to the current working directory. If any error
|
||||
// occurs, this error is ignored and the absolute path is returned instead.
|
||||
func tryToMakePathRelative(dir, wd string) string {
|
||||
if wd == "" {
|
||||
return dir // working directory not found
|
||||
}
|
||||
relpath, err := filepath.Rel(wd, dir)
|
||||
if err != nil {
|
||||
return dir
|
||||
}
|
||||
return relpath
|
||||
}
|
||||
|
||||
// printCompilerError prints compiler errors using the provided logger function
|
||||
// (similar to fmt.Println).
|
||||
func printCompilerError(err error, logln func(...interface{}), wd string) {
|
||||
switch err := err.(type) {
|
||||
case types.Error:
|
||||
printCompilerError(scanner.Error{
|
||||
Pos: err.Fset.Position(err.Pos),
|
||||
Msg: err.Msg,
|
||||
}, logln, wd)
|
||||
case scanner.Error:
|
||||
if !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
|
||||
// This file is not from the standard library (either the GOROOT or
|
||||
// the TINYGOROOT). Make the path relative, for easier reading.
|
||||
// Ignore any errors in the process (falling back to the absolute
|
||||
// path).
|
||||
err.Pos.Filename = tryToMakePathRelative(err.Pos.Filename, wd)
|
||||
}
|
||||
logln(err)
|
||||
case scanner.ErrorList:
|
||||
for _, scannerErr := range err {
|
||||
printCompilerError(*scannerErr, logln, wd)
|
||||
}
|
||||
case *interp.Error:
|
||||
logln("#", err.ImportPath)
|
||||
logln(err.Error())
|
||||
if len(err.Inst) != 0 {
|
||||
logln(err.Inst)
|
||||
}
|
||||
if len(err.Traceback) > 0 {
|
||||
logln("\ntraceback:")
|
||||
for _, line := range err.Traceback {
|
||||
logln(line.Pos.String() + ":")
|
||||
logln(line.Inst)
|
||||
}
|
||||
}
|
||||
case loader.Errors:
|
||||
// Parser errors, typechecking errors, or `go list` errors.
|
||||
// err.Pkg is nil for `go list` errors.
|
||||
if err.Pkg != nil {
|
||||
logln("#", err.Pkg.ImportPath)
|
||||
}
|
||||
for _, err := range err.Errs {
|
||||
printCompilerError(err, logln, wd)
|
||||
}
|
||||
case loader.Error:
|
||||
if err.Err.Pos.Filename != "" {
|
||||
// Probably a syntax error in a dependency.
|
||||
printCompilerError(err.Err, logln, wd)
|
||||
} else {
|
||||
// Probably an "import cycle not allowed" error.
|
||||
logln("package", err.ImportStack[0])
|
||||
for i := 1; i < len(err.ImportStack); i++ {
|
||||
pkgPath := err.ImportStack[i]
|
||||
if i == len(err.ImportStack)-1 {
|
||||
// last package
|
||||
logln("\timports", pkgPath+": "+err.Err.Error())
|
||||
} else {
|
||||
// not the last pacakge
|
||||
logln("\timports", pkgPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
case *builder.MultiError:
|
||||
for _, err := range err.Errs {
|
||||
printCompilerError(err, logln, wd)
|
||||
}
|
||||
default:
|
||||
logln("error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func handleCompilerError(err error) {
|
||||
wd, getwdErr := os.Getwd()
|
||||
if getwdErr != nil {
|
||||
wd = ""
|
||||
}
|
||||
if err != nil {
|
||||
printCompilerError(err, func(args ...interface{}) {
|
||||
fmt.Fprintln(os.Stderr, args...)
|
||||
}, wd)
|
||||
wd, getwdErr := os.Getwd()
|
||||
if getwdErr != nil {
|
||||
wd = ""
|
||||
}
|
||||
diagnostics.CreateDiagnostics(err).WriteTo(os.Stderr, wd)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -1780,13 +1698,11 @@ func main() {
|
||||
stderr := (*testStderr)(buf)
|
||||
passed, err := Test(pkgName, stdout, stderr, options, outpath)
|
||||
if err != nil {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
wd, getwdErr := os.Getwd()
|
||||
if getwdErr != nil {
|
||||
wd = ""
|
||||
}
|
||||
printCompilerError(err, func(args ...interface{}) {
|
||||
fmt.Fprintln(stderr, args...)
|
||||
}, wd)
|
||||
diagnostics.CreateDiagnostics(err).WriteTo(os.Stderr, wd)
|
||||
}
|
||||
if !passed {
|
||||
select {
|
||||
|
||||
+13
-1
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/aykevl/go-wasm"
|
||||
"github.com/tinygo-org/tinygo/builder"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/diagnostics"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
@@ -36,6 +37,7 @@ var supportedLinuxArches = map[string]string{
|
||||
"X86Linux": "linux/386",
|
||||
"ARMLinux": "linux/arm/6",
|
||||
"ARM64Linux": "linux/arm64",
|
||||
"MIPSLinux": "linux/mipsle",
|
||||
"WASIp1": "wasip1/wasm",
|
||||
}
|
||||
|
||||
@@ -210,6 +212,12 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if options.GOOS == "linux" && (options.GOARCH == "mips" || options.GOARCH == "mipsle") {
|
||||
if name == "atomic.go" {
|
||||
// 64-bit atomic operations aren't currently supported on MIPS.
|
||||
continue
|
||||
}
|
||||
}
|
||||
if options.Target == "simavr" {
|
||||
// Not all tests are currently supported on AVR.
|
||||
// Skip the ones that aren't.
|
||||
@@ -380,7 +388,11 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||
return cmd.Run()
|
||||
})
|
||||
if err != nil {
|
||||
printCompilerError(err, t.Log, "")
|
||||
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
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
@@ -23,9 +24,21 @@ import (
|
||||
// only supports Elliptic Curve based groups. See RFC 8446, Section 4.2.7.
|
||||
type CurveID uint16
|
||||
|
||||
// CipherSuiteName returns the standard name for the passed cipher suite ID
|
||||
//
|
||||
// Not Implemented.
|
||||
func CipherSuiteName(id uint16) string {
|
||||
return fmt.Sprintf("0x%04X", id)
|
||||
}
|
||||
|
||||
// ConnectionState records basic TLS details about the connection.
|
||||
type ConnectionState struct {
|
||||
// TINYGO: empty; TLS connection offloaded to device
|
||||
//
|
||||
// Minimum (empty) fields for fortio.org/log http logging and others
|
||||
// to compile and run.
|
||||
PeerCertificates []*x509.Certificate
|
||||
CipherSuite uint16
|
||||
}
|
||||
|
||||
// ClientAuthType declares the policy the server will follow for
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package cm
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// 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) {
|
||||
if unsafe.Sizeof(to) > unsafe.Sizeof(from) {
|
||||
panic("reinterpret: size of to > from")
|
||||
}
|
||||
return *(*T)(unsafe.Pointer(&from))
|
||||
}
|
||||
|
||||
// LowerString lowers a [string] into a pair of Core WebAssembly types.
|
||||
//
|
||||
// [string]: https://pkg.go.dev/builtin#string
|
||||
func LowerString[S ~string](s S) (*byte, uint32) {
|
||||
return unsafe.StringData(string(s)), uint32(len(s))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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) {
|
||||
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)))
|
||||
}
|
||||
|
||||
// BoolToU32 converts a value whose underlying type is [bool] into a [uint32].
|
||||
// Used to lower a [bool] into a Core WebAssembly i32 as specified in the [Canonical ABI].
|
||||
//
|
||||
// [bool]: https://pkg.go.dev/builtin#bool
|
||||
// [uint32]: https://pkg.go.dev/builtin#uint32
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
func BoolToU32[B ~bool](v B) uint32 { return uint32(*(*uint8)(unsafe.Pointer(&v))) }
|
||||
|
||||
// U32ToBool converts a [uint32] into a [bool].
|
||||
// Used to lift a Core WebAssembly i32 into a [bool] as specified in the [Canonical ABI].
|
||||
//
|
||||
// [uint32]: https://pkg.go.dev/builtin#uint32
|
||||
// [bool]: https://pkg.go.dev/builtin#bool
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
func U32ToBool(v uint32) bool { tmp := uint8(v); return *(*bool)(unsafe.Pointer(&tmp)) }
|
||||
|
||||
// F32ToU32 maps the bits of a [float32] into a [uint32].
|
||||
// Used to lower a [float32] into a Core WebAssembly i32 as specified in the [Canonical ABI].
|
||||
//
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
// [float32]: https://pkg.go.dev/builtin#float32
|
||||
// [uint32]: https://pkg.go.dev/builtin#uint32
|
||||
func F32ToU32(v float32) uint32 { return *(*uint32)(unsafe.Pointer(&v)) }
|
||||
|
||||
// U32ToF32 maps the bits of a [uint32] into a [float32].
|
||||
// Used to lift a Core WebAssembly i32 into a [float32] as specified in the [Canonical ABI].
|
||||
//
|
||||
// [uint32]: https://pkg.go.dev/builtin#uint32
|
||||
// [float32]: https://pkg.go.dev/builtin#float32
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
func U32ToF32(v uint32) float32 { return *(*float32)(unsafe.Pointer(&v)) }
|
||||
|
||||
// F64ToU64 maps the bits of a [float64] into a [uint64].
|
||||
// Used to lower a [float64] into a Core WebAssembly i64 as specified in the [Canonical ABI].
|
||||
//
|
||||
// [float64]: https://pkg.go.dev/builtin#float64
|
||||
// [uint64]: https://pkg.go.dev/builtin#uint64
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
//
|
||||
// [uint32]: https://pkg.go.dev/builtin#uint32
|
||||
func F64ToU64(v float64) uint64 { return *(*uint64)(unsafe.Pointer(&v)) }
|
||||
|
||||
// U64ToF64 maps the bits of a [uint64] into a [float64].
|
||||
// Used to lift a Core WebAssembly i64 into a [float64] as specified in the [Canonical ABI].
|
||||
//
|
||||
// [uint64]: https://pkg.go.dev/builtin#uint64
|
||||
// [float64]: https://pkg.go.dev/builtin#float64
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
func U64ToF64(v uint64) float64 { return *(*float64)(unsafe.Pointer(&v)) }
|
||||
|
||||
// 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].
|
||||
//
|
||||
// [uint32]: https://pkg.go.dev/builtin#uint32
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
func PointerToU32[T any](v *T) uint32 { return uint32(uintptr(unsafe.Pointer(v))) }
|
||||
|
||||
// U32ToPointer converts a [uint32] into a pointer of type *T.
|
||||
// Used to lift a Core WebAssembly i32 into a pointer as specified in the [Canonical ABI].
|
||||
//
|
||||
// [uint32]: https://pkg.go.dev/builtin#uint32
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
func U32ToPointer[T any](v uint32) *T { return (*T)(unsafePointer(uintptr(v))) }
|
||||
|
||||
// PointerToU64 converts a pointer of type *T into a [uint64].
|
||||
// Used to lower a pointer into a Core WebAssembly i64 as specified in the [Canonical ABI].
|
||||
//
|
||||
// [uint64]: https://pkg.go.dev/builtin#uint64
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
func PointerToU64[T any](v *T) uint64 { return uint64(uintptr(unsafe.Pointer(v))) }
|
||||
|
||||
// U64ToPointer converts a [uint64] into a pointer of type *T.
|
||||
// Used to lift a Core WebAssembly i64 into a pointer as specified in the [Canonical ABI].
|
||||
//
|
||||
// [uint64]: https://pkg.go.dev/builtin#uint64
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
func U64ToPointer[T any](v uint64) *T { return (*T)(unsafePointer(uintptr(v))) }
|
||||
|
||||
// Appease vet, see https://github.com/golang/go/issues/58625
|
||||
func unsafePointer(p uintptr) unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&p))
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Package cm contains types and functions for interfacing with the WebAssembly Component Model.
|
||||
//
|
||||
// The types in this package (such as [List], [Option], [Result], and [Variant]) are designed to match the memory layout
|
||||
// of [Component Model] types as specified in the [Canonical ABI].
|
||||
//
|
||||
// [Component Model]: https://component-model.bytecodealliance.org/introduction.html
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment
|
||||
package cm
|
||||
@@ -0,0 +1,48 @@
|
||||
package cm
|
||||
|
||||
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] }
|
||||
|
||||
// NewList returns a List[T] from data and len.
|
||||
func NewList[T any](data *T, len uint) List[T] {
|
||||
return List[T]{
|
||||
list[T]{
|
||||
data: data,
|
||||
len: len,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ToList returns a List[T] equivalent to the Go slice s.
|
||||
// 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)))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
data *T
|
||||
len uint
|
||||
}
|
||||
|
||||
// Slice returns a Go slice representing the List.
|
||||
func (l list[T]) Slice() []T {
|
||||
return unsafe.Slice(l.data, l.len)
|
||||
}
|
||||
|
||||
// Data returns the data pointer for the list.
|
||||
func (l list[T]) Data() *T {
|
||||
return l.data
|
||||
}
|
||||
|
||||
// Len returns the length of the list.
|
||||
// TODO: should this return an int instead of a uint?
|
||||
func (l list[T]) Len() uint {
|
||||
return l.len
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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] }
|
||||
|
||||
// None returns an [Option] representing the none case,
|
||||
// equivalent to the zero value.
|
||||
func None[T any]() Option[T] {
|
||||
return Option[T]{}
|
||||
}
|
||||
|
||||
// Some returns an [Option] representing the some case.
|
||||
func Some[T any](v T) Option[T] {
|
||||
return Option[T]{
|
||||
option[T]{
|
||||
isSome: true,
|
||||
some: v,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// option represents the internal representation of a Component Model option type.
|
||||
// The first byte is a bool representing none or some,
|
||||
// followed by storage for the associated type T.
|
||||
type option[T any] struct {
|
||||
isSome bool
|
||||
some T
|
||||
}
|
||||
|
||||
// None returns true if o represents the none case.
|
||||
func (o *option[T]) None() bool {
|
||||
return !o.isSome
|
||||
}
|
||||
|
||||
// Some returns a non-nil *T if o represents the some case,
|
||||
// or nil if o represents the none case.
|
||||
func (o *option[T]) Some() *T {
|
||||
if o.isSome {
|
||||
return &o.some
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cm
|
||||
|
||||
// Resource represents an opaque Component Model [resource handle].
|
||||
// It is represented in the [Canonical ABI] as an 32-bit integer.
|
||||
//
|
||||
// [resource handle]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md#handle-types
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
type Resource uint32
|
||||
|
||||
// Rep represents a Component Model [resource rep], the core representation type of a resource.
|
||||
// It is represented in the [Canonical ABI] as an 32-bit integer.
|
||||
//
|
||||
// [resource rep]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#canon-resourcerep
|
||||
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
|
||||
type Rep uint32
|
||||
|
||||
// ResourceNone is a sentinel value indicating a null or uninitialized resource.
|
||||
// This is a reserved value specified in the [Canonical ABI runtime state].
|
||||
//
|
||||
// [Canonical ABI runtime state]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#runtime-state
|
||||
const ResourceNone = 0
|
||||
@@ -0,0 +1,107 @@
|
||||
package cm
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const (
|
||||
// ResultOK represents the OK case of a result.
|
||||
ResultOK = false
|
||||
|
||||
// ResultErr represents the error case of a result.
|
||||
ResultErr = true
|
||||
)
|
||||
|
||||
// BoolResult represents a result with no OK or error type.
|
||||
// False represents the OK case and true represents the error case.
|
||||
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] }
|
||||
|
||||
// result represents the internal representation of a Component Model result type.
|
||||
type result[Shape, OK, Err any] struct {
|
||||
isErr bool
|
||||
_ [0]OK
|
||||
_ [0]Err
|
||||
data Shape // [unsafe.Sizeof(*(*Shape)(unsafe.Pointer(nil)))]byte
|
||||
}
|
||||
|
||||
// IsOK returns true if r represents the OK case.
|
||||
func (r *result[Shape, OK, Err]) IsOK() bool {
|
||||
r.validate()
|
||||
return !r.isErr
|
||||
}
|
||||
|
||||
// IsErr returns true if r represents the error case.
|
||||
func (r *result[Shape, OK, Err]) IsErr() bool {
|
||||
r.validate()
|
||||
return r.isErr
|
||||
}
|
||||
|
||||
// OK returns a non-nil *OK pointer if r represents the OK case.
|
||||
// If r represents an error, then it returns nil.
|
||||
func (r *result[Shape, OK, Err]) OK() *OK {
|
||||
r.validate()
|
||||
if r.isErr {
|
||||
return nil
|
||||
}
|
||||
return (*OK)(unsafe.Pointer(&r.data))
|
||||
}
|
||||
|
||||
// Err returns a non-nil *Err pointer if r represents the error case.
|
||||
// If r represents the OK case, then it returns nil.
|
||||
func (r *result[Shape, OK, Err]) Err() *Err {
|
||||
r.validate()
|
||||
if !r.isErr {
|
||||
return nil
|
||||
}
|
||||
return (*Err)(unsafe.Pointer(&r.data))
|
||||
}
|
||||
|
||||
// This function is sized so it can be inlined and optimized away.
|
||||
func (r *result[Shape, OK, Err]) validate() {
|
||||
var shape Shape
|
||||
var ok OK
|
||||
var err Err
|
||||
|
||||
// Check if size of Shape is greater than both OK and Err
|
||||
if unsafe.Sizeof(shape) > unsafe.Sizeof(ok) && unsafe.Sizeof(shape) > unsafe.Sizeof(err) {
|
||||
panic("result: size of data type > OK and Err types")
|
||||
}
|
||||
|
||||
// Check if size of OK is greater than Shape
|
||||
if unsafe.Sizeof(ok) > unsafe.Sizeof(shape) {
|
||||
panic("result: size of OK type > data type")
|
||||
}
|
||||
|
||||
// Check if size of Err is greater than Shape
|
||||
if unsafe.Sizeof(err) > unsafe.Sizeof(shape) {
|
||||
panic("result: size of Err type > data type")
|
||||
}
|
||||
|
||||
// Check if Shape is zero-sized, but size of result != 1
|
||||
if unsafe.Sizeof(shape) == 0 && unsafe.Sizeof(*r) != 1 {
|
||||
panic("result: size of data type == 0, but result size != 1")
|
||||
}
|
||||
}
|
||||
|
||||
// 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] }
|
||||
r.validate()
|
||||
r.isErr = ResultOK
|
||||
*((*OK)(unsafe.Pointer(&r.data))) = ok
|
||||
return R(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] }
|
||||
r.validate()
|
||||
r.isErr = ResultErr
|
||||
*((*Err)(unsafe.Pointer(&r.data))) = err
|
||||
return R(r)
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package cm
|
||||
|
||||
// Tuple represents a [Component Model tuple] with 2 fields.
|
||||
//
|
||||
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
|
||||
type Tuple[T0, T1 any] struct {
|
||||
F0 T0
|
||||
F1 T1
|
||||
}
|
||||
|
||||
// Tuple3 represents a [Component Model tuple] with 3 fields.
|
||||
//
|
||||
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
|
||||
type Tuple3[T0, T1, T2 any] struct {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
}
|
||||
|
||||
// Tuple4 represents a [Component Model tuple] with 4 fields.
|
||||
//
|
||||
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
|
||||
type Tuple4[T0, T1, T2, T3 any] struct {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
}
|
||||
|
||||
// Tuple5 represents a [Component Model tuple] with 5 fields.
|
||||
//
|
||||
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
|
||||
type Tuple5[T0, T1, T2, T3, T4 any] struct {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
}
|
||||
|
||||
// Tuple6 represents a [Component Model tuple] with 6 fields.
|
||||
//
|
||||
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
|
||||
type Tuple6[T0, T1, T2, T3, T4, T5 any] struct {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
}
|
||||
|
||||
// Tuple7 represents a [Component Model tuple] with 7 fields.
|
||||
//
|
||||
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
|
||||
type Tuple7[T0, T1, T2, T3, T4, T5, T6 any] struct {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
}
|
||||
|
||||
// Tuple8 represents a [Component Model tuple] with 8 fields.
|
||||
//
|
||||
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
|
||||
type Tuple8[T0, T1, T2, T3, T4, T5, T6, T7 any] struct {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
F7 T7
|
||||
}
|
||||
|
||||
// Tuple9 represents a [Component Model tuple] with 9 fields.
|
||||
//
|
||||
// [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 {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
F7 T7
|
||||
F8 T8
|
||||
}
|
||||
|
||||
// Tuple10 represents a [Component Model tuple] with 10 fields.
|
||||
//
|
||||
// [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 {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
F7 T7
|
||||
F8 T8
|
||||
F9 T9
|
||||
}
|
||||
|
||||
// Tuple11 represents a [Component Model tuple] with 11 fields.
|
||||
//
|
||||
// [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 {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
F7 T7
|
||||
F8 T8
|
||||
F9 T9
|
||||
F10 T10
|
||||
}
|
||||
|
||||
// Tuple12 represents a [Component Model tuple] with 12 fields.
|
||||
//
|
||||
// [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 {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
F7 T7
|
||||
F8 T8
|
||||
F9 T9
|
||||
F10 T10
|
||||
F11 T11
|
||||
}
|
||||
|
||||
// Tuple13 represents a [Component Model tuple] with 13 fields.
|
||||
//
|
||||
// [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 {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
F7 T7
|
||||
F8 T8
|
||||
F9 T9
|
||||
F10 T10
|
||||
F11 T11
|
||||
F12 T12
|
||||
}
|
||||
|
||||
// Tuple14 represents a [Component Model tuple] with 14 fields.
|
||||
//
|
||||
// [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 {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
F7 T7
|
||||
F8 T8
|
||||
F9 T9
|
||||
F10 T10
|
||||
F11 T11
|
||||
F12 T12
|
||||
F13 T13
|
||||
}
|
||||
|
||||
// Tuple15 represents a [Component Model tuple] with 15 fields.
|
||||
//
|
||||
// [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 {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
F7 T7
|
||||
F8 T8
|
||||
F9 T9
|
||||
F10 T10
|
||||
F11 T11
|
||||
F12 T12
|
||||
F13 T13
|
||||
F14 T14
|
||||
}
|
||||
|
||||
// Tuple16 represents a [Component Model tuple] with 16 fields.
|
||||
//
|
||||
// [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 {
|
||||
F0 T0
|
||||
F1 T1
|
||||
F2 T2
|
||||
F3 T3
|
||||
F4 T4
|
||||
F5 T5
|
||||
F6 T6
|
||||
F7 T7
|
||||
F8 T8
|
||||
F9 T9
|
||||
F10 T10
|
||||
F11 T11
|
||||
F12 T12
|
||||
F13 T13
|
||||
F14 T14
|
||||
F15 T15
|
||||
}
|
||||
|
||||
// MaxTuple specifies the maximum number of fields in a Tuple* type, currently [Tuple16].
|
||||
// See https://github.com/WebAssembly/component-model/issues/373 for more information.
|
||||
const MaxTuple = 16
|
||||
@@ -0,0 +1,74 @@
|
||||
package cm
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Discriminant is the set of types that can represent the tag or discriminator of a variant.
|
||||
// Use bool for 2-case variant types, result<T>, or option<T> types, uint8 where there are 256 or
|
||||
// fewer cases, uint16 for up to 65,536 cases, or uint32 for anything greater.
|
||||
type Discriminant interface {
|
||||
bool | uint8 | uint16 | uint32
|
||||
}
|
||||
|
||||
// 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] }
|
||||
|
||||
// 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.
|
||||
func NewVariant[Tag Discriminant, Shape, Align any, T any](tag Tag, data T) Variant[Tag, Shape, Align] {
|
||||
validateVariant[Tag, Shape, Align, T]()
|
||||
var v Variant[Tag, Shape, Align]
|
||||
v.tag = tag
|
||||
*(*T)(unsafe.Pointer(&v.data)) = data
|
||||
return v
|
||||
}
|
||||
|
||||
// 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 {
|
||||
validateVariant[Tag, Shape, Align, T]()
|
||||
var v variant[Tag, Shape, Align]
|
||||
v.tag = tag
|
||||
*(*T)(unsafe.Pointer(&v.data)) = data
|
||||
return *(*V)(unsafe.Pointer(&v))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
validateVariant[Tag, Shape, Align, T]()
|
||||
v2 := (*variant[Tag, Shape, Align])(unsafe.Pointer(v))
|
||||
if v2.tag == tag {
|
||||
return (*T)(unsafe.Pointer(&v2.data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
tag Tag
|
||||
_ [0]Align
|
||||
data Shape // [unsafe.Sizeof(*(*Shape)(unsafe.Pointer(nil)))]byte
|
||||
}
|
||||
|
||||
// Tag returns the tag (discriminant) of variant v.
|
||||
func (v *variant[Tag, Shape, Align]) Tag() Tag {
|
||||
return v.tag
|
||||
}
|
||||
|
||||
// This function is sized so it can be inlined and optimized away.
|
||||
func validateVariant[Disc Discriminant, Shape, Align any, T any]() {
|
||||
var v variant[Disc, Shape, Align]
|
||||
var t T
|
||||
|
||||
// Check if size of T is greater than Shape
|
||||
if unsafe.Sizeof(t) > unsafe.Sizeof(v.data) {
|
||||
panic("variant: size of requested type > data type")
|
||||
}
|
||||
|
||||
// Check if Shape is zero-sized, but size of result != 1
|
||||
if unsafe.Sizeof(v.data) == 0 && unsafe.Sizeof(v) != 1 {
|
||||
panic("variant: size of data type == 0, but variant size != 1")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
// Most importantly, s0 contains the pc of the to-be-started function and s1
|
||||
// contains the only argument it is given. Multiple arguments are packed
|
||||
// into one by storing them in a new allocation.
|
||||
|
||||
// Set the first argument of the goroutine start wrapper, which contains all
|
||||
// the arguments.
|
||||
move $a0, $s1
|
||||
|
||||
// Branch to the "goroutine start" function. Use jalr to write the return
|
||||
// address to ra so we'll return here after the goroutine exits.
|
||||
jalr $s0
|
||||
nop
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
j tinygo_pause
|
||||
nop
|
||||
|
||||
.section .text.tinygo_swapTask
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
// This function gets the following parameters:
|
||||
// a0 = newStack uintptr
|
||||
// a1 = oldStack *uintptr
|
||||
|
||||
// Push all callee-saved registers.
|
||||
addiu $sp, $sp, -40
|
||||
sw $ra, 36($sp)
|
||||
sw $s8, 32($sp)
|
||||
sw $s7, 28($sp)
|
||||
sw $s6, 24($sp)
|
||||
sw $s5, 20($sp)
|
||||
sw $s4, 16($sp)
|
||||
sw $s3, 12($sp)
|
||||
sw $s2, 8($sp)
|
||||
sw $s1, 4($sp)
|
||||
sw $s0, ($sp)
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
sw $sp, 0($a1)
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
move $sp, $a0
|
||||
|
||||
// Pop all saved registers from this new stack.
|
||||
lw $ra, 36($sp)
|
||||
lw $s8, 32($sp)
|
||||
lw $s7, 28($sp)
|
||||
lw $s6, 24($sp)
|
||||
lw $s5, 20($sp)
|
||||
lw $s4, 16($sp)
|
||||
lw $s3, 12($sp)
|
||||
lw $s2, 8($sp)
|
||||
lw $s1, 4($sp)
|
||||
lw $s0, ($sp)
|
||||
addiu $sp, $sp, 40
|
||||
|
||||
// Return into the task.
|
||||
jalr $ra
|
||||
nop
|
||||
@@ -0,0 +1,61 @@
|
||||
//go:build scheduler.tasks && (mips || mipsle)
|
||||
|
||||
package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_mips.S that relies on the exact
|
||||
// layout of this struct.
|
||||
type calleeSavedRegs struct {
|
||||
s0 uintptr
|
||||
s1 uintptr
|
||||
s2 uintptr
|
||||
s3 uintptr
|
||||
s4 uintptr
|
||||
s5 uintptr
|
||||
s6 uintptr
|
||||
s7 uintptr
|
||||
s8 uintptr
|
||||
ra uintptr
|
||||
}
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
// Store the initial sp for the startTask function (implemented in assembly).
|
||||
s.sp = uintptr(unsafe.Pointer(r))
|
||||
|
||||
// Initialize the registers.
|
||||
// These will be popped off of the stack on the first resume of the goroutine.
|
||||
|
||||
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_mipsle.S).
|
||||
// This assembly code calls a function (passed in s0) with a single argument
|
||||
// (passed in s1). After the function returns, it calls Pause().
|
||||
r.ra = uintptr(unsafe.Pointer(&startTask))
|
||||
|
||||
// Pass the function to call in s0.
|
||||
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
|
||||
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
|
||||
r.s0 = fn
|
||||
|
||||
// Pass the pointer to the arguments struct in s1.
|
||||
r.s1 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
}
|
||||
|
||||
// SystemStack returns the system stack pointer when called from a task stack.
|
||||
// When called from the system stack, it returns 0.
|
||||
func SystemStack() uintptr {
|
||||
return systemStack
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// GetEnvironment represents the imported function "get-environment".
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package exit
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// Exit represents the imported function "exit".
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// Exports represents the caller-defined exports from "wasi:cli/run@0.2.0".
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
//go:wasmexport wasi:cli/run@0.2.0#run
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package stderr
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/io/v0.2.0/streams"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package stdin
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/io/v0.2.0/streams"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package stdout
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/io/v0.2.0/streams"
|
||||
)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
package terminalinput
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// TerminalInput represents the imported resource "wasi:cli/terminal-input@0.2.0#terminal-input".
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
package terminaloutput
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// TerminalOutput represents the imported resource "wasi:cli/terminal-output@0.2.0#terminal-output".
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
package terminalstderr
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
terminaloutput "internal/wasi/cli/v0.2.0/terminal-output"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
package terminalstdin
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
terminalinput "internal/wasi/cli/v0.2.0/terminal-input"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
package terminalstdout
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
terminaloutput "internal/wasi/cli/v0.2.0/terminal-output"
|
||||
)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package monotonicclock
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/io/v0.2.0/poll"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package preopens
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/filesystem/v0.2.0/types"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
wallclock "internal/wasi/clocks/v0.2.0/wall-clock"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
wallclock "internal/wasi/clocks/v0.2.0/wall-clock"
|
||||
ioerror "internal/wasi/io/v0.2.0/error"
|
||||
"internal/wasi/io/v0.2.0/streams"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package ioerror
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// Error represents the imported resource "wasi:io/error@0.2.0#error".
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
package poll
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// Pollable represents the imported resource "wasi:io/poll@0.2.0#pollable".
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
package streams
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
ioerror "internal/wasi/io/v0.2.0/error"
|
||||
"internal/wasi/io/v0.2.0/poll"
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
package insecure
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// GetInsecureRandomBytes represents the imported function "get-insecure-random-bytes".
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
package random
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// GetRandomBytes represents the imported function "get-random-bytes".
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
package instancenetwork
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/sockets/v0.2.0/network"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package ipnamelookup
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/sockets/v0.2.0/network"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package ipnamelookup
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/io/v0.2.0/poll"
|
||||
"internal/wasi/sockets/v0.2.0/network"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
)
|
||||
|
||||
// Network represents the imported resource "wasi:sockets/network@0.2.0#network".
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package tcpcreatesocket
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/sockets/v0.2.0/network"
|
||||
"internal/wasi/sockets/v0.2.0/tcp"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/io/v0.2.0/streams"
|
||||
"internal/wasi/sockets/v0.2.0/network"
|
||||
"unsafe"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
monotonicclock "internal/wasi/clocks/v0.2.0/monotonic-clock"
|
||||
"internal/wasi/io/v0.2.0/poll"
|
||||
"internal/wasi/io/v0.2.0/streams"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package udpcreatesocket
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/sockets/v0.2.0/network"
|
||||
"internal/wasi/sockets/v0.2.0/udp"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/sockets/v0.2.0/network"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
"internal/cm"
|
||||
"internal/wasi/io/v0.2.0/poll"
|
||||
"internal/wasi/sockets/v0.2.0/network"
|
||||
)
|
||||
|
||||
@@ -501,15 +501,23 @@ func (i2c *I2C) Reply(buf []byte) error {
|
||||
}
|
||||
|
||||
for txPtr < len(buf) {
|
||||
if stat&rp.I2C0_IC_INTR_MASK_M_TX_EMPTY != 0 {
|
||||
i2c.Bus.IC_DATA_CMD.Set(uint32(buf[txPtr]))
|
||||
if i2c.Bus.GetIC_RAW_INTR_STAT_TX_EMPTY() != 0 {
|
||||
i2c.Bus.SetIC_DATA_CMD_DAT(uint32(buf[txPtr]))
|
||||
txPtr++
|
||||
// The DW_apb_i2c flushes/resets/empties the
|
||||
// TX_FIFO and RX_FIFO whenever there is a transmit abort
|
||||
// caused by any of the events tracked by the
|
||||
// IC_TX_ABRT_SOURCE register.
|
||||
// In other words, it's safe to block until TX FIFO is
|
||||
// EMPTY--it will empty from being transmitted or on error.
|
||||
for i2c.Bus.GetIC_RAW_INTR_STAT_TX_EMPTY() == 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// This Tx abort is a normal case - we're sending more
|
||||
// data than controller wants to receive
|
||||
if stat&rp.I2C0_IC_INTR_MASK_M_TX_ABRT != 0 {
|
||||
i2c.Bus.IC_CLR_TX_ABRT.Get()
|
||||
if i2c.Bus.GetIC_RAW_INTR_STAT_TX_ABRT() != 0 {
|
||||
i2c.Bus.GetIC_CLR_TX_ABRT_CLR_TX_ABRT()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+1
-1
Submodule src/net updated: a79417481d...a237059610
@@ -147,6 +147,20 @@ func Chmod(name string, mode FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chown changes the numeric uid and gid of the named file.
|
||||
// If the file is a symbolic link, it changes the uid and gid of the link's target.
|
||||
// A uid or gid of -1 means to not change that value.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Chown(name string, uid, gid int) error {
|
||||
e := ignoringEINTR(func() error {
|
||||
return syscall.Chown(name, uid, gid)
|
||||
})
|
||||
if e != nil {
|
||||
return &PathError{Op: "chown", Path: name, Err: e}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ignoringEINTR makes a function call and repeats it if it returns an
|
||||
// EINTR error. This appears to be required even though we install all
|
||||
// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.
|
||||
|
||||
@@ -9,12 +9,15 @@
|
||||
package os_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
. "os"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChmod(t *testing.T) {
|
||||
// Chmod
|
||||
f := newFile("TestChmod", t)
|
||||
defer Remove(f.Name())
|
||||
defer f.Close()
|
||||
@@ -28,4 +31,42 @@ func TestChmod(t *testing.T) {
|
||||
t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
|
||||
}
|
||||
checkMode(t, f.Name(), fm)
|
||||
|
||||
}
|
||||
|
||||
// Since testing syscalls requires a static, predictable environment that has to be controlled
|
||||
// by the CI, we don't test for success but for failures and verify that the error messages are as expected.
|
||||
// EACCES is returned when the user does not have the required permissions to change the ownership of the file
|
||||
// ENOENT is returned when the file does not exist
|
||||
// ENOTDIR is returned when the file is not a directory
|
||||
func TestChownErr(t *testing.T) {
|
||||
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
|
||||
t.Log("skipping on " + runtime.GOOS)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
TEST_UID_ROOT = 0
|
||||
TEST_GID_ROOT = 0
|
||||
)
|
||||
|
||||
f := newFile("TestChown", t)
|
||||
defer Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
// EACCES
|
||||
if err := Chown(f.Name(), TEST_UID_ROOT, TEST_GID_ROOT); err != nil {
|
||||
errCmp := fs.PathError{Op: "chown", Path: f.Name(), Err: errors.New("operation not permitted")}
|
||||
if errors.Is(err, &errCmp) {
|
||||
t.Fatalf("chown(%s, uid=%v, gid=%v): got '%v', want 'operation not permitted'", f.Name(), TEST_UID_ROOT, TEST_GID_ROOT, err)
|
||||
}
|
||||
}
|
||||
|
||||
// ENOENT
|
||||
if err := Chown("invalid", Geteuid(), Getgid()); err != nil {
|
||||
errCmp := fs.PathError{Op: "chown", Path: "invalid", Err: errors.New("no such file or directory")}
|
||||
if errors.Is(err, &errCmp) {
|
||||
t.Fatalf("chown(%s, uid=%v, gid=%v): got '%v', want 'no such file or directory'", f.Name(), Geteuid(), Getegid(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package reflect
|
||||
|
||||
// This file defines functions that logically belong in the runtime but that
|
||||
// depend on the internals of the reflect package and are therefore better
|
||||
// implemented directly in the reflect package.
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Return whether the concrete type implements the asserted method set.
|
||||
// This function is called both from the compiler and from the reflect package.
|
||||
//
|
||||
//go:linkname typeImplementsMethodSet runtime.typeImplementsMethodSet
|
||||
func typeImplementsMethodSet(concreteType, assertedMethodSet unsafe.Pointer) bool {
|
||||
if concreteType == nil {
|
||||
// This can happen when doing a type assert on a nil interface value.
|
||||
// According to the Go language specification, a type assert on a nil
|
||||
// interface always fails.
|
||||
return false
|
||||
}
|
||||
|
||||
const ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
itfNumMethod := *(*uintptr)(assertedMethodSet)
|
||||
if itfNumMethod == 0 {
|
||||
// While the compiler shouldn't emit such type asserts, the reflect
|
||||
// package may do so.
|
||||
// This is actually needed for correctness: for example a value of type
|
||||
// int may be assigned to a variable of type interface{}.
|
||||
return true
|
||||
}
|
||||
|
||||
// Pull the method set out of the concrete type.
|
||||
var methods *methodSet
|
||||
metaByte := *(*uint8)(concreteType)
|
||||
if metaByte&flagNamed != 0 {
|
||||
concreteType := (*namedType)(concreteType)
|
||||
methods = &concreteType.methods
|
||||
} else if metaByte&kindMask == uint8(Struct) {
|
||||
concreteType := (*structType)(concreteType)
|
||||
methods = &concreteType.methods
|
||||
} else if metaByte&kindMask == uint8(Pointer) {
|
||||
concreteType := (*ptrType)(concreteType)
|
||||
methods = &concreteType.methods
|
||||
} else {
|
||||
// Other types don't have a method set.
|
||||
return false
|
||||
}
|
||||
concreteTypePtr := unsafe.Pointer(&methods.methods)
|
||||
concreteTypeEnd := unsafe.Add(concreteTypePtr, uintptr(methods.length)*ptrSize)
|
||||
|
||||
// Iterate over each method in the interface method set, and check whether
|
||||
// the method exists in the method set of the concrete type.
|
||||
// Both method sets are sorted in the same way, so we can use a simple loop
|
||||
// to check for a match.
|
||||
assertedTypePtr := unsafe.Add(assertedMethodSet, ptrSize)
|
||||
assertedTypeEnd := unsafe.Add(assertedTypePtr, itfNumMethod*ptrSize)
|
||||
for assertedTypePtr != assertedTypeEnd {
|
||||
assertedMethod := *(*unsafe.Pointer)(assertedTypePtr)
|
||||
|
||||
// Search for the same method in the concrete type.
|
||||
for {
|
||||
if concreteTypePtr == concreteTypeEnd {
|
||||
// Reached the end of the concrete type method set.
|
||||
// The method wasn't found, so the type assert failed.
|
||||
return false
|
||||
}
|
||||
concreteMethod := *(*unsafe.Pointer)(concreteTypePtr)
|
||||
concreteTypePtr = unsafe.Add(concreteTypePtr, ptrSize)
|
||||
if concreteMethod == assertedMethod {
|
||||
// Found the method in the concrete type. Continue with the
|
||||
// next in the interface method set.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// The method was found. Continue with the next.
|
||||
assertedTypePtr = unsafe.Add(assertedTypePtr, ptrSize)
|
||||
}
|
||||
|
||||
// All methods in the interface were found.
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package reflect_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
. "reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//func TestImplicitMapConversion(t *testing.T) {
|
||||
// // Test implicit conversions in MapIndex and SetMapIndex.
|
||||
// {
|
||||
// // direct
|
||||
// m := make(map[int]int)
|
||||
// mv := ValueOf(m)
|
||||
// mv.SetMapIndex(ValueOf(1), ValueOf(2))
|
||||
// x, ok := m[1]
|
||||
// if x != 2 {
|
||||
// t.Errorf("#1 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
|
||||
// }
|
||||
// if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
|
||||
// t.Errorf("#1 MapIndex(1) = %d", n)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert interface key
|
||||
// m := make(map[any]int)
|
||||
// mv := ValueOf(m)
|
||||
// mv.SetMapIndex(ValueOf(1), ValueOf(2))
|
||||
// x, ok := m[1]
|
||||
// if x != 2 {
|
||||
// t.Errorf("#2 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
|
||||
// }
|
||||
// if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
|
||||
// t.Errorf("#2 MapIndex(1) = %d", n)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert interface value
|
||||
// m := make(map[int]any)
|
||||
// mv := ValueOf(m)
|
||||
// mv.SetMapIndex(ValueOf(1), ValueOf(2))
|
||||
// x, ok := m[1]
|
||||
// if x != 2 {
|
||||
// t.Errorf("#3 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
|
||||
// }
|
||||
// if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
|
||||
// t.Errorf("#3 MapIndex(1) = %d", n)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert both interface key and interface value
|
||||
// m := make(map[any]any)
|
||||
// mv := ValueOf(m)
|
||||
// mv.SetMapIndex(ValueOf(1), ValueOf(2))
|
||||
// x, ok := m[1]
|
||||
// if x != 2 {
|
||||
// t.Errorf("#4 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
|
||||
// }
|
||||
// if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
|
||||
// t.Errorf("#4 MapIndex(1) = %d", n)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert both, with non-empty interfaces
|
||||
// m := make(map[io.Reader]io.Writer)
|
||||
// mv := ValueOf(m)
|
||||
// b1 := new(bytes.Buffer)
|
||||
// b2 := new(bytes.Buffer)
|
||||
// mv.SetMapIndex(ValueOf(b1), ValueOf(b2))
|
||||
// x, ok := m[b1]
|
||||
// if x != b2 {
|
||||
// t.Errorf("#5 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
|
||||
// }
|
||||
// if p := mv.MapIndex(ValueOf(b1)).Elem().UnsafePointer(); p != unsafe.Pointer(b2) {
|
||||
// t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert channel direction
|
||||
// m := make(map[<-chan int]chan int)
|
||||
// mv := ValueOf(m)
|
||||
// c1 := make(chan int)
|
||||
// c2 := make(chan int)
|
||||
// mv.SetMapIndex(ValueOf(c1), ValueOf(c2))
|
||||
// x, ok := m[c1]
|
||||
// if x != c2 {
|
||||
// t.Errorf("#6 after SetMapIndex(c1, c2): %p (!= %p), %t (map=%v)", x, c2, ok, m)
|
||||
// }
|
||||
// if p := mv.MapIndex(ValueOf(c1)).UnsafePointer(); p != ValueOf(c2).UnsafePointer() {
|
||||
// t.Errorf("#6 MapIndex(c1) = %#x want %p", p, c2)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert identical underlying types
|
||||
// type MyBuffer bytes.Buffer
|
||||
// m := make(map[*MyBuffer]*bytes.Buffer)
|
||||
// mv := ValueOf(m)
|
||||
// b1 := new(MyBuffer)
|
||||
// b2 := new(bytes.Buffer)
|
||||
// mv.SetMapIndex(ValueOf(b1), ValueOf(b2))
|
||||
// x, ok := m[b1]
|
||||
// if x != b2 {
|
||||
// t.Errorf("#7 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
|
||||
// }
|
||||
// if p := mv.MapIndex(ValueOf(b1)).UnsafePointer(); p != unsafe.Pointer(b2) {
|
||||
// t.Errorf("#7 MapIndex(b1) = %#x want %p", p, b2)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func TestImplicitSetConversion(t *testing.T) {
|
||||
// // Assume TestImplicitMapConversion covered the basics.
|
||||
// // Just make sure conversions are being applied at all.
|
||||
// var r io.Reader
|
||||
// b := new(bytes.Buffer)
|
||||
// rv := ValueOf(&r).Elem()
|
||||
// rv.Set(ValueOf(b))
|
||||
// if r != b {
|
||||
// t.Errorf("after Set: r=%T(%v)", r, r)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestImplicitSendConversion(t *testing.T) {
|
||||
// c := make(chan io.Reader, 10)
|
||||
// b := new(bytes.Buffer)
|
||||
// ValueOf(c).Send(ValueOf(b))
|
||||
// if bb := <-c; bb != b {
|
||||
// t.Errorf("Received %p != %p", bb, b)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestImplicitCallConversion(t *testing.T) {
|
||||
// // Arguments must be assignable to parameter types.
|
||||
// fv := ValueOf(io.WriteString)
|
||||
// b := new(strings.Builder)
|
||||
// fv.Call([]Value{ValueOf(b), ValueOf("hello world")})
|
||||
// if b.String() != "hello world" {
|
||||
// t.Errorf("After call: string=%q want %q", b.String(), "hello world")
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestImplicitAppendConversion(t *testing.T) {
|
||||
// // Arguments must be assignable to the slice's element type.
|
||||
// s := []io.Reader{}
|
||||
// sv := ValueOf(&s).Elem()
|
||||
// b := new(bytes.Buffer)
|
||||
// sv.Set(Append(sv, ValueOf(b)))
|
||||
// if len(s) != 1 || s[0] != b {
|
||||
// t.Errorf("after append: s=%v want [%p]", s, b)
|
||||
// }
|
||||
//}
|
||||
|
||||
var implementsTests = []struct {
|
||||
x any
|
||||
t any
|
||||
b bool
|
||||
}{
|
||||
{new(*bytes.Buffer), new(io.Reader), true},
|
||||
{new(bytes.Buffer), new(io.Reader), false},
|
||||
{new(*bytes.Buffer), new(io.ReaderAt), false},
|
||||
{new(*ast.Ident), new(ast.Expr), true},
|
||||
{new(*notAnExpr), new(ast.Expr), false},
|
||||
{new(*ast.Ident), new(notASTExpr), false},
|
||||
{new(notASTExpr), new(ast.Expr), false},
|
||||
{new(ast.Expr), new(notASTExpr), false},
|
||||
{new(*notAnExpr), new(notASTExpr), true},
|
||||
}
|
||||
|
||||
type notAnExpr struct{}
|
||||
|
||||
func (notAnExpr) Pos() token.Pos { return token.NoPos }
|
||||
func (notAnExpr) End() token.Pos { return token.NoPos }
|
||||
func (notAnExpr) exprNode() {}
|
||||
|
||||
type notASTExpr interface {
|
||||
Pos() token.Pos
|
||||
End() token.Pos
|
||||
exprNode()
|
||||
}
|
||||
|
||||
func TestImplements(t *testing.T) {
|
||||
for _, tt := range implementsTests {
|
||||
xv := TypeOf(tt.x).Elem()
|
||||
xt := TypeOf(tt.t).Elem()
|
||||
if b := xv.Implements(xt); b != tt.b {
|
||||
t.Errorf("(%s).Implements(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var assignableTests = []struct {
|
||||
x any
|
||||
t any
|
||||
b bool
|
||||
}{
|
||||
{new(chan int), new(<-chan int), true},
|
||||
{new(<-chan int), new(chan int), false},
|
||||
{new(*int), new(IntPtr), true},
|
||||
{new(IntPtr), new(*int), true},
|
||||
{new(IntPtr), new(IntPtr1), false},
|
||||
{new(Ch), new(<-chan any), true},
|
||||
// test runs implementsTests too
|
||||
}
|
||||
|
||||
type IntPtr *int
|
||||
type IntPtr1 *int
|
||||
type Ch <-chan any
|
||||
|
||||
func TestAssignableTo(t *testing.T) {
|
||||
for _, tt := range append(assignableTests, implementsTests...) {
|
||||
xv := TypeOf(tt.x).Elem()
|
||||
xt := TypeOf(tt.t).Elem()
|
||||
if b := xv.AssignableTo(xt); b != tt.b {
|
||||
t.Errorf("(%s).AssignableTo(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
-9
@@ -23,6 +23,7 @@
|
||||
// meta uint8
|
||||
// nmethods uint16
|
||||
// elementType *typeStruct
|
||||
// methods methodSet
|
||||
// - array types (see arrayType)
|
||||
// meta uint8
|
||||
// nmethods uint16 (0)
|
||||
@@ -44,7 +45,8 @@
|
||||
// pkgpath *byte // package path; null terminated
|
||||
// numField uint16
|
||||
// fields [...]structField // the remaining fields are all of type structField
|
||||
// - interface types (this is missing the interface methods):
|
||||
// methods methodSet
|
||||
// - interface types:
|
||||
// meta uint8
|
||||
// ptrTo *typeStruct
|
||||
// - signature types (this is missing input and output parameters):
|
||||
@@ -56,6 +58,7 @@
|
||||
// ptrTo *typeStruct
|
||||
// elem *typeStruct // underlying type
|
||||
// pkgpath *byte // pkgpath; null terminated
|
||||
// methods methodSet
|
||||
// name [1]byte // actual name; null terminated
|
||||
//
|
||||
// The type struct is essentially a union of all the above types. Which it is,
|
||||
@@ -420,6 +423,7 @@ type ptrType struct {
|
||||
rawType
|
||||
numMethod uint16
|
||||
elem *rawType
|
||||
methods methodSet
|
||||
}
|
||||
|
||||
type arrayType struct {
|
||||
@@ -445,6 +449,7 @@ type namedType struct {
|
||||
ptrTo *rawType
|
||||
elem *rawType
|
||||
pkg *byte
|
||||
methods methodSet
|
||||
name [1]byte
|
||||
}
|
||||
|
||||
@@ -463,6 +468,7 @@ type structType struct {
|
||||
size uint32
|
||||
numField uint16
|
||||
fields [1]structField // the remaining fields are all of type structField
|
||||
methods methodSet
|
||||
}
|
||||
|
||||
type structField struct {
|
||||
@@ -470,6 +476,18 @@ type structField struct {
|
||||
data unsafe.Pointer // various bits of information, packed in a byte array
|
||||
}
|
||||
|
||||
type interfaceType struct {
|
||||
rawType
|
||||
ptrTo *rawType
|
||||
methods methodSet
|
||||
}
|
||||
|
||||
// Method set, as emitted by the compiler.
|
||||
type methodSet struct {
|
||||
length uintptr
|
||||
methods [0]*byte // variable number of methods
|
||||
}
|
||||
|
||||
// Equivalent to (go/types.Type).Underlying(): if this is a named type return
|
||||
// the underlying type, else just return the type itself.
|
||||
func (t *rawType) underlying() *rawType {
|
||||
@@ -954,17 +972,52 @@ func (t *rawType) FieldAlign() int {
|
||||
// AssignableTo returns whether a value of type t can be assigned to a variable
|
||||
// of type u.
|
||||
func (t *rawType) AssignableTo(u Type) bool {
|
||||
if t == u.(*rawType) {
|
||||
return true
|
||||
}
|
||||
|
||||
if u.Kind() == Interface && u.NumMethod() == 0 {
|
||||
// Quotes come from the language spec:
|
||||
// https://go.dev/ref/spec#Assignability
|
||||
// > A value x of type V is assignable to a variable of type T ("x is
|
||||
// > assignable to T") if one of the following conditions applies:
|
||||
// [...]
|
||||
// (Replace T with t, and x with u).
|
||||
u_raw := u.(*rawType)
|
||||
if t == u_raw {
|
||||
// > V and T are identical.
|
||||
return true
|
||||
}
|
||||
|
||||
if u.Kind() == Interface {
|
||||
panic("reflect: unimplemented: AssignableTo with interface")
|
||||
// > T is an interface type, but not a type parameter, and x implements
|
||||
// > T.
|
||||
u_itf := (*interfaceType)(unsafe.Pointer(u_raw.underlying()))
|
||||
res := typeImplementsMethodSet(unsafe.Pointer(t), unsafe.Pointer(&u_itf.methods))
|
||||
return res
|
||||
}
|
||||
|
||||
t_named := t.isNamed()
|
||||
u_named := u_raw.isNamed()
|
||||
if t_named && u_named {
|
||||
return false
|
||||
}
|
||||
if t.underlying() == u_raw.underlying() {
|
||||
// > V and T have identical underlying types but are not type parameters
|
||||
// > and at least one of V or T is not a named type.
|
||||
return true
|
||||
}
|
||||
|
||||
if t.Kind() == Chan && u_raw.Kind() == Chan {
|
||||
// > V and T are channel types with identical element types, V is a
|
||||
// > bidirectional channel, and at least one of V or T is not a named
|
||||
// > type.
|
||||
t_chan := (*elemType)(unsafe.Pointer(t.underlying()))
|
||||
u_chan := (*elemType)(unsafe.Pointer(u_raw.underlying()))
|
||||
if t_chan.elem != u_chan.elem {
|
||||
return false
|
||||
}
|
||||
if t_chan.ChanDir() != BothDir {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -972,7 +1025,8 @@ func (t *rawType) Implements(u Type) bool {
|
||||
if u.Kind() != Interface {
|
||||
panic("reflect: non-interface type passed to Type.Implements")
|
||||
}
|
||||
return t.AssignableTo(u)
|
||||
u_itf := (*interfaceType)(unsafe.Pointer(u.(*rawType).underlying()))
|
||||
return typeImplementsMethodSet(unsafe.Pointer(t), unsafe.Pointer(&u_itf.methods))
|
||||
}
|
||||
|
||||
// Comparable returns whether values of this type can be compared to each other.
|
||||
@@ -1046,7 +1100,9 @@ func readStringZ(data unsafe.Pointer) string {
|
||||
|
||||
func (t *rawType) name() string {
|
||||
ntype := (*namedType)(unsafe.Pointer(t))
|
||||
return readStringZ(unsafe.Pointer(&ntype.name[0]))
|
||||
ptr := unsafe.Pointer(&ntype.name[0])
|
||||
ptr = unsafe.Add(ptr, uintptr(ntype.methods.length)*unsafe.Sizeof(unsafe.Pointer(nil)))
|
||||
return readStringZ(ptr)
|
||||
}
|
||||
|
||||
func (t *rawType) Name() string {
|
||||
|
||||
@@ -592,15 +592,6 @@ func TestTinyNumMethods(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssignableTo(t *testing.T) {
|
||||
var a any
|
||||
refa := ValueOf(&a).Elem()
|
||||
refa.Set(ValueOf(4))
|
||||
if got, want := refa.Interface().(int), 4; got != want {
|
||||
t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
v := ValueOf(int64(3))
|
||||
c := v.Convert(TypeOf(byte(0)))
|
||||
|
||||
@@ -9,6 +9,8 @@ const deferExtraRegs = 0
|
||||
|
||||
const callInstSize = 5 // "call someFunction" is 5 bytes
|
||||
|
||||
const linux_MAP_ANONYMOUS = 0x20
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 15) &^ 15
|
||||
|
||||
@@ -9,6 +9,8 @@ const deferExtraRegs = 0
|
||||
|
||||
const callInstSize = 5 // "call someFunction" is 5 bytes
|
||||
|
||||
const linux_MAP_ANONYMOUS = 0x20
|
||||
|
||||
// Align a pointer.
|
||||
// Note that some amd64 instructions (like movaps) expect 16-byte aligned
|
||||
// memory, thus the result must be 16-byte aligned.
|
||||
|
||||
@@ -11,6 +11,8 @@ const deferExtraRegs = 0
|
||||
|
||||
const callInstSize = 4 // "bl someFunction" is 4 bytes
|
||||
|
||||
const linux_MAP_ANONYMOUS = 0x20
|
||||
|
||||
// Align on the maximum alignment for this platform (double).
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 7) &^ 7
|
||||
|
||||
@@ -9,6 +9,8 @@ const deferExtraRegs = 0
|
||||
|
||||
const callInstSize = 4 // "bl someFunction" is 4 bytes
|
||||
|
||||
const linux_MAP_ANONYMOUS = 0x20
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 15) &^ 15
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package runtime
|
||||
|
||||
const GOARCH = "mips"
|
||||
|
||||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 32
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
const callInstSize = 8 // "jal someFunc" is 4 bytes, plus a MIPS delay slot
|
||||
|
||||
const linux_MAP_ANONYMOUS = 0x800
|
||||
|
||||
// It appears that MIPS has a maximum alignment of 8 bytes.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 7) &^ 7
|
||||
}
|
||||
|
||||
func getCurrentStackPointer() uintptr {
|
||||
return uintptr(stacksave())
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package runtime
|
||||
|
||||
const GOARCH = "mipsle"
|
||||
|
||||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 32
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
const callInstSize = 8 // "jal someFunc" is 4 bytes, plus a MIPS delay slot
|
||||
|
||||
const linux_MAP_ANONYMOUS = 0x800
|
||||
|
||||
// It appears that MIPS has a maximum alignment of 8 bytes.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 7) &^ 7
|
||||
}
|
||||
|
||||
func getCurrentStackPointer() uintptr {
|
||||
return uintptr(stacksave())
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
.section .text.tinygo_scanCurrentStack
|
||||
.global tinygo_scanCurrentStack
|
||||
.type tinygo_scanCurrentStack, %function
|
||||
tinygo_scanCurrentStack:
|
||||
// Push callee-saved registers onto the stack.
|
||||
addiu $sp, $sp, -40
|
||||
sw $ra, 36($sp)
|
||||
sw $s8, 32($sp)
|
||||
sw $s7, 28($sp)
|
||||
sw $s6, 24($sp)
|
||||
sw $s5, 20($sp)
|
||||
sw $s4, 16($sp)
|
||||
sw $s3, 12($sp)
|
||||
sw $s2, 8($sp)
|
||||
sw $s1, 4($sp)
|
||||
sw $s0, ($sp)
|
||||
|
||||
// Scan the stack.
|
||||
jal tinygo_scanstack
|
||||
move $a0, $sp // in the branch delay slot
|
||||
|
||||
// Restore return address.
|
||||
lw $ra, 36($sp)
|
||||
|
||||
// Restore stack state.
|
||||
addiu $sp, $sp, 40
|
||||
|
||||
// Return to the caller.
|
||||
jalr $ra
|
||||
nop
|
||||
|
||||
.section .text.tinygo_longjmp
|
||||
.global tinygo_longjmp
|
||||
tinygo_longjmp:
|
||||
// Note: the code we jump to assumes a0 is non-zero, which is already the
|
||||
// case because that's the defer frame pointer.
|
||||
lw $sp, 0($a0) // jumpSP
|
||||
lw $a1, 4($a0) // jumpPC
|
||||
jr $a1
|
||||
nop
|
||||
@@ -1,6 +1,17 @@
|
||||
// Package debug is a dummy package that is not yet implemented.
|
||||
// Portions copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package debug is a very partially implemented package to allow compilation.
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SetMaxStack sets the maximum amount of memory that can be used by a single
|
||||
// goroutine stack.
|
||||
//
|
||||
@@ -27,16 +38,17 @@ func Stack() []byte {
|
||||
//
|
||||
// Not implemented.
|
||||
func ReadBuildInfo() (info *BuildInfo, ok bool) {
|
||||
return nil, false
|
||||
return &BuildInfo{GoVersion: runtime.Compiler + runtime.Version()}, true
|
||||
}
|
||||
|
||||
// BuildInfo represents the build information read from
|
||||
// the running binary.
|
||||
type BuildInfo struct {
|
||||
Path string // The main package path
|
||||
Main Module // The module containing the main package
|
||||
Deps []*Module // Module dependencies
|
||||
Settings []BuildSetting
|
||||
GoVersion string // version of the Go toolchain that built the binary, e.g. "go1.19.2"
|
||||
Path string // The main package path
|
||||
Main Module // The module containing the main package
|
||||
Deps []*Module // Module dependencies
|
||||
Settings []BuildSetting
|
||||
}
|
||||
|
||||
type BuildSetting struct {
|
||||
@@ -58,3 +70,60 @@ type Module struct {
|
||||
func SetGCPercent(n int) int {
|
||||
return n
|
||||
}
|
||||
|
||||
// Start of stolen from big go. TODO: import/reuse without copy pasta.
|
||||
|
||||
// quoteKey reports whether key is required to be quoted.
|
||||
func quoteKey(key string) bool {
|
||||
return len(key) == 0 || strings.ContainsAny(key, "= \t\r\n\"`")
|
||||
}
|
||||
|
||||
// quoteValue reports whether value is required to be quoted.
|
||||
func quoteValue(value string) bool {
|
||||
return strings.ContainsAny(value, " \t\r\n\"`")
|
||||
}
|
||||
|
||||
func (bi *BuildInfo) String() string {
|
||||
buf := new(strings.Builder)
|
||||
if bi.GoVersion != "" {
|
||||
fmt.Fprintf(buf, "go\t%s\n", bi.GoVersion)
|
||||
}
|
||||
if bi.Path != "" {
|
||||
fmt.Fprintf(buf, "path\t%s\n", bi.Path)
|
||||
}
|
||||
var formatMod func(string, Module)
|
||||
formatMod = func(word string, m Module) {
|
||||
buf.WriteString(word)
|
||||
buf.WriteByte('\t')
|
||||
buf.WriteString(m.Path)
|
||||
buf.WriteByte('\t')
|
||||
buf.WriteString(m.Version)
|
||||
if m.Replace == nil {
|
||||
buf.WriteByte('\t')
|
||||
buf.WriteString(m.Sum)
|
||||
} else {
|
||||
buf.WriteByte('\n')
|
||||
formatMod("=>", *m.Replace)
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
if bi.Main != (Module{}) {
|
||||
formatMod("mod", bi.Main)
|
||||
}
|
||||
for _, dep := range bi.Deps {
|
||||
formatMod("dep", *dep)
|
||||
}
|
||||
for _, s := range bi.Settings {
|
||||
key := s.Key
|
||||
if quoteKey(key) {
|
||||
key = strconv.Quote(key)
|
||||
}
|
||||
value := s.Value
|
||||
if quoteValue(value) {
|
||||
value = strconv.Quote(value)
|
||||
}
|
||||
fmt.Fprintf(buf, "build\t%s=%s\n", key, value)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ func reflectValueEqual(x, y reflect.Value) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Implemented in the reflect package.
|
||||
func typeImplementsMethodSet(actualTypeNum, assertedMethodSet unsafe.Pointer) bool
|
||||
|
||||
// interfaceTypeAssert is called when a type assert without comma-ok still
|
||||
// returns false.
|
||||
func interfaceTypeAssert(ok bool) {
|
||||
|
||||
@@ -14,7 +14,7 @@ const (
|
||||
flag_PROT_READ = 0x1
|
||||
flag_PROT_WRITE = 0x2
|
||||
flag_MAP_PRIVATE = 0x2
|
||||
flag_MAP_ANONYMOUS = 0x20
|
||||
flag_MAP_ANONYMOUS = linux_MAP_ANONYMOUS // different on alpha, hppa, mips, xtensa
|
||||
)
|
||||
|
||||
// Source: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"internal/cm"
|
||||
|
||||
exit "internal/wasi/cli/v0.2.0/exit"
|
||||
stdout "internal/wasi/cli/v0.2.0/stdout"
|
||||
monotonicclock "internal/wasi/clocks/v0.2.0/monotonic-clock"
|
||||
wallclock "internal/wasi/clocks/v0.2.0/wall-clock"
|
||||
random "internal/wasi/random/v0.2.0/random"
|
||||
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
)
|
||||
|
||||
const putcharBufferSize = 120
|
||||
|
||||
@@ -229,6 +229,9 @@ func preinit() {
|
||||
// heap size.
|
||||
// This can happen on 32-bit systems.
|
||||
heapMaxSize /= 2
|
||||
if heapMaxSize < 4096 {
|
||||
runtimePanic("cannot allocate heap memory")
|
||||
}
|
||||
continue
|
||||
}
|
||||
heapStart = uintptr(addr)
|
||||
|
||||
@@ -7,6 +7,8 @@ package syscall
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"internal/cm"
|
||||
|
||||
"internal/wasi/cli/v0.2.0/environment"
|
||||
"internal/wasi/cli/v0.2.0/stderr"
|
||||
"internal/wasi/cli/v0.2.0/stdin"
|
||||
@@ -17,8 +19,6 @@ import (
|
||||
ioerror "internal/wasi/io/v0.2.0/error"
|
||||
"internal/wasi/io/v0.2.0/streams"
|
||||
"internal/wasi/random/v0.2.0/random"
|
||||
|
||||
"github.com/ydnar/wasm-tools-go/cm"
|
||||
)
|
||||
|
||||
func goString(cstr *byte) string {
|
||||
|
||||
@@ -2,7 +2,6 @@ package syscall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -18,11 +17,6 @@ type Rlimit struct {
|
||||
Max uint64
|
||||
}
|
||||
|
||||
// origRlimitNofile, if not {0, 0}, is the original soft RLIMIT_NOFILE.
|
||||
// When we can assume that we are bootstrapping with Go 1.19,
|
||||
// this can be atomic.Pointer[Rlimit].
|
||||
var origRlimitNofile atomic.Value // of Rlimit
|
||||
|
||||
func Setrlimit(resource int, rlim *Rlimit) error {
|
||||
return errors.New("Setrlimit not implemented")
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user