mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 23:28:42 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73dc9963ed | |||
| 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
|
||||
|
||||
@@ -225,6 +225,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
)
|
||||
case *types.Map:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "extraFlags", types.Typ[types.Uint8]),
|
||||
types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||
@@ -273,10 +274,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
metabyte |= 1 << 6
|
||||
}
|
||||
|
||||
if hashmapIsBinaryKey(typ) {
|
||||
metabyte |= 1 << 7
|
||||
}
|
||||
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
@@ -333,7 +330,12 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
c.getTypeCode(types.NewSlice(typ.Elem())), // slicePtr
|
||||
}
|
||||
case *types.Map:
|
||||
var extraFlags uint8
|
||||
if hashmapIsBinaryKey(typ.Key()) {
|
||||
extraFlags |= 1 // extraFlagIsBinaryKey
|
||||
}
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int8Type(), uint64(extraFlags), false),
|
||||
llvm.ConstInt(c.ctx.Int16Type(), 0, false), // numMethods
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Elem()), // elem
|
||||
|
||||
@@ -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 } { i8 85, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
Vendored
+5
-5
@@ -6,14 +6,14 @@ target triple = "wasm32-unknown-wasi"
|
||||
%runtime._interface = type { ptr, ptr }
|
||||
%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:basic:int" = linkonce_odr constant { i8, ptr } { i8 66, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 85, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 85, 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.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:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 85, 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 85, 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.typeid:basic:int" = external constant i8
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -417,6 +417,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
} else {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
}
|
||||
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, ".$typeassert"):
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
|
||||
|
||||
+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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1102,6 +1102,20 @@ func init() {
|
||||
cycleMap3["different"] = cycleMap3
|
||||
}
|
||||
|
||||
type deepEqualInterface interface {
|
||||
Foo()
|
||||
}
|
||||
type deepEqualConcrete struct {
|
||||
int
|
||||
}
|
||||
|
||||
func (deepEqualConcrete) Foo() {}
|
||||
|
||||
var (
|
||||
deepEqualConcrete1 = deepEqualConcrete{1}
|
||||
deepEqualConcrete2 = deepEqualConcrete{2}
|
||||
)
|
||||
|
||||
var deepEqualTests = []DeepEqualTest{
|
||||
// Equalities
|
||||
{nil, nil, true},
|
||||
@@ -1119,6 +1133,7 @@ var deepEqualTests = []DeepEqualTest{
|
||||
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
|
||||
{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
|
||||
{MyBytes{1, 2, 3}, MyBytes{1, 2, 3}, true},
|
||||
{map[deepEqualInterface]string{deepEqualConcrete1: "a"}, map[deepEqualInterface]string{deepEqualConcrete1: "a"}, true},
|
||||
|
||||
// Inequalities
|
||||
{1, 2, false},
|
||||
@@ -1140,6 +1155,7 @@ var deepEqualTests = []DeepEqualTest{
|
||||
{fn3, fn3, false},
|
||||
{[][]int{{1}}, [][]int{{2}}, false},
|
||||
{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false},
|
||||
{map[deepEqualInterface]string{deepEqualConcrete1: "a"}, map[deepEqualInterface]string{deepEqualConcrete2: "a"}, false},
|
||||
|
||||
// Fun with floating point.
|
||||
{math.NaN(), math.NaN(), false},
|
||||
|
||||
+19
-12
@@ -30,8 +30,9 @@
|
||||
// elem *typeStruct // element type of the array
|
||||
// arrayLen uintptr // length of the array (this is part of the type)
|
||||
// slicePtr *typeStruct // pointer to []T type
|
||||
// - map types (this is still missing the key and element types)
|
||||
// - map types
|
||||
// meta uint8
|
||||
// extraMeta uint8
|
||||
// nmethods uint16 (0)
|
||||
// ptrTo *typeStruct
|
||||
// elem *typeStruct
|
||||
@@ -396,10 +397,14 @@ type Type interface {
|
||||
|
||||
// Constants for the 'meta' byte.
|
||||
const (
|
||||
kindMask = 31 // mask to apply to the meta byte to get the Kind value
|
||||
flagNamed = 32 // flag that is set if this is a named type
|
||||
flagComparable = 64 // flag that is set if this type is comparable
|
||||
flagIsBinary = 128 // flag that is set if this type uses the hashmap binary algorithm
|
||||
kindMask = 31 // mask to apply to the meta byte to get the Kind value
|
||||
flagNamed = 32 // flag that is set if this is a named type
|
||||
flagComparable = 64 // flag that is set if this type is comparable
|
||||
)
|
||||
|
||||
// Constants for the 'extraFlags' byte in the map type.
|
||||
const (
|
||||
extraFlagIsBinaryKey = 1 // flag that is set if this type uses the hashmap binary algorithm
|
||||
)
|
||||
|
||||
// The base type struct. All type structs start with this.
|
||||
@@ -433,10 +438,11 @@ type arrayType struct {
|
||||
|
||||
type mapType struct {
|
||||
rawType
|
||||
numMethod uint16
|
||||
ptrTo *rawType
|
||||
elem *rawType
|
||||
key *rawType
|
||||
extraFlags uint8
|
||||
numMethod uint16
|
||||
ptrTo *rawType
|
||||
elem *rawType
|
||||
key *rawType
|
||||
}
|
||||
|
||||
type namedType struct {
|
||||
@@ -980,9 +986,10 @@ func (t *rawType) Comparable() bool {
|
||||
return (t.meta & flagComparable) == flagComparable
|
||||
}
|
||||
|
||||
// isBinary returns if the hashmapAlgorithmBinary functions can be used on this type
|
||||
func (t *rawType) isBinary() bool {
|
||||
return (t.meta & flagIsBinary) == flagIsBinary
|
||||
// isBinaryKey returns if the hashmapAlgorithmBinary functions can be used on
|
||||
// the key type of this map.
|
||||
func (t *mapType) isBinaryKey() bool {
|
||||
return t.extraFlags&extraFlagIsBinaryKey != 0
|
||||
}
|
||||
|
||||
func (t *rawType) ChanDir() ChanDir {
|
||||
|
||||
+15
-8
@@ -947,9 +947,10 @@ func (v Value) MapKeys() []Value {
|
||||
k := New(v.typecode.Key())
|
||||
e := New(v.typecode.Elem())
|
||||
|
||||
typecode := (*mapType)(unsafe.Pointer((v.typecode.underlying())))
|
||||
keyType := v.typecode.key()
|
||||
keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
|
||||
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()
|
||||
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !typecode.isBinaryKey()
|
||||
|
||||
for hashmapNext(v.pointer(), it, k.value, e.value) {
|
||||
if shouldUnpackInterface {
|
||||
@@ -979,10 +980,13 @@ func (v Value) MapIndex(key Value) Value {
|
||||
panic(&ValueError{Method: "MapIndex", Kind: v.Kind()})
|
||||
}
|
||||
|
||||
typecode := (*mapType)(unsafe.Pointer(v.typecode.underlying()))
|
||||
vkey := v.typecode.key()
|
||||
|
||||
// compare key type with actual key type of map
|
||||
if !key.typecode.AssignableTo(vkey) {
|
||||
// AssignableTo is not implemented for interfaces
|
||||
// avoid: "reflect: unimplemented: AssignableTo with interface"
|
||||
if vkey.Kind() != Interface && !key.typecode.AssignableTo(vkey) {
|
||||
// type error?
|
||||
panic("reflect.Value.MapIndex: incompatible types for key")
|
||||
}
|
||||
@@ -995,7 +999,7 @@ func (v Value) MapIndex(key Value) Value {
|
||||
return Value{}
|
||||
}
|
||||
return elem.Elem()
|
||||
} else if vkey.isBinary() {
|
||||
} else if typecode.isBinaryKey() {
|
||||
var keyptr unsafe.Pointer
|
||||
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||
keyptr = key.value
|
||||
@@ -1026,10 +1030,11 @@ func (v Value) MapRange() *MapIter {
|
||||
panic(&ValueError{Method: "MapRange", Kind: v.Kind()})
|
||||
}
|
||||
|
||||
typecode := (*mapType)(unsafe.Pointer(v.typecode.underlying()))
|
||||
keyType := v.typecode.key()
|
||||
|
||||
keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
|
||||
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()
|
||||
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !typecode.isBinaryKey()
|
||||
|
||||
return &MapIter{
|
||||
m: v,
|
||||
@@ -1822,6 +1827,7 @@ func (v Value) SetMapIndex(key, elem Value) {
|
||||
}
|
||||
|
||||
vkey := v.typecode.key()
|
||||
typecode := (*mapType)(unsafe.Pointer(v.typecode.underlying()))
|
||||
|
||||
// compare key type with actual key type of map
|
||||
if !key.typecode.AssignableTo(vkey) {
|
||||
@@ -1857,7 +1863,7 @@ func (v Value) SetMapIndex(key, elem Value) {
|
||||
hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr)
|
||||
}
|
||||
|
||||
} else if key.typecode.isBinary() {
|
||||
} else if typecode.isBinaryKey() {
|
||||
var keyptr unsafe.Pointer
|
||||
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
|
||||
keyptr = key.value
|
||||
@@ -1963,14 +1969,15 @@ func MakeMapWithSize(typ Type, n int) Value {
|
||||
panic("reflect.MakeMapWithSize: negative size hint")
|
||||
}
|
||||
|
||||
key := typ.Key().(*rawType)
|
||||
val := typ.Elem().(*rawType)
|
||||
typecode := (*mapType)(unsafe.Pointer(typ.(*rawType).underlying()))
|
||||
key := typecode.rawType.key()
|
||||
val := typecode.rawType.elem()
|
||||
|
||||
var alg uint8
|
||||
|
||||
if key.Kind() == String {
|
||||
alg = hashmapAlgorithmString
|
||||
} else if key.isBinary() {
|
||||
} else if typecode.isBinaryKey() {
|
||||
alg = hashmapAlgorithmBinary
|
||||
} else {
|
||||
alg = hashmapAlgorithmInterface
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -163,6 +163,46 @@ func Unlink(path string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func Chown(path string, uid, gid int) (err error) {
|
||||
data := cstring(path)
|
||||
fail := int(libc_chown(&data[0], uid, gid))
|
||||
if fail < 0 {
|
||||
err = getErrno()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Fork() (err error) {
|
||||
fail := int(libc_fork())
|
||||
if fail < 0 {
|
||||
err = getErrno()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Execve(pathname string, argv []string, envv []string) (err error) {
|
||||
argv0 := cstring(pathname)
|
||||
|
||||
// transform argv and envv into the format expected by execve
|
||||
argv1 := make([]*byte, len(argv)+1)
|
||||
for i, arg := range argv {
|
||||
argv1[i] = &cstring(arg)[0]
|
||||
}
|
||||
argv1[len(argv)] = nil
|
||||
|
||||
env1 := make([]*byte, len(envv)+1)
|
||||
for i, env := range envv {
|
||||
env1[i] = &cstring(env)[0]
|
||||
}
|
||||
env1[len(envv)] = nil
|
||||
|
||||
fail := int(libc_execve(&argv0[0], &argv1[0], &env1[0]))
|
||||
if fail < 0 {
|
||||
err = getErrno()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error)
|
||||
|
||||
func Kill(pid int, sig Signal) (err error) {
|
||||
@@ -357,6 +397,11 @@ func libc_chdir(pathname *byte) int32
|
||||
//export chmod
|
||||
func libc_chmod(pathname *byte, mode uint32) int32
|
||||
|
||||
// int chown(const char *pathname, uid_t owner, gid_t group);
|
||||
//
|
||||
//export chown
|
||||
func libc_chown(pathname *byte, owner, group int) int32
|
||||
|
||||
// int mkdir(const char *pathname, mode_t mode);
|
||||
//
|
||||
//export mkdir
|
||||
@@ -396,3 +441,13 @@ func libc_readlink(path *byte, buf *byte, count uint) int
|
||||
//
|
||||
//export unlink
|
||||
func libc_unlink(pathname *byte) int32
|
||||
|
||||
// pid_t fork(void);
|
||||
//
|
||||
//export fork
|
||||
func libc_fork() int32
|
||||
|
||||
// int execve(const char *filename, char *const argv[], char *const envp[]);
|
||||
//
|
||||
//export execve
|
||||
func libc_execve(filename *byte, argv **byte, envp **byte) int
|
||||
|
||||
+14
-1
@@ -463,10 +463,23 @@ func (t *T) Run(name string, f func(t *T)) bool {
|
||||
return !sub.failed
|
||||
}
|
||||
|
||||
// Deadline reports the time at which the test binary will have
|
||||
// exceeded the timeout specified by the -timeout flag.
|
||||
//
|
||||
// The ok result is false if the -timeout flag indicates “no timeout” (0).
|
||||
// For now tinygo always return 0, false.
|
||||
//
|
||||
// Not Implemented.
|
||||
func (t *T) Deadline() (deadline time.Time, ok bool) {
|
||||
deadline = t.context.deadline
|
||||
return deadline, !deadline.IsZero()
|
||||
}
|
||||
|
||||
// testContext holds all fields that are common to all tests. This includes
|
||||
// synchronization primitives to run at most *parallel tests.
|
||||
type testContext struct {
|
||||
match *matcher
|
||||
match *matcher
|
||||
deadline time.Time
|
||||
}
|
||||
|
||||
func newTestContext(m *matcher) *testContext {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user