Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a5b16cad6 | ||
|
|
1990b96ccd | ||
|
|
da27c4fbc2 | ||
|
|
f5b5481794 | ||
|
|
79b7420ee1 | ||
|
|
8a6061cc86 | ||
|
|
c10e468d01 | ||
|
|
75e5f99bc5 | ||
|
|
79e32b5a92 | ||
|
|
63b8cc42b9 | ||
|
|
f4a9cd3cbe | ||
|
|
6447a677f3 | ||
|
|
68a430f969 | ||
|
|
dc7c64ba88 | ||
|
|
d6ad13acea | ||
|
|
d124954a7d | ||
|
|
8de3add6fa | ||
|
|
20c8f5ef7c | ||
|
|
ce2bb794fa | ||
|
|
c4a297cbdc | ||
|
|
c473dceda8 | ||
|
|
f202764973 | ||
|
|
9d658604be | ||
|
|
166fff7072 | ||
|
|
8efc4f0735 | ||
|
|
6aa4f45c42 | ||
|
|
f3dbce93a4 | ||
|
|
0e3ea5732a | ||
|
|
1679870ea3 | ||
|
|
9b4ea62f69 | ||
|
|
eee72d1aae | ||
|
|
97cf8c4210 | ||
|
|
7bb8b4631f | ||
|
|
9f43170708 | ||
|
|
71112dbe87 | ||
|
|
4a8093609f | ||
|
|
7865c90737 | ||
|
|
e4e3d11772 | ||
|
|
a5242cbb9e | ||
|
|
c4d1bf5029 | ||
|
|
e003140c6e | ||
|
|
6b8c94e6c4 | ||
|
|
143e4a4559 | ||
|
|
dfeddbe823 | ||
|
|
021824930d | ||
|
|
b8301f10a8 | ||
|
|
2e8808317f |
4
.github/workflows/go-cross.yml
vendored
4
.github/workflows/go-cross.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.18, 1.19 ]
|
||||
go-version: [ '1.21', '1.22' ]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
include:
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
# https://github.com/marketplace/actions/cache
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
# In order:
|
||||
# * Module download cache
|
||||
|
||||
10
.github/workflows/main.yml
vendored
10
.github/workflows/main.yml
vendored
@@ -7,8 +7,8 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.18
|
||||
GOLANGCI_LINT_VERSION: v1.47.1
|
||||
GO_VERSION: '1.22'
|
||||
GOLANGCI_LINT_VERSION: v1.55.2
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
needs: linting
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.18, 1.19 ]
|
||||
go-version: [ '1.21', '1.22' ]
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v2
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/yaegi
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.18, 1.19 ]
|
||||
go-version: [ '1.21', '1.22' ]
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go-version }}
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
|
||||
# https://github.com/marketplace/actions/cache
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./_test/tmp
|
||||
key: ${{ runner.os }}-yaegi-${{ hashFiles('**//_test/tmp/') }}
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
- v[0-9]+.[0-9]+*
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.19
|
||||
GO_VERSION: '1.21'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
[run]
|
||||
deadline = "5m"
|
||||
skip-files = []
|
||||
|
||||
[linters-settings]
|
||||
|
||||
[linters-settings.govet]
|
||||
check-shadowing = false
|
||||
|
||||
[linters-settings.gocyclo]
|
||||
min-complexity = 12.0
|
||||
|
||||
[linters-settings.maligned]
|
||||
suggest-new = true
|
||||
|
||||
[linters-settings.goconst]
|
||||
min-len = 3.0
|
||||
min-occurrences = 3.0
|
||||
|
||||
[linters-settings.misspell]
|
||||
locale = "US"
|
||||
|
||||
[linters]
|
||||
enable-all = true
|
||||
disable = [
|
||||
"golint", # deprecated
|
||||
"scopelint", # deprecated
|
||||
"interfacer", # deprecated
|
||||
"maligned", # deprecated
|
||||
"exhaustivestruct", # deprecated
|
||||
"lll",
|
||||
"gas",
|
||||
"dupl",
|
||||
"prealloc",
|
||||
"gocyclo",
|
||||
"cyclop",
|
||||
"gochecknoinits",
|
||||
"gochecknoglobals",
|
||||
"wsl",
|
||||
"nlreturn",
|
||||
"godox",
|
||||
"funlen",
|
||||
"gocognit",
|
||||
"stylecheck",
|
||||
"gomnd",
|
||||
"testpackage",
|
||||
"paralleltest",
|
||||
"tparallel",
|
||||
"goerr113",
|
||||
"wrapcheck",
|
||||
"nestif",
|
||||
"exhaustive",
|
||||
"exhaustruct",
|
||||
"forbidigo",
|
||||
"ifshort",
|
||||
"forcetypeassert",
|
||||
"varnamelen",
|
||||
"nosnakecase",
|
||||
"nonamedreturns",
|
||||
"nilnil",
|
||||
"maintidx",
|
||||
"errorlint", # TODO: must be reactivate before fixes
|
||||
]
|
||||
|
||||
[issues]
|
||||
exclude-use-default = false
|
||||
max-per-linter = 0
|
||||
max-same-issues = 0
|
||||
exclude = []
|
||||
|
||||
[[issues.exclude-rules]]
|
||||
path = ".+_test\\.go"
|
||||
linters = ["goconst"]
|
||||
[[issues.exclude-rules]]
|
||||
path = ".+_test\\.go"
|
||||
text = "var-declaration:"
|
||||
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp.go"
|
||||
text = "`in` can be `io.Reader`"
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp.go"
|
||||
text = "`out` can be `io.Writer`"
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp.go"
|
||||
text = "`Panic` should conform to the `XxxError` format"
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp_eval_test.go"
|
||||
linters = ["thelper"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/debugger.go"
|
||||
linters = ["containedctx"]
|
||||
165
.golangci.yml
Normal file
165
.golangci.yml
Normal file
@@ -0,0 +1,165 @@
|
||||
run:
|
||||
timeout: 10m
|
||||
skip-files: []
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: false
|
||||
gocyclo:
|
||||
min-complexity: 12
|
||||
maligned:
|
||||
suggest-new: true
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 3
|
||||
funlen:
|
||||
lines: -1
|
||||
statements: 50
|
||||
misspell:
|
||||
locale: US
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
files:
|
||||
- $all
|
||||
allow:
|
||||
- $gostd
|
||||
- github.com/traefik/yaegi
|
||||
tagalign:
|
||||
align: false
|
||||
order:
|
||||
- xml
|
||||
- json
|
||||
- yaml
|
||||
- yml
|
||||
- toml
|
||||
- mapstructure
|
||||
- url
|
||||
godox:
|
||||
keywords:
|
||||
- FIXME
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- style
|
||||
- performance
|
||||
disabled-checks:
|
||||
- paramTypeCombine # already handle by gofumpt.extra-rules
|
||||
- whyNoLint # already handle by nonolint
|
||||
- unnamedResult
|
||||
- hugeParam
|
||||
- sloppyReassign
|
||||
- rangeValCopy
|
||||
- octalLiteral
|
||||
- ptrToRefParam
|
||||
- appendAssign
|
||||
- ruleguard
|
||||
- httpNoBody
|
||||
- exposedSyncMutex
|
||||
- importShadow # TODO should be fixed
|
||||
- commentedOutCode # TODO should be fixed
|
||||
revive:
|
||||
rules:
|
||||
- name: struct-tag
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
disabled: true
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
disabled: true
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: indent-error-flow
|
||||
- name: errorf
|
||||
- name: empty-block
|
||||
- name: superfluous-else
|
||||
- name: unused-parameter
|
||||
disabled: true
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- deadcode # deprecated
|
||||
- exhaustivestruct # deprecated
|
||||
- golint # deprecated
|
||||
- ifshort # deprecated
|
||||
- interfacer # deprecated
|
||||
- maligned # deprecated
|
||||
- nosnakecase # deprecated
|
||||
- scopelint # deprecated
|
||||
- structcheck # deprecated
|
||||
- varcheck # deprecated
|
||||
- cyclop # duplicate of gocyclo
|
||||
- sqlclosecheck # not relevant (SQL)
|
||||
- rowserrcheck # not relevant (SQL)
|
||||
- execinquery # not relevant (SQL)
|
||||
- lll
|
||||
- gas
|
||||
- dupl
|
||||
- prealloc
|
||||
- gocyclo
|
||||
- cyclop
|
||||
- gochecknoinits
|
||||
- gochecknoglobals
|
||||
- wsl
|
||||
- nlreturn
|
||||
- godox
|
||||
- funlen
|
||||
- gocognit
|
||||
- stylecheck
|
||||
- gomnd
|
||||
- testpackage
|
||||
- paralleltest
|
||||
- tparallel
|
||||
- goerr113
|
||||
- wrapcheck
|
||||
- nestif
|
||||
- exhaustive
|
||||
- exhaustruct
|
||||
- forbidigo
|
||||
- ifshort
|
||||
- forcetypeassert
|
||||
- varnamelen
|
||||
- nosnakecase
|
||||
- nonamedreturns
|
||||
- nilnil
|
||||
- maintidx
|
||||
- dupword # false positives
|
||||
- errorlint # TODO: must be reactivate after fixes
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
max-per-linter: 0
|
||||
max-same-issues: 0
|
||||
exclude: []
|
||||
exclude-rules:
|
||||
- path: .+_test\.go
|
||||
linters:
|
||||
- goconst
|
||||
- path: .+_test\.go
|
||||
text: 'var-declaration:'
|
||||
- path: interp/interp.go
|
||||
text: '`in` can be `io.Reader`'
|
||||
- path: interp/interp.go
|
||||
text: '`out` can be `io.Writer`'
|
||||
- path: interp/interp.go
|
||||
text: '`Panic` should conform to the `XxxError` format'
|
||||
- path: interp/interp_eval_test.go
|
||||
linters:
|
||||
- thelper
|
||||
- path: interp/debugger.go
|
||||
linters:
|
||||
- containedctx
|
||||
13
Makefile
13
Makefile
@@ -27,4 +27,15 @@ tests:
|
||||
install.sh: .goreleaser.yml
|
||||
godownloader --repo=traefik/yaegi -o install.sh .goreleaser.yml
|
||||
|
||||
.PHONY: check gen_all_syscall gen_tests generate_downloader internal/cmd/extract/extract install
|
||||
generic_list = cmp/cmp.go slices/slices.go slices/sort.go slices/zsortanyfunc.go maps/maps.go \
|
||||
sync/oncefunc.go sync/atomic/type.go
|
||||
|
||||
# get_generic_src imports stdlib files containing generic symbols definitions
|
||||
get_generic_src:
|
||||
eval "`go env`"; echo $$GOROOT; gov=$${GOVERSION#*.}; gov=$${gov%.*}; \
|
||||
for f in ${generic_list}; do \
|
||||
nf=stdlib/generic/go1_$${gov}_`echo $$f | tr / _`.txt; echo "nf: $$nf"; \
|
||||
cat "$$GOROOT/src/$$f" > "$$nf"; \
|
||||
done
|
||||
|
||||
.PHONY: check gen_all_syscall internal/cmd/extract/extract get_generic_src install
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
[](https://github.com/traefik/yaegi/releases)
|
||||
[](https://github.com/traefik/yaegi/actions/workflows/main.yml)
|
||||
[](https://pkg.go.dev/mod/github.com/traefik/yaegi)
|
||||
[](https://community.traefik.io/c/yaegi)
|
||||
|
||||
Yaegi is Another Elegant Go Interpreter.
|
||||
It powers executable Go scripts and plugins, in embedded interpreters or interactive shells, on top of the Go runtime.
|
||||
@@ -18,7 +17,7 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
|
||||
* Works everywhere Go works
|
||||
* All Go & runtime resources accessible from script (with control)
|
||||
* Security: `unsafe` and `syscall` packages neither used nor exported by default
|
||||
* Support Go 1.18 and Go 1.19 (the latest 2 major releases)
|
||||
* Support the latest 2 major releases of Go (Go 1.21 and Go 1.22)
|
||||
|
||||
## Install
|
||||
|
||||
@@ -31,7 +30,7 @@ import "github.com/traefik/yaegi/interp"
|
||||
### Command-line executable
|
||||
|
||||
```bash
|
||||
go get -u github.com/traefik/yaegi/cmd/yaegi
|
||||
go install github.com/traefik/yaegi/cmd/yaegi@latest
|
||||
```
|
||||
|
||||
Note that you can use [rlwrap](https://github.com/hanslub42/rlwrap) (install with your favorite package manager),
|
||||
@@ -173,10 +172,13 @@ Beside the known [bugs] which are supposed to be fixed in the short term, there
|
||||
|
||||
- Assembly files (`.s`) are not supported.
|
||||
- Calling C code is not supported (no virtual "C" package).
|
||||
- Directives about the compiler, the linker, or embedding files are not supported.
|
||||
- Interfaces to be used from the pre-compiled code can not be added dynamically, as it is required to pre-compile interface wrappers.
|
||||
- Representation of types by `reflect` and printing values using %T may give different results between compiled mode and interpreted mode.
|
||||
- Interpreting computation intensive code is likely to remain significantly slower than in compiled mode.
|
||||
|
||||
Go modules are not supported yet. Until that, it is necessary to install the source into `$GOPATH/src/github.com/traefik/yaegi` to pass all the tests.
|
||||
|
||||
## Contributing
|
||||
|
||||
[Contributing guide](CONTRIBUTING.md).
|
||||
|
||||
@@ -15,11 +15,10 @@ func main() {
|
||||
r := extendedRequest{}
|
||||
req := &r.Request
|
||||
|
||||
|
||||
fmt.Println(r)
|
||||
fmt.Println(req)
|
||||
fmt.Printf("%T\n", r.Request)
|
||||
fmt.Printf("%T\n", req)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {{ <nil> 0 0 map[] <nil> <nil> 0 [] false map[] map[] <nil> map[] <nil> <nil> <nil> <nil>} }
|
||||
// &{ <nil> 0 0 map[] <nil> <nil> 0 [] false map[] map[] <nil> map[] <nil> <nil> <nil> <nil>}
|
||||
// http.Request
|
||||
// *http.Request
|
||||
|
||||
16
_test/assert3.go
Normal file
16
_test/assert3.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "crypto/rsa"
|
||||
|
||||
func main() {
|
||||
var pKey interface{} = &rsa.PublicKey{}
|
||||
|
||||
if _, ok := pKey.(*rsa.PublicKey); ok {
|
||||
println("ok")
|
||||
} else {
|
||||
println("nok")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
11
_test/assert4.go
Normal file
11
_test/assert4.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
var cc interface{} = 2
|
||||
var dd = cc.(int)
|
||||
|
||||
func main() {
|
||||
println(dd)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
56
_test/cli7.go
Normal file
56
_test/cli7.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
type mw1 struct {
|
||||
}
|
||||
|
||||
var obj = map[string]interface{}{}
|
||||
|
||||
func (m *mw1) ServeHTTP(rw http.ResponseWriter, rq *http.Request) {
|
||||
t := &T{
|
||||
ResponseWriter: rw,
|
||||
}
|
||||
x := t.Header()
|
||||
i := obj["m1"].(*mw1)
|
||||
fmt.Fprint(rw, "Welcome to my website!", x, i)
|
||||
}
|
||||
|
||||
func main() {
|
||||
m1 := &mw1{}
|
||||
|
||||
obj["m1"] = m1
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", m1.ServeHTTP)
|
||||
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client(server.URL)
|
||||
}
|
||||
|
||||
func client(uri string) {
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Welcome to my website!map[] &{}
|
||||
41
_test/cli8.go
Normal file
41
_test/cli8.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
next http.Handler
|
||||
}
|
||||
|
||||
func (t *T) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
println("in T.ServeHTTP")
|
||||
if t.next != nil {
|
||||
t.next.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
func New(name string, next http.Handler) (http.Handler, error) { return &T{name, next}, nil }
|
||||
|
||||
func main() {
|
||||
next := func(rw http.ResponseWriter, req *http.Request) {
|
||||
println("in next")
|
||||
}
|
||||
|
||||
t, err := New("test", http.HandlerFunc(next))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
t.ServeHTTP(recorder, req)
|
||||
println(recorder.Result().Status)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in T.ServeHTTP
|
||||
// in next
|
||||
// 200 OK
|
||||
36
_test/closure13.go
Normal file
36
_test/closure13.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type monkey struct {
|
||||
test func() int
|
||||
}
|
||||
|
||||
func main() {
|
||||
input := []string{"1", "2", "3"}
|
||||
|
||||
var monkeys []*monkey
|
||||
|
||||
for _, v := range input {
|
||||
kong := monkey{}
|
||||
divisor, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Print(divisor, " ")
|
||||
kong.test = func() int {
|
||||
return divisor
|
||||
}
|
||||
monkeys = append(monkeys, &kong)
|
||||
}
|
||||
|
||||
for _, mk := range monkeys {
|
||||
fmt.Print(mk.test(), " ")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1 2 3 1 2 3
|
||||
32
_test/closure14.go
Normal file
32
_test/closure14.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type monkey struct {
|
||||
test func() int
|
||||
}
|
||||
|
||||
func getk(k int) (int, error) { return k, nil }
|
||||
|
||||
func main() {
|
||||
input := []string{"1", "2", "3"}
|
||||
|
||||
var monkeys []*monkey
|
||||
|
||||
for k := range input {
|
||||
kong := monkey{}
|
||||
divisor, _ := getk(k)
|
||||
fmt.Print(divisor, " ")
|
||||
kong.test = func() int {
|
||||
return divisor
|
||||
}
|
||||
monkeys = append(monkeys, &kong)
|
||||
}
|
||||
|
||||
for _, mk := range monkeys {
|
||||
fmt.Print(mk.test(), " ")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0 1 2 0 1 2
|
||||
18
_test/convert3.go
Normal file
18
_test/convert3.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
next := func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.Header().Set("Cache-Control", "max-age=20")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
f := http.HandlerFunc(next)
|
||||
fmt.Printf("%T\n", f.ServeHTTP)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// func(http.ResponseWriter, *http.Request)
|
||||
25
_test/fun28.go
Normal file
25
_test/fun28.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func finalize(t *T) { println("finalize") }
|
||||
|
||||
func newT() *T {
|
||||
t := new(T)
|
||||
runtime.SetFinalizer(t, finalize)
|
||||
return t
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := newT()
|
||||
println(t != nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
33
_test/gen11.go
Normal file
33
_test/gen11.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
type Slice[T any] struct {
|
||||
x []T
|
||||
}
|
||||
|
||||
type IPPrefixSlice struct {
|
||||
x Slice[netip.Prefix]
|
||||
}
|
||||
|
||||
func (v Slice[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.x) }
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (v IPPrefixSlice) MarshalJSON() ([]byte, error) {
|
||||
return v.x.MarshalJSON()
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := IPPrefixSlice{}
|
||||
fmt.Println(t)
|
||||
b, e := t.MarshalJSON()
|
||||
fmt.Println(string(b), e)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {{[]}}
|
||||
// null <nil>
|
||||
31
_test/gen12.go
Normal file
31
_test/gen12.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func MapOf[K comparable, V any](m map[K]V) Map[K, V] {
|
||||
return Map[K, V]{m}
|
||||
}
|
||||
|
||||
type Map[K comparable, V any] struct {
|
||||
ж map[K]V
|
||||
}
|
||||
|
||||
func (v MapView) Int() Map[string, int] { return MapOf(v.ж.Int) }
|
||||
|
||||
type VMap struct {
|
||||
Int map[string]int
|
||||
}
|
||||
|
||||
type MapView struct {
|
||||
ж *VMap
|
||||
}
|
||||
|
||||
func main() {
|
||||
mv := MapView{&VMap{}}
|
||||
fmt.Println(mv.ж)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{map[]}
|
||||
18
_test/gen13.go
Normal file
18
_test/gen13.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
type Map[K comparable, V any] struct {
|
||||
ж map[K]V
|
||||
}
|
||||
|
||||
func (m Map[K, V]) Has(k K) bool {
|
||||
_, ok := m.ж[k]
|
||||
return ok
|
||||
}
|
||||
|
||||
func main() {
|
||||
m := Map[string, float64]{}
|
||||
println(m.Has("test"))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
17
_test/issue-1416.go
Normal file
17
_test/issue-1416.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type Number int32
|
||||
|
||||
type Number1 = Number
|
||||
|
||||
type Number2 = Number1
|
||||
|
||||
func (n Number2) IsValid() bool { return true }
|
||||
|
||||
func main() {
|
||||
a := Number(5)
|
||||
println(a.IsValid())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
41
_test/issue-1442.go
Normal file
41
_test/issue-1442.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, _ := context.WithCancel(context.Background())
|
||||
ch := make(chan string, 20)
|
||||
defer close(ch)
|
||||
|
||||
go func(ctx context.Context, ch <-chan string) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case tmp := <-ch:
|
||||
_ = tmp
|
||||
}
|
||||
}
|
||||
}(ctx, ch)
|
||||
|
||||
for _, i := range "abcdef" {
|
||||
for _, j := range "0123456789" {
|
||||
// i, j := "a", "0"
|
||||
for _, k := range "ABCDEF" {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
tmp := string(i) + string(j) + string(k)
|
||||
ch <- tmp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Output:
|
||||
//
|
||||
19
_test/issue-1451.go
Normal file
19
_test/issue-1451.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type t1 uint8
|
||||
|
||||
const (
|
||||
n1 t1 = iota
|
||||
n2
|
||||
)
|
||||
|
||||
type T struct {
|
||||
elem [n2 + 1]int
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(len(T{}.elem))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
22
_test/issue-1454.go
Normal file
22
_test/issue-1454.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type I2 interface {
|
||||
I2() string
|
||||
}
|
||||
|
||||
type I interface {
|
||||
I2
|
||||
}
|
||||
|
||||
type S struct{}
|
||||
|
||||
func (*S) I2() string { return "foo" }
|
||||
|
||||
func main() {
|
||||
var i I
|
||||
_, ok := i.(*S)
|
||||
println(ok)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
22
_test/issue-1459.go
Normal file
22
_test/issue-1459.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type funclistItem func()
|
||||
|
||||
type funclist struct {
|
||||
list []funclistItem
|
||||
}
|
||||
|
||||
func main() {
|
||||
funcs := funclist{}
|
||||
|
||||
funcs.list = append(funcs.list, func() { fmt.Println("first") })
|
||||
|
||||
for _, f := range funcs.list {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// first
|
||||
84
_test/issue-1460.go
Normal file
84
_test/issue-1460.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func unmarshalJSON[T any](b []byte, x *[]T) error {
|
||||
if *x != nil {
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(b, x)
|
||||
}
|
||||
|
||||
func SliceOfViews[T ViewCloner[T, V], V StructView[T]](x []T) SliceView[T, V] {
|
||||
return SliceView[T, V]{x}
|
||||
}
|
||||
|
||||
type StructView[T any] interface {
|
||||
Valid() bool
|
||||
AsStruct() T
|
||||
}
|
||||
|
||||
type SliceView[T ViewCloner[T, V], V StructView[T]] struct {
|
||||
ж []T
|
||||
}
|
||||
|
||||
type ViewCloner[T any, V StructView[T]] interface {
|
||||
View() V
|
||||
Clone() T
|
||||
}
|
||||
|
||||
func (v SliceView[T, V]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
|
||||
|
||||
func (v *SliceView[T, V]) UnmarshalJSON(b []byte) error { return unmarshalJSON(b, &v.ж) }
|
||||
|
||||
type Slice[T any] struct {
|
||||
ж []T
|
||||
}
|
||||
|
||||
func (v Slice[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
|
||||
|
||||
func (v *Slice[T]) UnmarshalJSON(b []byte) error { return unmarshalJSON(b, &v.ж) }
|
||||
|
||||
func SliceOf[T any](x []T) Slice[T] {
|
||||
return Slice[T]{x}
|
||||
}
|
||||
|
||||
type IPPrefixSlice struct {
|
||||
ж Slice[netip.Prefix]
|
||||
}
|
||||
|
||||
type viewStruct struct {
|
||||
Int int
|
||||
Strings Slice[string]
|
||||
StringsPtr *Slice[string] `json:",omitempty"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
ss := SliceOf([]string{"bar"})
|
||||
in := viewStruct{
|
||||
Int: 1234,
|
||||
Strings: ss,
|
||||
StringsPtr: &ss,
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetIndent("", "")
|
||||
err1 := encoder.Encode(&in)
|
||||
b := buf.Bytes()
|
||||
var got viewStruct
|
||||
err2 := json.Unmarshal(b, &got)
|
||||
println(err1 == nil, err2 == nil, reflect.DeepEqual(got, in))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true true true
|
||||
24
_test/issue-1465.go
Normal file
24
_test/issue-1465.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func SomeFunc[T int | string](defaultValue T) T {
|
||||
switch v := any(&defaultValue).(type) {
|
||||
case *string:
|
||||
*v = *v + " abc"
|
||||
case *int:
|
||||
*v -= 234
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(SomeFunc("test"))
|
||||
fmt.Println(SomeFunc(1234))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test abc
|
||||
// 1000
|
||||
24
_test/issue-1466.go
Normal file
24
_test/issue-1466.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func SomeFunc(defaultValue interface{}) interface{} {
|
||||
switch v := defaultValue.(type) {
|
||||
case string:
|
||||
return v + " abc"
|
||||
case int:
|
||||
return v - 234
|
||||
}
|
||||
panic("whoops")
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(SomeFunc(1234))
|
||||
fmt.Println(SomeFunc("test"))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1000
|
||||
// test abc
|
||||
15
_test/issue-1470.go
Normal file
15
_test/issue-1470.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
num [tnum + 2]int
|
||||
}
|
||||
|
||||
const tnum = 23
|
||||
|
||||
func main() {
|
||||
t := T{}
|
||||
println(len(t.num))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 25
|
||||
12
_test/issue-1475.go
Normal file
12
_test/issue-1475.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
type T uint16
|
||||
|
||||
func f() T { return 0 }
|
||||
|
||||
func main() {
|
||||
println(f())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
23
_test/issue-1488.go
Normal file
23
_test/issue-1488.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type vector interface {
|
||||
[]int | [3]int
|
||||
}
|
||||
|
||||
func sum[V vector](v V) (out int) {
|
||||
for i := 0; i < len(v); i++ {
|
||||
out += v[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
va := [3]int{1, 2, 3}
|
||||
vs := []int{1, 2, 3}
|
||||
fmt.Println(sum[[3]int](va), sum[[]int](vs))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 6 6
|
||||
16
_test/issue-1496.go
Normal file
16
_test/issue-1496.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := []byte{} == nil
|
||||
b := nil == []byte{}
|
||||
c := nil == &struct{}{}
|
||||
i := 100
|
||||
d := nil == &i
|
||||
var v interface{}
|
||||
f := nil == v
|
||||
g := v == nil
|
||||
println(a, b, c, d, f, g)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false false false false true true
|
||||
43
_test/issue-1515.go
Normal file
43
_test/issue-1515.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
type I1 interface {
|
||||
I2
|
||||
Wrap() *S3
|
||||
}
|
||||
|
||||
type I2 interface {
|
||||
F()
|
||||
}
|
||||
|
||||
type S2 struct {
|
||||
I2
|
||||
}
|
||||
|
||||
func newS2(i2 I2) I1 {
|
||||
return &S2{i2}
|
||||
}
|
||||
|
||||
type S3 struct {
|
||||
base *S2
|
||||
}
|
||||
|
||||
func (s *S2) Wrap() *S3 {
|
||||
i2 := s
|
||||
return &S3{i2}
|
||||
}
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (t *T) F() { println("in F", t.name) }
|
||||
|
||||
func main() {
|
||||
t := &T{"test"}
|
||||
s2 := newS2(t)
|
||||
s3 := s2.Wrap()
|
||||
s3.base.F()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in F test
|
||||
15
_test/issue-1536.go
Normal file
15
_test/issue-1536.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
var a [len(prefix+path) + 2]int
|
||||
|
||||
const (
|
||||
prefix = "/usr/"
|
||||
path = prefix + "local/bin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(len(a))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 21
|
||||
20
_test/issue-1571.go
Normal file
20
_test/issue-1571.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
type A struct {
|
||||
*B[string]
|
||||
}
|
||||
|
||||
type B[T any] struct {
|
||||
data T
|
||||
}
|
||||
|
||||
func main() {
|
||||
_ = &A{
|
||||
B: &B[string]{},
|
||||
}
|
||||
|
||||
println("PASS")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// PASS
|
||||
17
_test/issue-1594.go
Normal file
17
_test/issue-1594.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var fns []func()
|
||||
for _, v := range []int{1, 2, 3} {
|
||||
x := v*100 + v
|
||||
fns = append(fns, func() { println(x) })
|
||||
}
|
||||
for _, fn := range fns {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 101
|
||||
// 202
|
||||
// 303
|
||||
32
_test/method40.go
Normal file
32
_test/method40.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type TMemoryBuffer struct {
|
||||
*bytes.Buffer
|
||||
size int
|
||||
}
|
||||
|
||||
func newTMemoryBuffer() *TMemoryBuffer {
|
||||
return &TMemoryBuffer{}
|
||||
}
|
||||
|
||||
var globalMemoryBuffer = newTMemoryBuffer()
|
||||
|
||||
type TTransport interface {
|
||||
io.ReadWriter
|
||||
}
|
||||
|
||||
func check(t TTransport) {
|
||||
println("ok")
|
||||
}
|
||||
|
||||
func main() {
|
||||
check(globalMemoryBuffer)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
@@ -1,6 +1,6 @@
|
||||
package main
|
||||
|
||||
import "github.com/traefik/yaegi/_test/alias3"
|
||||
import "github.com/traefik/yaegi/_test/named3"
|
||||
|
||||
var globalT *T
|
||||
|
||||
@@ -8,10 +8,10 @@ func init() {
|
||||
globalT = &T{A: "test"}
|
||||
}
|
||||
|
||||
type T alias3.T
|
||||
type T named3.T
|
||||
|
||||
func (t *T) PrintT() {
|
||||
(*alias3.T)(t).Print()
|
||||
(*named3.T)(t).Print()
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -1,4 +1,4 @@
|
||||
package alias3
|
||||
package named3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
14
_test/p6.go
Normal file
14
_test/p6.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/traefik/yaegi/_test/p6"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := p6.IPPrefixSlice{}
|
||||
fmt.Println(t)
|
||||
b, e := t.MarshalJSON()
|
||||
fmt.Println(string(b), e)
|
||||
}
|
||||
21
_test/p6/p6.go
Normal file
21
_test/p6/p6.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package p6
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
type Slice[T any] struct {
|
||||
x []T
|
||||
}
|
||||
|
||||
type IPPrefixSlice struct {
|
||||
x Slice[netip.Prefix]
|
||||
}
|
||||
|
||||
func (v Slice[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.x) }
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (v IPPrefixSlice) MarshalJSON() ([]byte, error) {
|
||||
return v.x.MarshalJSON()
|
||||
}
|
||||
20
_test/panic0.go
Normal file
20
_test/panic0.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
foo()
|
||||
}
|
||||
|
||||
func foo() {
|
||||
bar()
|
||||
}
|
||||
|
||||
func bar() {
|
||||
baz()
|
||||
}
|
||||
|
||||
func baz() {
|
||||
panic("stop!")
|
||||
}
|
||||
|
||||
// Error:
|
||||
// stop!
|
||||
16
_test/recurse1.go
Normal file
16
_test/recurse1.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type F func(a *A)
|
||||
|
||||
type A struct {
|
||||
Name string
|
||||
F
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := &A{"Test", func(a *A) { println("in f", a.Name) }}
|
||||
a.F(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in f Test
|
||||
27
_test/recurse2.go
Normal file
27
_test/recurse2.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
type F func(a *A)
|
||||
|
||||
type A struct {
|
||||
B string
|
||||
D
|
||||
f F
|
||||
}
|
||||
|
||||
type D struct {
|
||||
*A
|
||||
E *A
|
||||
}
|
||||
|
||||
func f1(a *A) { println("in f1", a.B) }
|
||||
|
||||
func main() {
|
||||
a := &A{B: "b", f: f1}
|
||||
a.D = D{E: a}
|
||||
println(a.D.E.B)
|
||||
a.f(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// b
|
||||
// in f1 b
|
||||
25
_test/recurse3.go
Normal file
25
_test/recurse3.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
type F func(a *A)
|
||||
|
||||
type A struct {
|
||||
B string
|
||||
D
|
||||
}
|
||||
|
||||
type D struct {
|
||||
*A
|
||||
E *A
|
||||
f F
|
||||
}
|
||||
|
||||
func f1(a *A) { println("in f1", a.B) }
|
||||
|
||||
func main() {
|
||||
a := &A{B: "b"}
|
||||
a.D = D{f: f1}
|
||||
a.f(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in f1 b
|
||||
@@ -10,11 +10,11 @@ func main() {
|
||||
c2 := make(chan string)
|
||||
|
||||
go func() {
|
||||
time.Sleep(1e7)
|
||||
time.Sleep(1e8)
|
||||
c1 <- "one"
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(2e7)
|
||||
time.Sleep(2e8)
|
||||
c2 <- "two"
|
||||
}()
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
period = 100 * time.Millisecond
|
||||
precision = 7 * time.Millisecond
|
||||
period = 300 * time.Millisecond
|
||||
precision = 30 * time.Millisecond
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -25,7 +25,7 @@ func main() {
|
||||
}
|
||||
s.ts["test"] = append(s.ts["test"], &T{s: s})
|
||||
|
||||
t , ok:= s.getT("test")
|
||||
t, ok := s.getT("test")
|
||||
println(t != nil, ok)
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,6 @@ func assertValue() {
|
||||
}
|
||||
|
||||
// Output:
|
||||
// interface conversion: interface {} is int, not string
|
||||
// interface conversion: interface {} is nil, not string
|
||||
// interface conversion: *httptest.ResponseRecorder is not http.Pusher: missing method Push
|
||||
// 22:10: interface conversion: interface {} is int, not string
|
||||
// 32:10: interface conversion: interface {} is nil, not string
|
||||
// 42:10: interface conversion: *httptest.ResponseRecorder is not http.Pusher: missing method Push
|
||||
|
||||
17
_test/unsafe10.go
Normal file
17
_test/unsafe10.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type T struct {
|
||||
X uint64
|
||||
Y uint64
|
||||
}
|
||||
|
||||
func f(off uintptr) { println(off) }
|
||||
|
||||
func main() {
|
||||
f(unsafe.Offsetof(T{}.Y))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 8
|
||||
@@ -21,3 +21,6 @@ func main() {
|
||||
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
|
||||
18
_test/unsafe8.go
Normal file
18
_test/unsafe8.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type T struct {
|
||||
i uint64
|
||||
}
|
||||
|
||||
var d T
|
||||
|
||||
var b [unsafe.Sizeof(d)]byte
|
||||
|
||||
func main() {
|
||||
println(len(b))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 8
|
||||
@@ -69,7 +69,7 @@ func extractCmd(arg []string) error {
|
||||
ext.Include = strings.Split(include, ",")
|
||||
}
|
||||
|
||||
r := strings.NewReplacer("/", "-", ".", "_")
|
||||
r := strings.NewReplacer("/", "-", ".", "_", "~", "_")
|
||||
|
||||
for _, pkgIdent := range args {
|
||||
var buf bytes.Buffer
|
||||
|
||||
@@ -37,17 +37,7 @@ func applyCIMultiplier(timeout time.Duration) time.Duration {
|
||||
}
|
||||
|
||||
func TestYaegiCmdCancel(t *testing.T) {
|
||||
tmp, err := os.MkdirTemp("", "yaegi-")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tmp directory: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
err = os.RemoveAll(tmp)
|
||||
if err != nil {
|
||||
t.Errorf("failed to clean up %v: %v", tmp, err)
|
||||
}
|
||||
}()
|
||||
|
||||
tmp := t.TempDir()
|
||||
yaegi := filepath.Join(tmp, "yaegi")
|
||||
|
||||
args := []string{"build"}
|
||||
@@ -125,6 +115,12 @@ func TestYaegiCmdCancel(t *testing.T) {
|
||||
}
|
||||
|
||||
func raceDetectorSupported(goos, goarch string) bool {
|
||||
if strings.Contains(os.Getenv("GOFLAGS"), "-buildmode=pie") {
|
||||
// The Go race detector is not compatible with position independent code (pie).
|
||||
// We read the conventional GOFLAGS env variable used for example on AlpineLinux
|
||||
// to build packages, as there is no way to get this information from the runtime.
|
||||
return false
|
||||
}
|
||||
switch goos {
|
||||
case "linux":
|
||||
return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64"
|
||||
|
||||
@@ -128,7 +128,7 @@ func TestPackages(t *testing.T) {
|
||||
if test.topImport != "" {
|
||||
topImport = test.topImport
|
||||
}
|
||||
if _, err = i.Eval(fmt.Sprintf(`import "%s"`, topImport)); err != nil {
|
||||
if _, err = i.Eval(fmt.Sprintf(`import %q`, topImport)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
value, err := i.Eval(`pkg.NewSample()`)
|
||||
|
||||
@@ -39,7 +39,9 @@ import (
|
||||
"{{$key}}"
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
{{- if or .Val .Typ }}
|
||||
"{{.ImportPath}}"
|
||||
{{- end}}
|
||||
"reflect"
|
||||
)
|
||||
|
||||
@@ -130,9 +132,18 @@ func matchList(name string, list []string) (match bool, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Extractor creates a package with all the symbols from a dependency package.
|
||||
type Extractor struct {
|
||||
Dest string // The name of the created package.
|
||||
License string // License text to be included in the created package, optional.
|
||||
Exclude []string // Comma separated list of regexp matching symbols to exclude.
|
||||
Include []string // Comma separated list of regexp matching symbols to include.
|
||||
Tag []string // Comma separated of build tags to be added to the created package.
|
||||
}
|
||||
|
||||
func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, error) {
|
||||
prefix := "_" + importPath + "_"
|
||||
prefix = strings.NewReplacer("/", "_", "-", "_", ".", "_").Replace(prefix)
|
||||
prefix = strings.NewReplacer("/", "_", "-", "_", ".", "_", "~", "_").Replace(prefix)
|
||||
|
||||
typ := map[string]string{}
|
||||
val := map[string]Val{}
|
||||
@@ -190,6 +201,10 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
|
||||
val[name] = Val{pname, false}
|
||||
}
|
||||
case *types.Func:
|
||||
// Skip generic functions and methods.
|
||||
if s := o.Type().(*types.Signature); s.TypeParams().Len() > 0 || s.RecvTypeParams().Len() > 0 {
|
||||
continue
|
||||
}
|
||||
val[name] = Val{pname, false}
|
||||
case *types.Var:
|
||||
val[name] = Val{pname, true}
|
||||
@@ -198,9 +213,13 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
|
||||
if t, ok := o.Type().(*types.Named); ok && t.TypeParams().Len() > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
typ[name] = pname
|
||||
if t, ok := o.Type().Underlying().(*types.Interface); ok {
|
||||
if t.NumMethods() == 0 && t.NumEmbeddeds() != 0 {
|
||||
// Skip interfaces used to implement constraints for generics.
|
||||
delete(typ, name)
|
||||
continue
|
||||
}
|
||||
var methods []Method
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
f := t.Method(i)
|
||||
@@ -283,11 +302,11 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
|
||||
}
|
||||
|
||||
for _, t := range e.Tag {
|
||||
if len(t) != 0 {
|
||||
if t != "" {
|
||||
buildTags += "," + t
|
||||
}
|
||||
}
|
||||
if len(buildTags) != 0 && buildTags[0] == ',' {
|
||||
if buildTags != "" && buildTags[0] == ',' {
|
||||
buildTags = buildTags[1:]
|
||||
}
|
||||
|
||||
@@ -340,7 +359,7 @@ func fixConst(name string, val constant.Value, imports map[string]bool) string {
|
||||
str = f.Text('g', int(f.Prec()))
|
||||
case constant.Complex:
|
||||
// TODO: not sure how to parse this case
|
||||
fallthrough
|
||||
fallthrough //nolint:gocritic // Empty Fallthrough is expected.
|
||||
default:
|
||||
return name
|
||||
}
|
||||
@@ -351,15 +370,6 @@ func fixConst(name string, val constant.Value, imports map[string]bool) string {
|
||||
return fmt.Sprintf("constant.MakeFromLiteral(%q, token.%s, 0)", str, tok)
|
||||
}
|
||||
|
||||
// Extractor creates a package with all the symbols from a dependency package.
|
||||
type Extractor struct {
|
||||
Dest string // The name of the created package.
|
||||
License string // License text to be included in the created package, optional.
|
||||
Exclude []string // Comma separated list of regexp matching symbols to exclude.
|
||||
Include []string // Comma separated list of regexp matching symbols to include.
|
||||
Tag []string // Comma separated of build tags to be added to the created package.
|
||||
}
|
||||
|
||||
// importPath checks whether pkgIdent is an existing directory relative to
|
||||
// e.WorkingDir. If yes, it returns the actual import path of the Go package
|
||||
// located in the directory. If it is definitely a relative path, but it does not
|
||||
@@ -468,7 +478,7 @@ func GetMinor(part string) string {
|
||||
return minor
|
||||
}
|
||||
|
||||
const defaultMinorVersion = 19
|
||||
const defaultMinorVersion = 22
|
||||
|
||||
func genBuildTags() (string, error) {
|
||||
version := runtime.Version()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package yaegi provides a Go interpreter.
|
||||
package yaegi
|
||||
|
||||
//go:generate go generate github.com/traefik/yaegi/internal/cmd/extract
|
||||
|
||||
@@ -539,7 +539,7 @@ func {{$name}}(n *node) {
|
||||
|
||||
{{- if or (eq $op.Name "==") (eq $op.Name "!=") }}
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
if c0.typ.cat == linkedT || c1.typ.cat == linkedT {
|
||||
switch {
|
||||
case isInterface:
|
||||
v0 := genValue(c0)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
//go:build !go1.21
|
||||
// +build !go1.21
|
||||
|
||||
// Package unsafe2 provides helpers to generate recursive struct types.
|
||||
package unsafe2
|
||||
|
||||
import (
|
||||
72
internal/unsafe2/go1_21_unsafe.go
Normal file
72
internal/unsafe2/go1_21_unsafe.go
Normal file
@@ -0,0 +1,72 @@
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
// Package unsafe2 provides helpers to generate recursive struct types.
|
||||
package unsafe2
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type dummy struct{}
|
||||
|
||||
// DummyType represents a stand-in for a recursive type.
|
||||
var DummyType = reflect.TypeOf(dummy{})
|
||||
|
||||
// The following type sizes must match their original definition in Go src/internal/abi/type.go.
|
||||
type abiType struct {
|
||||
_ uintptr
|
||||
_ uintptr
|
||||
_ uint32
|
||||
_ uint8
|
||||
_ uint8
|
||||
_ uint8
|
||||
_ uint8
|
||||
_ uintptr
|
||||
_ uintptr
|
||||
_ int32
|
||||
_ int32
|
||||
}
|
||||
|
||||
type abiName struct {
|
||||
Bytes *byte
|
||||
}
|
||||
|
||||
type abiStructField struct {
|
||||
Name abiName
|
||||
Typ *abiType
|
||||
Offset uintptr
|
||||
}
|
||||
|
||||
type abiStructType struct {
|
||||
abiType
|
||||
PkgPath abiName
|
||||
Fields []abiStructField
|
||||
}
|
||||
|
||||
type emptyInterface struct {
|
||||
typ *abiType
|
||||
_ unsafe.Pointer
|
||||
}
|
||||
|
||||
// SetFieldType sets the type of the struct field at the given index, to the given type.
|
||||
//
|
||||
// The struct type must have been created at runtime. This is very unsafe.
|
||||
func SetFieldType(s reflect.Type, idx int, t reflect.Type) {
|
||||
if s.Kind() != reflect.Struct || idx >= s.NumField() {
|
||||
return
|
||||
}
|
||||
|
||||
rtyp := unpackType(s)
|
||||
styp := (*abiStructType)(unsafe.Pointer(rtyp))
|
||||
f := styp.Fields[idx]
|
||||
f.Typ = unpackType(t)
|
||||
styp.Fields[idx] = f
|
||||
}
|
||||
|
||||
func unpackType(t reflect.Type) *abiType {
|
||||
v := reflect.New(t).Elem().Interface()
|
||||
eface := *(*emptyInterface)(unsafe.Pointer(&v))
|
||||
return eface.typ
|
||||
}
|
||||
@@ -147,10 +147,22 @@ func skipFile(ctx *build.Context, p string, skipTest bool) bool {
|
||||
}
|
||||
a := strings.Split(p[i+1:], "_")
|
||||
last := len(a) - 1
|
||||
if last1 := last - 1; last1 >= 0 && a[last1] == ctx.GOOS && a[last] == ctx.GOARCH {
|
||||
return false
|
||||
if last-1 >= 0 {
|
||||
switch x, y := a[last-1], a[last]; {
|
||||
case x == ctx.GOOS:
|
||||
if knownArch[y] {
|
||||
return y != ctx.GOARCH
|
||||
}
|
||||
return false
|
||||
case knownOs[x] && knownArch[y]:
|
||||
return true
|
||||
case knownArch[y] && y != ctx.GOARCH:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
if s := a[last]; s != ctx.GOOS && s != ctx.GOARCH && knownOs[s] || knownArch[s] {
|
||||
if x := a[last]; knownOs[x] && x != ctx.GOOS || knownArch[x] && x != ctx.GOARCH {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -170,6 +182,7 @@ var knownOs = map[string]bool{
|
||||
"openbsd": true,
|
||||
"plan9": true,
|
||||
"solaris": true,
|
||||
"wasip1": true,
|
||||
"windows": true,
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestBuildTag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFile(t *testing.T) {
|
||||
func TestSkipFile(t *testing.T) {
|
||||
// Assume a specific OS, arch and go pattern no matter the real underlying system
|
||||
ctx := build.Context{
|
||||
GOARCH: "amd64",
|
||||
@@ -65,10 +65,18 @@ func TestBuildFile(t *testing.T) {
|
||||
{"bar_linux.go", false},
|
||||
{"bar_maix.go", false},
|
||||
{"bar_mlinux.go", false},
|
||||
|
||||
{"bar_aix_foo.go", false},
|
||||
{"bar_linux_foo.go", false},
|
||||
{"bar_foo_amd64.go", false},
|
||||
{"bar_foo_arm.go", true},
|
||||
|
||||
{"bar_aix_s390x.go", true},
|
||||
{"bar_aix_amd64.go", true},
|
||||
{"bar_linux_arm.go", true},
|
||||
|
||||
{"bar_amd64.go", false},
|
||||
{"bar_arm.go", true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
466
interp/cfg.go
466
interp/cfg.go
@@ -45,6 +45,13 @@ var constBltn = map[string]func(*node){
|
||||
|
||||
const nilIdent = "nil"
|
||||
|
||||
func init() {
|
||||
// Use init() to avoid initialization cycles for the following constant builtins.
|
||||
constBltn[bltnAlignof] = alignof
|
||||
constBltn[bltnOffsetof] = offsetof
|
||||
constBltn[bltnSizeof] = sizeof
|
||||
}
|
||||
|
||||
// cfg generates a control flow graph (CFG) from AST (wiring successors in AST)
|
||||
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
||||
// variables. A list of nodes of init functions is returned.
|
||||
@@ -145,9 +152,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
|
||||
switch o.typ.cat {
|
||||
case valueT, aliasT:
|
||||
case valueT, linkedT:
|
||||
typ := o.typ.rtype
|
||||
if o.typ.cat == aliasT {
|
||||
if o.typ.cat == linkedT {
|
||||
typ = o.typ.val.TypeOf()
|
||||
}
|
||||
switch typ.Kind() {
|
||||
@@ -322,8 +329,60 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
}
|
||||
if n.typ == nil {
|
||||
err = n.cfgErrorf("undefined type")
|
||||
return false
|
||||
// A nil type indicates either an error or a generic type.
|
||||
// A child indexExpr or indexListExpr is used for type parameters,
|
||||
// it indicates an instanciated generic.
|
||||
if n.child[0].kind != indexExpr && n.child[0].kind != indexListExpr {
|
||||
err = n.cfgErrorf("undefined type")
|
||||
return false
|
||||
}
|
||||
t0, err1 := nodeType(interp, sc, n.child[0].child[0])
|
||||
if err1 != nil {
|
||||
return false
|
||||
}
|
||||
if t0.cat != genericT {
|
||||
err = n.cfgErrorf("undefined type")
|
||||
return false
|
||||
}
|
||||
// We have a composite literal of generic type, instantiate it.
|
||||
lt := []*itype{}
|
||||
for _, n1 := range n.child[0].child[1:] {
|
||||
t1, err1 := nodeType(interp, sc, n1)
|
||||
if err1 != nil {
|
||||
return false
|
||||
}
|
||||
lt = append(lt, t1)
|
||||
}
|
||||
var g *node
|
||||
g, _, err = genAST(sc, t0.node.anc, lt)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
n.child[0] = g.lastChild()
|
||||
n.typ, err = nodeType(interp, sc, n.child[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// Generate methods if any.
|
||||
for _, nod := range t0.method {
|
||||
gm, _, err2 := genAST(nod.scope, nod, lt)
|
||||
if err2 != nil {
|
||||
err = err2
|
||||
return false
|
||||
}
|
||||
gm.typ, err = nodeType(interp, nod.scope, gm.child[2])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if _, err = interp.cfg(gm, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
return false
|
||||
}
|
||||
if err = genRun(gm); err != nil {
|
||||
return false
|
||||
}
|
||||
n.typ.addMethod(gm)
|
||||
}
|
||||
n.nleft = 1 // Indictate the type of composite literal.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +429,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
case funcDecl:
|
||||
// Do not allow function declarations without body.
|
||||
if len(n.child) < 4 {
|
||||
err = n.cfgErrorf("function declaration without body is unsupported (linkname or assembly can not be interpreted).")
|
||||
err = n.cfgErrorf("missing function body")
|
||||
return false
|
||||
}
|
||||
n.val = n
|
||||
@@ -439,6 +498,19 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
if typ, err = nodeType(interp, sc, recvTypeNode); err != nil {
|
||||
return false
|
||||
}
|
||||
if typ.cat == nilT {
|
||||
// This may happen when instantiating generic methods.
|
||||
s2, _, ok := sc.lookup(typ.id())
|
||||
if !ok {
|
||||
err = n.cfgErrorf("type not found: %s", typ.id())
|
||||
break
|
||||
}
|
||||
typ = s2.typ
|
||||
if typ.cat == nilT {
|
||||
err = n.cfgErrorf("nil type: %s", typ.id())
|
||||
break
|
||||
}
|
||||
}
|
||||
recvTypeNode.typ = typ
|
||||
n.child[2].typ.recv = typ
|
||||
n.typ.recv = typ
|
||||
@@ -579,6 +651,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
var sym *symbol
|
||||
var level int
|
||||
|
||||
if dest.rval.IsValid() && isConstType(dest.typ) {
|
||||
err = n.cfgErrorf("cannot assign to %s (%s constant)", dest.rval, dest.typ.str)
|
||||
break
|
||||
}
|
||||
if isBlank(src) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
@@ -603,7 +679,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
return
|
||||
}
|
||||
if sc.global {
|
||||
// Do not overload existing symbols (defined in GTA) in global scope
|
||||
// Do not overload existing symbols (defined in GTA) in global scope.
|
||||
sym, _, _ = sc.lookup(dest.ident)
|
||||
}
|
||||
if sym == nil {
|
||||
@@ -679,7 +755,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
n.gen = nop
|
||||
src.findex = dest.findex
|
||||
src.level = level
|
||||
case len(n.child) < 4 && isArithmeticAction(src):
|
||||
case len(n.child) < 4 && n.kind != defineStmt && isArithmeticAction(src) && !isInterface(dest.typ):
|
||||
// Optimize single assignments from some arithmetic operations.
|
||||
src.typ = dest.typ
|
||||
src.findex = dest.findex
|
||||
@@ -792,7 +868,11 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
n.typ = sc.getType("bool")
|
||||
if c0.sym == nilSym || c1.sym == nilSym {
|
||||
if n.action == aEqual {
|
||||
n.gen = isNil
|
||||
if c1.sym == nilSym {
|
||||
n.gen = isNilChild(0)
|
||||
} else {
|
||||
n.gen = isNilChild(1)
|
||||
}
|
||||
} else {
|
||||
n.gen = isNotNil
|
||||
}
|
||||
@@ -828,9 +908,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
case n.anc.kind == returnStmt:
|
||||
// To avoid a copy in frame, if the result is to be returned, store it directly
|
||||
// at the frame location reserved for output arguments.
|
||||
pos := childPos(n)
|
||||
n.typ = sc.def.typ.ret[pos]
|
||||
n.findex = pos
|
||||
n.findex = childPos(n)
|
||||
default:
|
||||
// Allocate a new location in frame, and store the result here.
|
||||
n.findex = sc.add(n.typ)
|
||||
@@ -843,7 +921,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
wireChild(n)
|
||||
t := n.child[0].typ
|
||||
for t.cat == aliasT {
|
||||
for t.cat == linkedT {
|
||||
t = t.val
|
||||
}
|
||||
switch t.cat {
|
||||
@@ -869,16 +947,18 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
n.typ = t
|
||||
return
|
||||
}
|
||||
g, err := genAST(sc, t.node.anc, []*node{c1})
|
||||
g, found, err := genAST(sc, t.node.anc, []*itype{c1.typ})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = interp.cfg(g, nil, importPath, pkgName); err != nil {
|
||||
return
|
||||
}
|
||||
// Generate closures for function body.
|
||||
if err = genRun(g.child[3]); err != nil {
|
||||
return
|
||||
if !found {
|
||||
if _, err = interp.cfg(g, t.node.anc.scope, importPath, pkgName); err != nil {
|
||||
return
|
||||
}
|
||||
// Generate closures for function body.
|
||||
if err = genRun(g.child[3]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Replace generic func node by instantiated one.
|
||||
n.anc.child[childPos(n)] = g
|
||||
@@ -960,7 +1040,14 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
case declStmt, exprStmt, sendStmt:
|
||||
case sendStmt:
|
||||
if !isChan(n.child[0].typ) {
|
||||
err = n.cfgErrorf("invalid operation: cannot send to non-channel %s", n.child[0].typ.id())
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case declStmt, exprStmt:
|
||||
wireChild(n)
|
||||
l := n.lastChild()
|
||||
n.findex = l.findex
|
||||
@@ -1021,17 +1108,23 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
case c0.kind == indexListExpr:
|
||||
// Instantiate a generic function then call it.
|
||||
fun := c0.child[0].sym.node
|
||||
g, err := genAST(sc, fun, c0.child[1:])
|
||||
lt := []*itype{}
|
||||
for _, c := range c0.child[1:] {
|
||||
lt = append(lt, c.typ)
|
||||
}
|
||||
g, found, err := genAST(sc, fun, lt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = interp.cfg(g, nil, importPath, pkgName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = genRun(g.child[3]) // Generate closures for function body.
|
||||
if err != nil {
|
||||
return
|
||||
if !found {
|
||||
_, err = interp.cfg(g, fun.scope, importPath, pkgName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = genRun(g.child[3]) // Generate closures for function body.
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
n.child[0] = g
|
||||
c0 = n.child[0]
|
||||
@@ -1060,7 +1153,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
|
||||
n.gen = c0.sym.builtin
|
||||
c0.typ = &itype{cat: builtinT}
|
||||
c0.typ = &itype{cat: builtinT, name: bname}
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -1068,6 +1161,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
case n.typ.cat == builtinT:
|
||||
n.findex = notInFrame
|
||||
n.val = nil
|
||||
switch bname {
|
||||
case "unsafe.alignOf", "unsafe.Offsetof", "unsafe.Sizeof":
|
||||
n.gen = nop
|
||||
}
|
||||
case n.anc.kind == returnStmt:
|
||||
// Store result directly to frame output location, to avoid a frame copy.
|
||||
n.findex = 0
|
||||
@@ -1100,8 +1197,8 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
default:
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
if op, ok := constBltn[bname]; ok && n.anc.action != aAssign {
|
||||
op(n) // pre-compute non-assigned constant :
|
||||
if op, ok := constBltn[bname]; ok {
|
||||
op(n)
|
||||
}
|
||||
|
||||
case c0.isType(sc):
|
||||
@@ -1182,44 +1279,32 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
}
|
||||
|
||||
case isOffsetof(c0):
|
||||
if len(n.child) != 2 || n.child[1].kind != selectorExpr || !isStruct(n.child[1].child[0].typ) {
|
||||
err = n.cfgErrorf("Offsetof argument: invalid expression")
|
||||
break
|
||||
}
|
||||
c1 := n.child[1]
|
||||
field, ok := c1.child[0].typ.rtype.FieldByName(c1.child[1].ident)
|
||||
if !ok {
|
||||
err = n.cfgErrorf("struct does not contain field: %s", c1.child[1].ident)
|
||||
break
|
||||
}
|
||||
n.typ = valueTOf(reflect.TypeOf(field.Offset))
|
||||
n.rval = reflect.ValueOf(field.Offset)
|
||||
n.gen = nop
|
||||
|
||||
default:
|
||||
// The call may be on a generic function. In that case, replace the
|
||||
// generic function AST by an instantiated one before going further.
|
||||
if isGeneric(c0.typ) {
|
||||
fun := c0.typ.node.anc
|
||||
var g *node
|
||||
var types []*node
|
||||
var types []*itype
|
||||
var found bool
|
||||
|
||||
// Infer type parameter from function call arguments.
|
||||
if types, err = inferTypesFromCall(sc, fun, n.child[1:]); err != nil {
|
||||
break
|
||||
}
|
||||
// Generate an instantiated AST from the generic function one.
|
||||
if g, err = genAST(sc, fun, types); err != nil {
|
||||
if g, found, err = genAST(sc, fun, types); err != nil {
|
||||
break
|
||||
}
|
||||
// Compile the generated function AST, so it becomes part of the scope.
|
||||
if _, err = interp.cfg(g, nil, importPath, pkgName); err != nil {
|
||||
break
|
||||
}
|
||||
// AST compilation part 2: Generate closures for function body.
|
||||
if err = genRun(g.child[3]); err != nil {
|
||||
break
|
||||
if !found {
|
||||
// Compile the generated function AST, so it becomes part of the scope.
|
||||
if _, err = interp.cfg(g, fun.scope, importPath, pkgName); err != nil {
|
||||
break
|
||||
}
|
||||
// AST compilation part 2: Generate closures for function body.
|
||||
if err = genRun(g.child[3]); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
n.child[0] = g
|
||||
c0 = n.child[0]
|
||||
@@ -1478,6 +1563,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
|
||||
sym, level, found := sc.lookup(n.ident)
|
||||
if !found {
|
||||
if n.typ != nil {
|
||||
// Node is a generic instance with an already populated type.
|
||||
break
|
||||
}
|
||||
// retry with the filename, in case ident is a package name.
|
||||
sym, level, found = sc.lookup(filepath.Join(n.ident, baseName))
|
||||
if !found {
|
||||
@@ -1700,12 +1789,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
if c.typ.cat == nilT {
|
||||
// nil: Set node value to zero of return type
|
||||
if typ.cat == funcT {
|
||||
// Wrap the typed nil value in a node, as per other interpreter functions
|
||||
c.rval = reflect.ValueOf(&node{kind: basicLit, rval: reflect.New(typ.TypeOf()).Elem()})
|
||||
} else {
|
||||
c.rval = reflect.New(typ.TypeOf()).Elem()
|
||||
}
|
||||
c.rval = reflect.New(typ.TypeOf()).Elem()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1728,6 +1812,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
} else {
|
||||
n.typ = valueTOf(fixPossibleConstType(s.Type()), withUntyped(isValueUntyped(s)))
|
||||
n.rval = s
|
||||
if pkg == "unsafe" && (name == "AlignOf" || name == "Offsetof" || name == "Sizeof") {
|
||||
n.sym = &symbol{kind: bltnSym, node: n, rval: s}
|
||||
n.ident = pkg + "." + name
|
||||
}
|
||||
}
|
||||
n.action = aGetSym
|
||||
n.gen = nop
|
||||
@@ -1814,105 +1902,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
tryMethods:
|
||||
fallthrough
|
||||
default:
|
||||
// Find a matching method.
|
||||
// TODO (marc): simplify the following if/elseif blocks.
|
||||
if n.typ.cat == valueT || n.typ.cat == errorT {
|
||||
switch method, ok := n.typ.rtype.MethodByName(n.child[1].ident); {
|
||||
case ok:
|
||||
hasRecvType := n.typ.TypeOf().Kind() != reflect.Interface
|
||||
n.val = method.Index
|
||||
n.gen = getIndexBinMethod
|
||||
n.action = aGetMethod
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.typ = valueTOf(method.Type, isBinMethod())
|
||||
if hasRecvType {
|
||||
n.typ.recv = n.typ
|
||||
}
|
||||
case n.typ.TypeOf().Kind() == reflect.Ptr:
|
||||
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
|
||||
n.typ = valueTOf(field.Type)
|
||||
n.val = field.Index
|
||||
n.gen = getPtrIndexSeq
|
||||
break
|
||||
}
|
||||
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
|
||||
case n.typ.TypeOf().Kind() == reflect.Struct:
|
||||
if field, ok := n.typ.rtype.FieldByName(n.child[1].ident); ok {
|
||||
n.typ = valueTOf(field.Type)
|
||||
n.val = field.Index
|
||||
n.gen = getIndexSeq
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
// method lookup failed on type, now lookup on pointer to type
|
||||
pt := reflect.PtrTo(n.typ.rtype)
|
||||
if m2, ok2 := pt.MethodByName(n.child[1].ident); ok2 {
|
||||
n.val = m2.Index
|
||||
n.gen = getIndexBinPtrMethod
|
||||
n.typ = valueTOf(m2.Type, isBinMethod(), withRecv(valueTOf(pt)))
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.action = aGetMethod
|
||||
break
|
||||
}
|
||||
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
|
||||
}
|
||||
} else if n.typ.cat == ptrT && (n.typ.val.cat == valueT || n.typ.val.cat == errorT) {
|
||||
// Handle pointer on object defined in runtime
|
||||
if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
|
||||
n.val = method.Index
|
||||
n.typ = valueTOf(method.Type, isBinMethod(), withRecv(n.typ))
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.gen = getIndexBinElemMethod
|
||||
n.action = aGetMethod
|
||||
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
|
||||
n.val = method.Index
|
||||
n.gen = getIndexBinMethod
|
||||
n.typ = valueTOf(method.Type, withRecv(valueTOf(reflect.PtrTo(n.typ.val.rtype), isBinMethod())))
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.action = aGetMethod
|
||||
} else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
|
||||
n.typ = valueTOf(field.Type)
|
||||
n.val = field.Index
|
||||
n.gen = getPtrIndexSeq
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
|
||||
}
|
||||
} else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil {
|
||||
n.action = aGetMethod
|
||||
if n.child[0].isType(sc) {
|
||||
// Handle method as a function with receiver in 1st argument.
|
||||
n.val = m
|
||||
n.findex = notInFrame
|
||||
n.gen = nop
|
||||
n.typ = &itype{}
|
||||
*n.typ = *m.typ
|
||||
n.typ.arg = append([]*itype{n.child[0].typ}, m.typ.arg...)
|
||||
} else {
|
||||
// Handle method with receiver.
|
||||
n.gen = getMethod
|
||||
n.val = m
|
||||
n.typ = m.typ
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
}
|
||||
} else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
|
||||
n.action = aGetMethod
|
||||
switch {
|
||||
case isPtr && n.typ.fieldSeq(lind).cat != ptrT:
|
||||
n.gen = getIndexSeqPtrMethod
|
||||
case isInterfaceSrc(n.typ):
|
||||
n.gen = getMethodByName
|
||||
default:
|
||||
n.gen = getIndexSeqMethod
|
||||
}
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
n.val = append([]int{m.Index}, lind...)
|
||||
n.typ = valueTOf(m.Type, isBinMethod(), withRecv(n.child[0].typ))
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
|
||||
}
|
||||
err = matchSelectorMethod(sc, n)
|
||||
}
|
||||
if err == nil && n.findex != -1 {
|
||||
if err == nil && n.findex != -1 && n.typ.cat != genericT {
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
|
||||
@@ -1920,9 +1912,13 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
wireChild(n)
|
||||
// Move action to block statement, so select node can be an exit point.
|
||||
n.child[0].gen = _select
|
||||
// Chain channel init actions in commClauses prior to invoke select.
|
||||
// Chain channel init actions in commClauses prior to invoking select.
|
||||
var cur *node
|
||||
for _, c := range n.child[0].child {
|
||||
if c.kind == commClauseDefault {
|
||||
// No channel init in this case.
|
||||
continue
|
||||
}
|
||||
var an, pn *node // channel init action nodes
|
||||
if len(c.child) > 0 {
|
||||
switch c0 := c.child[0]; {
|
||||
@@ -2327,6 +2323,7 @@ func compDefineX(sc *scope, n *node) error {
|
||||
canRedeclare := hasNewSymbol && len(symIsNew) > 1 && !symIsNew[id] && ok
|
||||
if canRedeclare && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() {
|
||||
index = sym.index
|
||||
n.child[i].redeclared = true
|
||||
} else {
|
||||
index = sc.add(t)
|
||||
sc.sym[id] = &symbol{index: index, kind: varSym, typ: t}
|
||||
@@ -2367,11 +2364,13 @@ func (n *node) cfgErrorf(format string, a ...interface{}) *cfgError {
|
||||
|
||||
func genRun(nod *node) error {
|
||||
var err error
|
||||
seen := map[*node]bool{}
|
||||
|
||||
nod.Walk(func(n *node) bool {
|
||||
if err != nil {
|
||||
if err != nil || seen[n] {
|
||||
return false
|
||||
}
|
||||
seen[n] = true
|
||||
switch n.kind {
|
||||
case funcType:
|
||||
if len(n.anc.child) == 4 {
|
||||
@@ -2795,10 +2794,6 @@ func isBinCall(n *node, sc *scope) bool {
|
||||
return c0.typ.cat == valueT && c0.typ.rtype.Kind() == reflect.Func
|
||||
}
|
||||
|
||||
func isOffsetof(n *node) bool {
|
||||
return n.typ != nil && n.typ.cat == valueT && n.rval.String() == "Offsetof"
|
||||
}
|
||||
|
||||
func mustReturnValue(n *node) bool {
|
||||
if len(n.child) < 3 {
|
||||
return false
|
||||
@@ -2886,7 +2881,7 @@ func typeSwichAssign(n *node) bool {
|
||||
|
||||
func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerator) {
|
||||
switch typ.cat {
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
gen = compositeGenerator(n, typ.val, rtyp)
|
||||
case arrayT, sliceT:
|
||||
gen = arrayLit
|
||||
@@ -2934,6 +2929,124 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
|
||||
return gen
|
||||
}
|
||||
|
||||
// matchSelectorMethod, given that n represents a selector for a method, tries
|
||||
// to find the corresponding method, and populates n accordingly.
|
||||
func matchSelectorMethod(sc *scope, n *node) (err error) {
|
||||
name := n.child[1].ident
|
||||
if n.typ.cat == valueT || n.typ.cat == errorT {
|
||||
switch method, ok := n.typ.rtype.MethodByName(name); {
|
||||
case ok:
|
||||
hasRecvType := n.typ.TypeOf().Kind() != reflect.Interface
|
||||
n.val = method.Index
|
||||
n.gen = getIndexBinMethod
|
||||
n.action = aGetMethod
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.typ = valueTOf(method.Type, isBinMethod())
|
||||
if hasRecvType {
|
||||
n.typ.recv = n.typ
|
||||
}
|
||||
case n.typ.TypeOf().Kind() == reflect.Ptr:
|
||||
if field, ok := n.typ.rtype.Elem().FieldByName(name); ok {
|
||||
n.typ = valueTOf(field.Type)
|
||||
n.val = field.Index
|
||||
n.gen = getPtrIndexSeq
|
||||
break
|
||||
}
|
||||
err = n.cfgErrorf("undefined method: %s", name)
|
||||
case n.typ.TypeOf().Kind() == reflect.Struct:
|
||||
if field, ok := n.typ.rtype.FieldByName(name); ok {
|
||||
n.typ = valueTOf(field.Type)
|
||||
n.val = field.Index
|
||||
n.gen = getIndexSeq
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
// method lookup failed on type, now lookup on pointer to type
|
||||
pt := reflect.PtrTo(n.typ.rtype)
|
||||
if m2, ok2 := pt.MethodByName(name); ok2 {
|
||||
n.val = m2.Index
|
||||
n.gen = getIndexBinPtrMethod
|
||||
n.typ = valueTOf(m2.Type, isBinMethod(), withRecv(valueTOf(pt)))
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.action = aGetMethod
|
||||
break
|
||||
}
|
||||
err = n.cfgErrorf("undefined method: %s", name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if n.typ.cat == ptrT && (n.typ.val.cat == valueT || n.typ.val.cat == errorT) {
|
||||
// Handle pointer on object defined in runtime
|
||||
if method, ok := n.typ.val.rtype.MethodByName(name); ok {
|
||||
n.val = method.Index
|
||||
n.typ = valueTOf(method.Type, isBinMethod(), withRecv(n.typ))
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.gen = getIndexBinElemMethod
|
||||
n.action = aGetMethod
|
||||
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(name); ok {
|
||||
n.val = method.Index
|
||||
n.gen = getIndexBinMethod
|
||||
n.typ = valueTOf(method.Type, withRecv(valueTOf(reflect.PtrTo(n.typ.val.rtype), isBinMethod())))
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.action = aGetMethod
|
||||
} else if field, ok := n.typ.val.rtype.FieldByName(name); ok {
|
||||
n.typ = valueTOf(field.Type)
|
||||
n.val = field.Index
|
||||
n.gen = getPtrIndexSeq
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector: %s", name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if m, lind := n.typ.lookupMethod(name); m != nil {
|
||||
n.action = aGetMethod
|
||||
if n.child[0].isType(sc) {
|
||||
// Handle method as a function with receiver in 1st argument.
|
||||
n.val = m
|
||||
n.findex = notInFrame
|
||||
n.gen = nop
|
||||
n.typ = &itype{}
|
||||
*n.typ = *m.typ
|
||||
n.typ.arg = append([]*itype{n.child[0].typ}, m.typ.arg...)
|
||||
} else {
|
||||
// Handle method with receiver.
|
||||
n.gen = getMethod
|
||||
n.val = m
|
||||
n.typ = m.typ
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if m, lind, isPtr, ok := n.typ.lookupBinMethod(name); ok {
|
||||
n.action = aGetMethod
|
||||
switch {
|
||||
case isPtr && n.typ.fieldSeq(lind).cat != ptrT:
|
||||
n.gen = getIndexSeqPtrMethod
|
||||
case isInterfaceSrc(n.typ):
|
||||
n.gen = getMethodByName
|
||||
default:
|
||||
n.gen = getIndexSeqMethod
|
||||
}
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
n.val = append([]int{m.Index}, lind...)
|
||||
n.typ = valueTOf(m.Type, isBinMethod(), withRecv(n.child[0].typ))
|
||||
return nil
|
||||
}
|
||||
|
||||
if typ := n.typ.interfaceMethod(name); typ != nil {
|
||||
n.typ = typ
|
||||
n.action = aGetMethod
|
||||
n.gen = getMethodByName
|
||||
return nil
|
||||
}
|
||||
|
||||
return n.cfgErrorf("undefined selector: %s", name)
|
||||
}
|
||||
|
||||
// arrayTypeLen returns the node's array length. If the expression is an
|
||||
// array variable it is determined from the value's type, otherwise it is
|
||||
// computed from the source definition.
|
||||
@@ -3019,3 +3132,24 @@ func isBlank(n *node) bool {
|
||||
}
|
||||
return n.ident == "_"
|
||||
}
|
||||
|
||||
func alignof(n *node) {
|
||||
n.gen = nop
|
||||
n.typ = n.scope.getType("uintptr")
|
||||
n.rval = reflect.ValueOf(uintptr(n.child[1].typ.TypeOf().Align()))
|
||||
}
|
||||
|
||||
func offsetof(n *node) {
|
||||
n.gen = nop
|
||||
n.typ = n.scope.getType("uintptr")
|
||||
c1 := n.child[1]
|
||||
if field, ok := c1.child[0].typ.rtype.FieldByName(c1.child[1].ident); ok {
|
||||
n.rval = reflect.ValueOf(field.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
func sizeof(n *node) {
|
||||
n.gen = nop
|
||||
n.typ = n.scope.getType("uintptr")
|
||||
n.rval = reflect.ValueOf(n.child[1].typ.TypeOf().Size())
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@ import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// adot produces an AST dot(1) directed acyclic graph for the given node. For debugging only.
|
||||
// func (n *node) adot() { n.astDot(dotWriter(n.interp.dotCmd), n.ident) }
|
||||
|
||||
// genAST returns a new AST where generic types are replaced by instantiated types.
|
||||
func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
func genAST(sc *scope, root *node, types []*itype) (*node, bool, error) {
|
||||
typeParam := map[string]*node{}
|
||||
pindex := 0
|
||||
tname := ""
|
||||
@@ -14,9 +17,20 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
recvrPtr := false
|
||||
fixNodes := []*node{}
|
||||
var gtree func(*node, *node) (*node, error)
|
||||
sname := root.child[0].ident + "["
|
||||
if root.kind == funcDecl {
|
||||
sname = root.child[1].ident + "["
|
||||
}
|
||||
|
||||
// Input type parameters must be resolved prior AST generation, as compilation
|
||||
// of generated AST may occur in a different scope.
|
||||
for _, t := range types {
|
||||
sname += t.id() + ","
|
||||
}
|
||||
sname = strings.TrimSuffix(sname, ",") + "]"
|
||||
|
||||
gtree = func(n, anc *node) (*node, error) {
|
||||
nod := copyNode(n, anc)
|
||||
nod := copyNode(n, anc, false)
|
||||
switch n.kind {
|
||||
case funcDecl, funcType:
|
||||
nod.val = nod
|
||||
@@ -27,7 +41,8 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
nod = copyNode(nt, anc)
|
||||
nod = copyNode(nt, anc, true)
|
||||
nod.typ = nt.typ
|
||||
|
||||
case indexExpr:
|
||||
// Catch a possible recursive generic type definition
|
||||
@@ -37,7 +52,7 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
if root.child[0].ident != n.child[0].ident {
|
||||
break
|
||||
}
|
||||
nod := copyNode(n.child[0], anc)
|
||||
nod := copyNode(n.child[0], anc, false)
|
||||
fixNodes = append(fixNodes, nod)
|
||||
return nod, nil
|
||||
|
||||
@@ -51,10 +66,16 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
if pindex >= len(types) {
|
||||
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
|
||||
}
|
||||
if err := checkConstraint(sc, types[pindex], c.child[l]); err != nil {
|
||||
t, err := nodeType(c.interp, sc, c.child[l])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeParam[cc.ident] = types[pindex]
|
||||
if err := checkConstraint(types[pindex], t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeParam[cc.ident] = copyNode(cc, cc.anc, false)
|
||||
typeParam[cc.ident].ident = types[pindex].id()
|
||||
typeParam[cc.ident].typ = types[pindex]
|
||||
pindex++
|
||||
}
|
||||
}
|
||||
@@ -65,9 +86,9 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
// Node is the receiver of a generic method.
|
||||
if root.kind == funcDecl && n.anc == root && childPos(n) == 0 && len(n.child) > 0 {
|
||||
rtn := n.child[0].child[1]
|
||||
if rtn.kind == indexExpr || (rtn.kind == starExpr && rtn.child[0].kind == indexExpr) {
|
||||
// Method receiver is a generic type.
|
||||
if rtn.kind == starExpr && rtn.child[0].kind == indexExpr {
|
||||
// Method receiver is a generic type if it takes some type parameters.
|
||||
if rtn.kind == indexExpr || rtn.kind == indexListExpr || (rtn.kind == starExpr && (rtn.child[0].kind == indexExpr || rtn.child[0].kind == indexListExpr)) {
|
||||
if rtn.kind == starExpr {
|
||||
// Method receiver is a pointer on a generic type.
|
||||
rtn = rtn.child[0]
|
||||
recvrPtr = true
|
||||
@@ -77,11 +98,10 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
if pindex >= len(types) {
|
||||
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
|
||||
}
|
||||
it, err := nodeType(n.interp, sc, types[pindex])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeParam[cc.ident] = types[pindex]
|
||||
it := types[pindex]
|
||||
typeParam[cc.ident] = copyNode(cc, cc.anc, false)
|
||||
typeParam[cc.ident].ident = it.id()
|
||||
typeParam[cc.ident].typ = it
|
||||
rtname += it.id() + ","
|
||||
pindex++
|
||||
}
|
||||
@@ -99,14 +119,17 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
if pindex >= len(types) {
|
||||
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
|
||||
}
|
||||
it, err := nodeType(n.interp, sc, types[pindex])
|
||||
it := types[pindex]
|
||||
t, err := nodeType(c.interp, sc, c.child[l])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := checkConstraint(sc, types[pindex], c.child[l]); err != nil {
|
||||
if err := checkConstraint(types[pindex], t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeParam[cc.ident] = types[pindex]
|
||||
typeParam[cc.ident] = copyNode(cc, cc.anc, false)
|
||||
typeParam[cc.ident].ident = it.id()
|
||||
typeParam[cc.ident].typ = it
|
||||
tname += it.id() + ","
|
||||
pindex++
|
||||
}
|
||||
@@ -115,6 +138,7 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
return nod, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range n.child {
|
||||
gn, err := gtree(c, nod)
|
||||
if err != nil {
|
||||
@@ -125,10 +149,16 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
return nod, nil
|
||||
}
|
||||
|
||||
if nod, found := root.interp.generic[sname]; found {
|
||||
return nod, true, nil
|
||||
}
|
||||
|
||||
r, err := gtree(root, root.anc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
root.interp.generic[sname] = r
|
||||
r.param = append(r.param, types...)
|
||||
if tname != "" {
|
||||
for _, nod := range fixNodes {
|
||||
nod.ident = tname
|
||||
@@ -145,11 +175,11 @@ func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
nod.ident = rtname
|
||||
nod.child = nil
|
||||
}
|
||||
// r.astDot(dotWriter(root.interp.dotCmd), root.child[1].ident) // Used for debugging only.
|
||||
return r, nil
|
||||
// r.adot() // Used for debugging only.
|
||||
return r, false, nil
|
||||
}
|
||||
|
||||
func copyNode(n, anc *node) *node {
|
||||
func copyNode(n, anc *node, recursive bool) *node {
|
||||
var i interface{}
|
||||
nindex := atomic.AddInt64(&n.interp.nindex, 1)
|
||||
nod := &node{
|
||||
@@ -170,25 +200,30 @@ func copyNode(n, anc *node) *node {
|
||||
meta: n.meta,
|
||||
}
|
||||
nod.start = nod
|
||||
if recursive {
|
||||
for _, c := range n.child {
|
||||
nod.child = append(nod.child, copyNode(c, nod, true))
|
||||
}
|
||||
}
|
||||
return nod
|
||||
}
|
||||
|
||||
func inferTypesFromCall(sc *scope, fun *node, args []*node) ([]*node, error) {
|
||||
func inferTypesFromCall(sc *scope, fun *node, args []*node) ([]*itype, error) {
|
||||
ftn := fun.typ.node
|
||||
// Fill the map of parameter types, indexed by type param ident.
|
||||
types := map[string]*itype{}
|
||||
paramTypes := map[string]*itype{}
|
||||
for _, c := range ftn.child[0].child {
|
||||
typ, err := nodeType(fun.interp, sc, c.lastChild())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, cc := range c.child[:len(c.child)-1] {
|
||||
types[cc.ident] = typ
|
||||
paramTypes[cc.ident] = typ
|
||||
}
|
||||
}
|
||||
|
||||
var inferTypes func(*itype, *itype) ([]*node, error)
|
||||
inferTypes = func(param, input *itype) ([]*node, error) {
|
||||
var inferTypes func(*itype, *itype) ([]*itype, error)
|
||||
inferTypes = func(param, input *itype) ([]*itype, error) {
|
||||
switch param.cat {
|
||||
case chanT, ptrT, sliceT:
|
||||
return inferTypes(param.val, input.val)
|
||||
@@ -205,77 +240,80 @@ func inferTypesFromCall(sc *scope, fun *node, args []*node) ([]*node, error) {
|
||||
return append(k, v...), nil
|
||||
|
||||
case structT:
|
||||
nods := []*node{}
|
||||
lt := []*itype{}
|
||||
for i, f := range param.field {
|
||||
nl, err := inferTypes(f.typ, input.field[i].typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nods = append(nods, nl...)
|
||||
lt = append(lt, nl...)
|
||||
}
|
||||
return nods, nil
|
||||
return lt, nil
|
||||
|
||||
case funcT:
|
||||
nods := []*node{}
|
||||
lt := []*itype{}
|
||||
for i, t := range param.arg {
|
||||
if i >= len(input.arg) {
|
||||
break
|
||||
}
|
||||
nl, err := inferTypes(t, input.arg[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nods = append(nods, nl...)
|
||||
lt = append(lt, nl...)
|
||||
}
|
||||
for i, t := range param.ret {
|
||||
if i >= len(input.ret) {
|
||||
break
|
||||
}
|
||||
nl, err := inferTypes(t, input.ret[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nods = append(nods, nl...)
|
||||
lt = append(lt, nl...)
|
||||
}
|
||||
return lt, nil
|
||||
|
||||
case nilT:
|
||||
if paramTypes[param.name] != nil {
|
||||
return []*itype{input}, nil
|
||||
}
|
||||
return nods, nil
|
||||
|
||||
case genericT:
|
||||
return []*node{input.node}, nil
|
||||
return []*itype{input}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
nodes := []*node{}
|
||||
types := []*itype{}
|
||||
for i, c := range ftn.child[1].child {
|
||||
typ, err := nodeType(fun.interp, sc, c.lastChild())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nods, err := inferTypes(typ, args[i].typ)
|
||||
lt, err := inferTypes(typ, args[i].typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes = append(nodes, nods...)
|
||||
types = append(types, lt...)
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
return types, nil
|
||||
}
|
||||
|
||||
func checkConstraint(sc *scope, input, constraint *node) error {
|
||||
ct, err := nodeType(constraint.interp, sc, constraint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it, err := nodeType(input.interp, sc, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func checkConstraint(it, ct *itype) error {
|
||||
if len(ct.constraint) == 0 && len(ct.ulconstraint) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, c := range ct.constraint {
|
||||
if it.equals(c) {
|
||||
if it.equals(c) || it.matchDefault(c) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for _, c := range ct.ulconstraint {
|
||||
if it.underlying().equals(c) {
|
||||
if it.underlying().equals(c) || it.matchDefault(c) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return input.cfgErrorf("%s does not implement %s", input.typ.id(), ct.id())
|
||||
return it.node.cfgErrorf("%s does not implement %s", it.id(), ct.id())
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if n.scope == nil {
|
||||
n.scope = sc
|
||||
}
|
||||
switch n.kind {
|
||||
case constDecl:
|
||||
// Early parse of constDecl subtree, to compute all constant
|
||||
@@ -161,11 +164,12 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
switch rtn.kind {
|
||||
case starExpr:
|
||||
typPtr = true
|
||||
if rtn.child[0].kind == indexExpr {
|
||||
typName = rtn.child[0].child[0].ident
|
||||
switch c := rtn.child[0]; c.kind {
|
||||
case indexExpr, indexListExpr:
|
||||
typName = c.child[0].ident
|
||||
genericMethod = true
|
||||
}
|
||||
case indexExpr:
|
||||
case indexExpr, indexListExpr:
|
||||
genericMethod = true
|
||||
}
|
||||
}
|
||||
@@ -175,7 +179,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
if sym.kind != typeSym || (sym.node != nil && sym.node.kind == typeSpecAssign) {
|
||||
if sym.typ.path != pkgName {
|
||||
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(sym.typ).id())
|
||||
return false
|
||||
}
|
||||
@@ -188,6 +192,14 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
}
|
||||
rcvrtype.addMethod(n)
|
||||
rtn.typ = rcvrtype
|
||||
if rcvrtype.cat == genericT {
|
||||
// generate methods for already instantiated receivers
|
||||
for _, it := range rcvrtype.instance {
|
||||
if err = genMethod(interp, sc, it, n, it.node.anc.param); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
case ident == "init":
|
||||
// init functions do not get declared as per the Go spec.
|
||||
default:
|
||||
@@ -297,7 +309,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
typeName := n.child[0].ident
|
||||
if len(n.child) > 2 {
|
||||
// Handle a generic type: skip definition as parameter is not instantiated yet.
|
||||
n.typ = genericOf(nil, typeName, withNode(n.child[0]), withScope(sc))
|
||||
n.typ = genericOf(nil, typeName, pkgName, withNode(n.child[0]), withScope(sc))
|
||||
if _, exists := sc.sym[typeName]; !exists {
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
}
|
||||
@@ -311,6 +323,15 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
return false
|
||||
}
|
||||
|
||||
if n.kind == typeSpecAssign {
|
||||
// Create an aliased type in the current scope
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n, typ: typ}
|
||||
n.typ = typ
|
||||
break
|
||||
}
|
||||
|
||||
// else we are not an alias (typeSpec)
|
||||
|
||||
switch n.child[1].kind {
|
||||
case identExpr, selectorExpr:
|
||||
n.typ = namedOf(typ, pkgName, typeName, withNode(n.child[0]), withScope(sc))
|
||||
@@ -332,24 +353,15 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
}
|
||||
sym, exists := sc.sym[typeName]
|
||||
if !exists {
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
} else {
|
||||
if sym.typ != nil && (len(sym.typ.method) > 0) {
|
||||
if n.kind == typeSpecAssign {
|
||||
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(typ).id())
|
||||
return false
|
||||
}
|
||||
// Type has already been seen as a receiver in a method function
|
||||
for _, m := range sym.typ.method {
|
||||
n.typ.addMethod(m)
|
||||
}
|
||||
} else {
|
||||
// TODO(mpl): figure out how to detect redeclarations without breaking type aliases.
|
||||
// Allow redeclarations for now.
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
sym = &symbol{kind: typeSym, node: n}
|
||||
sc.sym[typeName] = sym
|
||||
} else if sym.typ != nil && (len(sym.typ.method) > 0) {
|
||||
// Type has already been seen as a receiver in a method function
|
||||
for _, m := range sym.typ.method {
|
||||
n.typ.addMethod(m)
|
||||
}
|
||||
}
|
||||
sc.sym[typeName].typ = n.typ
|
||||
sym.typ = n.typ
|
||||
if !n.typ.isComplete() {
|
||||
revisit = append(revisit, n)
|
||||
}
|
||||
@@ -367,7 +379,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
func baseType(t *itype) *itype {
|
||||
for {
|
||||
switch t.cat {
|
||||
case ptrT, aliasT:
|
||||
case ptrT, linkedT:
|
||||
t = t.val
|
||||
default:
|
||||
return t
|
||||
@@ -439,7 +451,7 @@ func definedType(typ *itype) error {
|
||||
return err
|
||||
}
|
||||
fallthrough
|
||||
case aliasT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT:
|
||||
case linkedT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT:
|
||||
if err := definedType(typ.val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -25,33 +25,35 @@ import (
|
||||
|
||||
// Interpreter node structure for AST and CFG.
|
||||
type node struct {
|
||||
debug *nodeDebugData // debug info
|
||||
child []*node // child subtrees (AST)
|
||||
anc *node // ancestor (AST)
|
||||
start *node // entry point in subtree (CFG)
|
||||
tnext *node // true branch successor (CFG)
|
||||
fnext *node // false branch successor (CFG)
|
||||
interp *Interpreter // interpreter context
|
||||
frame *frame // frame pointer used for closures only (TODO: suppress this)
|
||||
index int64 // node index (dot display)
|
||||
findex int // index of value in frame or frame size (func def, type def)
|
||||
level int // number of frame indirections to access value
|
||||
nleft int // number of children in left part (assign) or indicates preceding type (compositeLit)
|
||||
nright int // number of children in right part (assign)
|
||||
kind nkind // kind of node
|
||||
pos token.Pos // position in source code, relative to fset
|
||||
sym *symbol // associated symbol
|
||||
typ *itype // type of value in frame, or nil
|
||||
recv *receiver // method receiver node for call, or nil
|
||||
types []reflect.Type // frame types, used by function literals only
|
||||
scope *scope // frame scope
|
||||
action action // action
|
||||
exec bltn // generated function to execute
|
||||
gen bltnGenerator // generator function to produce above bltn
|
||||
val interface{} // static generic value (CFG execution)
|
||||
rval reflect.Value // reflection value to let runtime access interpreter (CFG)
|
||||
ident string // set if node is a var or func
|
||||
meta interface{} // meta stores meta information between gta runs, like errors
|
||||
debug *nodeDebugData // debug info
|
||||
child []*node // child subtrees (AST)
|
||||
anc *node // ancestor (AST)
|
||||
param []*itype // generic parameter nodes (AST)
|
||||
start *node // entry point in subtree (CFG)
|
||||
tnext *node // true branch successor (CFG)
|
||||
fnext *node // false branch successor (CFG)
|
||||
interp *Interpreter // interpreter context
|
||||
frame *frame // frame pointer used for closures only (TODO: suppress this)
|
||||
index int64 // node index (dot display)
|
||||
findex int // index of value in frame or frame size (func def, type def)
|
||||
level int // number of frame indirections to access value
|
||||
nleft int // number of children in left part (assign) or indicates preceding type (compositeLit)
|
||||
nright int // number of children in right part (assign)
|
||||
kind nkind // kind of node
|
||||
pos token.Pos // position in source code, relative to fset
|
||||
sym *symbol // associated symbol
|
||||
typ *itype // type of value in frame, or nil
|
||||
recv *receiver // method receiver node for call, or nil
|
||||
types []reflect.Type // frame types, used by function literals only
|
||||
scope *scope // frame scope
|
||||
action action // action
|
||||
exec bltn // generated function to execute
|
||||
gen bltnGenerator // generator function to produce above bltn
|
||||
val interface{} // static generic value (CFG execution)
|
||||
rval reflect.Value // reflection value to let runtime access interpreter (CFG)
|
||||
ident string // set if node is a var or func
|
||||
redeclared bool // set if node is a redeclared variable (CFG)
|
||||
meta interface{} // meta stores meta information between gta runs, like errors
|
||||
}
|
||||
|
||||
func (n *node) shouldBreak() bool {
|
||||
@@ -104,7 +106,7 @@ type receiver struct {
|
||||
type frame struct {
|
||||
// id is an atomic counter used for cancellation, only accessed
|
||||
// via newFrame/runid/setrunid/clone.
|
||||
// Located at start of struct to ensure proper aligment.
|
||||
// Located at start of struct to ensure proper alignment.
|
||||
id uint64
|
||||
|
||||
debug *frameDebugData
|
||||
@@ -182,14 +184,14 @@ type opt struct {
|
||||
noRun bool // compile, but do not run
|
||||
fastChan bool // disable cancellable chan operations
|
||||
specialStdio bool // allows os.Stdin, os.Stdout, os.Stderr to not be file descriptors
|
||||
unrestricted bool // allow use of non sandboxed symbols
|
||||
unrestricted bool // allow use of non-sandboxed symbols
|
||||
}
|
||||
|
||||
// Interpreter contains global resources and state.
|
||||
type Interpreter struct {
|
||||
// id is an atomic counter counter used for run cancellation,
|
||||
// id is an atomic counter used for run cancellation,
|
||||
// only accessed via runid/stop
|
||||
// Located at start of struct to ensure proper alignment on 32 bit
|
||||
// Located at start of struct to ensure proper alignment on 32-bit
|
||||
// architectures.
|
||||
id uint64
|
||||
|
||||
@@ -215,6 +217,7 @@ type Interpreter struct {
|
||||
pkgNames map[string]string // package names, indexed by import path
|
||||
done chan struct{} // for cancellation of channel operations
|
||||
roots []*node
|
||||
generic map[string]*node
|
||||
|
||||
hooks *hooks // symbol hooks
|
||||
|
||||
@@ -335,6 +338,7 @@ func New(options Options) *Interpreter {
|
||||
pkgNames: map[string]string{},
|
||||
rdir: map[string]bool{},
|
||||
hooks: &hooks{},
|
||||
generic: map[string]*node{},
|
||||
}
|
||||
|
||||
if i.opt.stdin = options.Stdin; i.opt.stdin == nil {
|
||||
@@ -401,21 +405,24 @@ func New(options Options) *Interpreter {
|
||||
}
|
||||
|
||||
const (
|
||||
bltnAppend = "append"
|
||||
bltnCap = "cap"
|
||||
bltnClose = "close"
|
||||
bltnComplex = "complex"
|
||||
bltnImag = "imag"
|
||||
bltnCopy = "copy"
|
||||
bltnDelete = "delete"
|
||||
bltnLen = "len"
|
||||
bltnMake = "make"
|
||||
bltnNew = "new"
|
||||
bltnPanic = "panic"
|
||||
bltnPrint = "print"
|
||||
bltnPrintln = "println"
|
||||
bltnReal = "real"
|
||||
bltnRecover = "recover"
|
||||
bltnAlignof = "unsafe.Alignof"
|
||||
bltnAppend = "append"
|
||||
bltnCap = "cap"
|
||||
bltnClose = "close"
|
||||
bltnComplex = "complex"
|
||||
bltnImag = "imag"
|
||||
bltnCopy = "copy"
|
||||
bltnDelete = "delete"
|
||||
bltnLen = "len"
|
||||
bltnMake = "make"
|
||||
bltnNew = "new"
|
||||
bltnOffsetof = "unsafe.Offsetof"
|
||||
bltnPanic = "panic"
|
||||
bltnPrint = "print"
|
||||
bltnPrintln = "println"
|
||||
bltnReal = "real"
|
||||
bltnRecover = "recover"
|
||||
bltnSizeof = "unsafe.Sizeof"
|
||||
)
|
||||
|
||||
func initUniverse() *scope {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package interp_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/build"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -14,6 +16,13 @@ import (
|
||||
"github.com/traefik/yaegi/stdlib/unsafe"
|
||||
)
|
||||
|
||||
// The following tests depend on an incompatible language change in go1.22, where `for` variables are now
|
||||
// defined in body (thus reallocated at each loop). We skip them until both supported versions behave the same.
|
||||
// We will remove this in Go1.23.
|
||||
var testsToSkipGo122 = map[string]bool{"closure9.go": true, "closure10.go": true, "closure11.go": true, "closure12.go": true}
|
||||
|
||||
var go122 = strings.HasPrefix(runtime.Version(), "go1.22")
|
||||
|
||||
func TestInterpConsistencyBuild(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("short mode")
|
||||
@@ -89,6 +98,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "redeclaration-global5.go" || // expect error
|
||||
file.Name() == "redeclaration-global6.go" || // expect error
|
||||
file.Name() == "redeclaration-global7.go" || // expect error
|
||||
file.Name() == "panic0.go" || // expect error
|
||||
file.Name() == "pkgname0.go" || // has deps
|
||||
file.Name() == "pkgname1.go" || // expect error
|
||||
file.Name() == "pkgname2.go" || // has deps
|
||||
@@ -109,6 +119,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "range9.go" || // expect error
|
||||
file.Name() == "unsafe6.go" || // needs go.mod to be 1.17
|
||||
file.Name() == "unsafe7.go" || // needs go.mod to be 1.17
|
||||
file.Name() == "type24.go" || // expect error
|
||||
file.Name() == "type27.go" || // expect error
|
||||
file.Name() == "type28.go" || // expect error
|
||||
file.Name() == "type29.go" || // expect error
|
||||
@@ -118,6 +129,13 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "type33.go" { // expect error
|
||||
continue
|
||||
}
|
||||
// Skip some tests which are problematic in go1.21 only.
|
||||
if go121 && testsToSkipGo121[file.Name()] {
|
||||
continue
|
||||
}
|
||||
if go122 && testsToSkipGo122[file.Name()] {
|
||||
continue
|
||||
}
|
||||
|
||||
file := file
|
||||
t.Run(file.Name(), func(t *testing.T) {
|
||||
@@ -175,7 +193,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if string(outInterp) != string(outRun) {
|
||||
if !bytes.Equal(outInterp, outRun) {
|
||||
t.Errorf("\nGot: %q,\n want: %q", string(outInterp), string(outRun))
|
||||
}
|
||||
})
|
||||
@@ -186,6 +204,7 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
testCases := []struct {
|
||||
fileName string
|
||||
expectedInterp string
|
||||
expectedStderr string
|
||||
expectedExec string
|
||||
}{
|
||||
{
|
||||
@@ -216,7 +235,7 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
{
|
||||
fileName: "const9.go",
|
||||
expectedInterp: "5:2: constant definition loop",
|
||||
expectedExec: "5:2: initialization loop for b",
|
||||
expectedExec: "5:2: initialization",
|
||||
},
|
||||
{
|
||||
fileName: "if2.go",
|
||||
@@ -246,7 +265,7 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
{
|
||||
fileName: "issue-1093.go",
|
||||
expectedInterp: "9:6: cannot use type untyped string as type int in assignment",
|
||||
expectedExec: `9:6: cannot use "a" + b() (value of type string) as type int in assignment`,
|
||||
expectedExec: `9:6: cannot use "a" + b() (value of type string)`,
|
||||
},
|
||||
{
|
||||
fileName: "op1.go",
|
||||
@@ -283,17 +302,28 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
expectedInterp: "37:2: duplicate case Bir in type switch",
|
||||
expectedExec: "37:7: duplicate case Bir in type switch",
|
||||
},
|
||||
{
|
||||
fileName: "panic0.go",
|
||||
expectedInterp: "stop!",
|
||||
expectedStderr: `
|
||||
../_test/panic0.go:16:2: panic: main.baz(...)
|
||||
../_test/panic0.go:12:2: panic: main.bar(...)
|
||||
../_test/panic0.go:8:2: panic: main.foo(...)
|
||||
../_test/panic0.go:4:2: panic: main.main(...)
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.fileName, func(t *testing.T) {
|
||||
if len(test.expectedInterp) == 0 && len(test.expectedExec) == 0 {
|
||||
t.Fatal("at least expectedInterp must be define")
|
||||
if test.expectedInterp == "" && test.expectedExec == "" {
|
||||
t.Fatal("at least expectedInterp must be defined")
|
||||
}
|
||||
|
||||
filePath := filepath.Join("..", "_test", test.fileName)
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
|
||||
var stderr bytes.Buffer
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, Stderr: &stderr})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -306,6 +336,12 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
if !strings.Contains(errEval.Error(), test.expectedInterp) {
|
||||
t.Errorf("got %q, want: %q", errEval.Error(), test.expectedInterp)
|
||||
}
|
||||
if test.expectedStderr != "" {
|
||||
exp, got := strings.TrimSpace(test.expectedStderr), strings.TrimSpace(stderr.String())
|
||||
if exp != got {
|
||||
t.Errorf("got %q, want: %q", got, exp)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "run", filePath)
|
||||
outRun, errExec := cmd.CombinedOutput()
|
||||
@@ -314,7 +350,7 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
t.Fatal("An error is expected but got none.")
|
||||
}
|
||||
|
||||
if len(test.expectedExec) == 0 && !strings.Contains(string(outRun), test.expectedInterp) {
|
||||
if test.expectedExec == "" && !strings.Contains(string(outRun), test.expectedInterp) {
|
||||
t.Errorf("got %q, want: %q", string(outRun), test.expectedInterp)
|
||||
} else if !strings.Contains(string(outRun), test.expectedExec) {
|
||||
t.Errorf("got %q, want: %q", string(outRun), test.expectedExec)
|
||||
|
||||
@@ -135,6 +135,7 @@ func TestEvalAssign(t *testing.T) {
|
||||
{src: "j := interface{}(int(1)); j.(_)", err: "1:54: cannot use _ as value"},
|
||||
{src: "ff := func() (a, b, c int) {return 1, 2, 3}; x, y, x := ff()", err: "1:73: x repeated on left side of :="},
|
||||
{src: "xx := 1; xx, _ := 2, 3", err: "1:37: no new variables on left side of :="},
|
||||
{src: "1 = 2", err: "1:28: cannot assign to 1 (untyped int constant)"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -218,6 +219,8 @@ func TestEvalTypeSpec(t *testing.T) {
|
||||
runTests(t, i, []testCase{
|
||||
{src: `type _ struct{}`, err: "1:19: cannot use _ as value"},
|
||||
{src: `a := struct{a, _ int}{32, 0}`, res: "{32 0}"},
|
||||
{src: "type A int; type A = string", err: "1:31: A redeclared in this block"},
|
||||
{src: "type B int; type B string", err: "1:31: B redeclared in this block"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -542,8 +545,8 @@ func TestEvalSliceExpression(t *testing.T) {
|
||||
{src: `a := (&[]int{0,1,2,3})[1:3]`, err: "1:33: cannot slice type *[]int"},
|
||||
{src: `a := "hello"[1:3:4]`, err: "1:45: invalid operation: 3-index slice of string"},
|
||||
{src: `ar := [3]int{0,1,2}; a := ar[:4]`, err: "1:58: index int is out of bounds"},
|
||||
{src: `a := []int{0,1,2,3}[1::4]`, err: "1:49: 2nd index required in 3-index slice"},
|
||||
{src: `a := []int{0,1,2,3}[1:3:]`, err: "1:51: 3rd index required in 3-index slice"},
|
||||
{src: `a := []int{0,1,2,3}[1::4]`, err: "index required in 3-index slice"},
|
||||
{src: `a := []int{0,1,2,3}[1:3:]`, err: "index required in 3-index slice"},
|
||||
{src: `a := []int{0,1,2}[3:1]`, err: "invalid index values, must be low <= high <= max"},
|
||||
{pre: func() { eval(t, i, `type Str = string; var r Str = "truc"`) }, src: `r[1]`, res: "114"},
|
||||
{src: `_[12]`, err: "1:28: cannot use _ as value"},
|
||||
@@ -642,6 +645,8 @@ func TestEvalChan(t *testing.T) {
|
||||
return ok && msg == "ping"
|
||||
})()`, res: "true",
|
||||
},
|
||||
{src: `a :=5; a <- 4`, err: "cannot send to non-channel int"},
|
||||
{src: `a :=5; b := <-a`, err: "cannot receive from non-channel int"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -703,7 +708,7 @@ func TestEvalCall(t *testing.T) {
|
||||
{src: ` test := func(a, b int) int { return a }
|
||||
blah := func() (int, float64) { return 1, 1.1 }
|
||||
a := test(blah())`, err: "3:15: cannot use func() (int,float64) as type (int,int)"},
|
||||
{src: "func f()", err: "function declaration without body is unsupported"},
|
||||
{src: "func f()", err: "missing function body"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1090,6 +1095,10 @@ func main() {
|
||||
}
|
||||
|
||||
func TestImportPathIsKey(t *testing.T) {
|
||||
// FIXME(marc): support of stdlib generic packages like "cmp", "maps", "slices" has changed
|
||||
// the scope layout by introducing new source packages when stdlib is used.
|
||||
// The logic of the following test doesn't apply anymore.
|
||||
t.Skip("This test needs to be reworked.")
|
||||
// No need to check the results of Eval, as TestFile already does it.
|
||||
i := interp.New(interp.Options{GoPath: filepath.FromSlash("../_test/testdata/redeclaration-global7")})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
@@ -1247,10 +1256,9 @@ func TestConcurrentEvals2(t *testing.T) {
|
||||
if hello1 {
|
||||
if l == "hello world2" {
|
||||
break
|
||||
} else {
|
||||
done <- fmt.Errorf("unexpected output: %v", l)
|
||||
return
|
||||
}
|
||||
done <- fmt.Errorf("unexpected output: %v", l)
|
||||
return
|
||||
}
|
||||
if l == "hello world1" {
|
||||
hello1 = true
|
||||
@@ -1330,7 +1338,7 @@ func TestConcurrentEvals3(t *testing.T) {
|
||||
}()
|
||||
|
||||
for _, v := range input {
|
||||
in := strings.NewReader(fmt.Sprintf("println(\"%s\")\n", v))
|
||||
in := strings.NewReader(fmt.Sprintf("println(%q)\n", v))
|
||||
if _, err := io.Copy(poutin, in); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1875,25 +1883,25 @@ func TestIssue1383(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
interp := interp.New(interp.Options{})
|
||||
err := interp.Use(stdlib.Symbols)
|
||||
i := interp.New(interp.Options{})
|
||||
err := i.Use(stdlib.Symbols)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = interp.Eval(`import "fmt"`)
|
||||
_, err = i.Eval(`import "fmt"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ast, err := parser.ParseFile(interp.FileSet(), "_.go", src, parser.DeclarationErrors)
|
||||
ast, err := parser.ParseFile(i.FileSet(), "_.go", src, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prog, err := interp.CompileAST(ast)
|
||||
prog, err := i.CompileAST(ast)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = interp.Execute(prog)
|
||||
_, err = i.Execute(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -15,6 +17,12 @@ import (
|
||||
"github.com/traefik/yaegi/stdlib/unsafe"
|
||||
)
|
||||
|
||||
// The following tests sometimes (not always) crash with go1.21 but not with go1.20 or go1.22.
|
||||
// The reason of failure is not obvious, maybe due to the runtime itself, and will be investigated separately.
|
||||
var testsToSkipGo121 = map[string]bool{"cli6.go": true, "cli7.go": true, "issue-1276.go": true, "issue-1330.go": true, "struct11.go": true}
|
||||
|
||||
var go121 = strings.HasPrefix(runtime.Version(), "go1.21")
|
||||
|
||||
func TestFile(t *testing.T) {
|
||||
filePath := "../_test/str.go"
|
||||
runCheck(t, filePath)
|
||||
@@ -26,10 +34,15 @@ func TestFile(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if filepath.Ext(file.Name()) != ".go" {
|
||||
continue
|
||||
}
|
||||
// Skip some tests which are problematic in go1.21 only.
|
||||
if go121 && testsToSkipGo121[file.Name()] {
|
||||
continue
|
||||
}
|
||||
file := file
|
||||
t.Run(file.Name(), func(t *testing.T) {
|
||||
runCheck(t, filepath.Join(baseDir, file.Name()))
|
||||
@@ -76,7 +89,9 @@ func runCheck(t *testing.T, p string) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res := strings.TrimSpace(stdout.String()); res != wanted {
|
||||
// Remove path in output, to have results independent of location.
|
||||
re := regexp.MustCompile(p + ":")
|
||||
if res := re.ReplaceAllString(strings.TrimSpace(stdout.String()), ""); res != wanted {
|
||||
t.Errorf("\ngot: %q,\nwant: %q", res, wanted)
|
||||
}
|
||||
}
|
||||
@@ -95,7 +110,7 @@ func wantedFromComment(p string) (res string, goPath string, err bool) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
goPath = filepath.Join(wd, "../_test", strings.TrimPrefix(parts[0], "GOPATH:"))
|
||||
goPath = filepath.Join(wd, "..", "_test", strings.TrimPrefix(parts[0], "GOPATH:"))
|
||||
}
|
||||
if strings.HasPrefix(text, "Output:\n") {
|
||||
return strings.TrimPrefix(text, "Output:\n"), goPath, false
|
||||
|
||||
@@ -2626,7 +2626,7 @@ func equal(n *node) {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
if c0.typ.cat == linkedT || c1.typ.cat == linkedT {
|
||||
switch {
|
||||
case isInterface:
|
||||
v0 := genValue(c0)
|
||||
@@ -4538,7 +4538,7 @@ func notEqual(n *node) {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
if c0.typ.cat == linkedT || c1.typ.cat == linkedT {
|
||||
switch {
|
||||
case isInterface:
|
||||
v0 := genValue(c0)
|
||||
|
||||
@@ -17,6 +17,11 @@ type Program struct {
|
||||
init []*node
|
||||
}
|
||||
|
||||
// PackageName returns name used in a package clause.
|
||||
func (p *Program) PackageName() string {
|
||||
return p.pkgName
|
||||
}
|
||||
|
||||
// FileSet is the fileset that must be used for parsing Go that will be passed
|
||||
// to interp.CompileAST().
|
||||
func (interp *Interpreter) FileSet() *token.FileSet {
|
||||
|
||||
565
interp/run.go
565
interp/run.go
@@ -171,8 +171,36 @@ func originalExecNode(n *node, exec bltn) *node {
|
||||
return originalNode
|
||||
}
|
||||
|
||||
// cloned from net/http/server.go , so we can enforce a similar behavior:
|
||||
// in the stdlib, this error is used as sentinel in panic triggered e.g. on
|
||||
// request cancellation, in order to catch it and suppress it in a following defer.
|
||||
// in yaegi, we use it to suppress a "panic" log message that happens in the
|
||||
// same circumstances.
|
||||
var errAbortHandler = errors.New("net/http: abort Handler")
|
||||
|
||||
// Functions set to run during execution of CFG.
|
||||
|
||||
func panicFunc(s *scope) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
def := s.def
|
||||
if def == nil {
|
||||
return s.pkgID
|
||||
}
|
||||
switch def.kind {
|
||||
case funcDecl:
|
||||
if c := def.child[1]; c.kind == identExpr {
|
||||
return s.pkgID + "." + c.ident
|
||||
}
|
||||
case funcLit:
|
||||
if def.anc != nil {
|
||||
return panicFunc(def.anc.scope) + ".func"
|
||||
}
|
||||
}
|
||||
return s.pkgID
|
||||
}
|
||||
|
||||
// runCfg executes a node AST by walking its CFG and running node builtin at each step.
|
||||
func runCfg(n *node, f *frame, funcNode, callNode *node) {
|
||||
var exec bltn
|
||||
@@ -187,7 +215,13 @@ func runCfg(n *node, f *frame, funcNode, callNode *node) {
|
||||
if oNode == nil {
|
||||
oNode = n
|
||||
}
|
||||
fmt.Fprintln(n.interp.stderr, oNode.cfgErrorf("panic"))
|
||||
errorer, ok := f.recovered.(error)
|
||||
// in this specific case, the stdlib would/will suppress the panic, so we
|
||||
// suppress the logging here accordingly, to get a similar and consistent
|
||||
// behavior.
|
||||
if !ok || errorer.Error() != errAbortHandler.Error() {
|
||||
fmt.Fprintln(n.interp.stderr, oNode.cfgErrorf("panic: %s(...)", panicFunc(oNode.scope)))
|
||||
}
|
||||
f.mutex.Unlock()
|
||||
panic(f.recovered)
|
||||
}
|
||||
@@ -277,7 +311,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
|
||||
typ := c1.typ // type to assert or convert to
|
||||
typID := typ.id()
|
||||
rtype := typ.rtype // type to assert
|
||||
rtype := typ.refType(nil) // type to assert
|
||||
next := getExec(n.tnext)
|
||||
|
||||
switch {
|
||||
@@ -401,7 +435,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
ok = v.IsValid()
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -409,7 +443,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
if !ok {
|
||||
if !withOk {
|
||||
method := firstMissingMethod(leftType, rtype)
|
||||
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", leftType.String(), rtype.String(), method))
|
||||
panic(n.cfgErrorf("interface conversion: %s is not %s: missing method %s", leftType.String(), rtype.String(), method))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -430,14 +464,18 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
concrete := val.Interface()
|
||||
ctyp := reflect.TypeOf(concrete)
|
||||
|
||||
if vv, ok := concrete.(valueInterface); ok {
|
||||
ctyp = vv.value.Type()
|
||||
concrete = vv.value.Interface()
|
||||
}
|
||||
ok = canAssertTypes(ctyp, rtype)
|
||||
if !ok {
|
||||
if !withOk {
|
||||
// TODO(mpl): think about whether this should ever happen.
|
||||
if ctyp == nil {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", ctyp.String(), rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is %s, not %s", ctyp.String(), rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -462,7 +500,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
}
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -475,7 +513,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
if !ok {
|
||||
if !withOk {
|
||||
method := firstMissingMethod(v.Type(), rtype)
|
||||
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), rtype.String(), method))
|
||||
panic(n.cfgErrorf("interface conversion: %s is not %s: missing method %s", v.Type().String(), rtype.String(), method))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -495,7 +533,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
if !ok || !v.value.IsValid() {
|
||||
ok = false
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -503,7 +541,7 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
ok = canAssertTypes(v.value.Type(), rtype)
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), rtype.String()))
|
||||
panic(n.cfgErrorf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -562,29 +600,11 @@ func convert(n *node) {
|
||||
return
|
||||
}
|
||||
|
||||
if isFuncSrc(n.child[0].typ) && isFuncSrc(c.typ) {
|
||||
value := genValue(c)
|
||||
n.exec = func(f *frame) bltn {
|
||||
n, ok := value(f).Interface().(*node)
|
||||
if !ok || !n.typ.convertibleTo(c.typ) {
|
||||
panic("cannot convert")
|
||||
}
|
||||
n1 := *n
|
||||
n1.typ = c.typ
|
||||
dest(f).Set(reflect.ValueOf(&n1))
|
||||
return next
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
doConvert := true
|
||||
var value func(*frame) reflect.Value
|
||||
switch {
|
||||
case isFuncSrc(c.typ):
|
||||
value = genFunctionWrapper(c)
|
||||
case isFuncSrc(n.child[0].typ) && c.typ.cat == valueT:
|
||||
doConvert = false
|
||||
value = genValueNode(c)
|
||||
default:
|
||||
value = genValue(c)
|
||||
}
|
||||
@@ -637,6 +657,15 @@ func assignFromCall(n *node) {
|
||||
continue
|
||||
}
|
||||
s := f.data[ncall.findex+i]
|
||||
c := n.child[i]
|
||||
if n.kind == defineXStmt && !c.redeclared {
|
||||
// Recreate destination value in case of define statement,
|
||||
// to preserve previous value possibly in use by a closure.
|
||||
data := getFrame(f, c.level).data
|
||||
data[c.findex] = reflect.New(data[c.findex].Type()).Elem()
|
||||
data[c.findex].Set(s)
|
||||
continue
|
||||
}
|
||||
v(f).Set(s)
|
||||
}
|
||||
return next
|
||||
@@ -655,8 +684,8 @@ func assign(n *node) {
|
||||
|
||||
for i := 0; i < n.nleft; i++ {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
if isFuncSrc(src.typ) && isField(dest) {
|
||||
svalue[i] = genFunctionWrapper(src)
|
||||
if isNamedFuncSrc(src.typ) {
|
||||
svalue[i] = genFuncValue(src)
|
||||
} else {
|
||||
svalue[i] = genDestValue(dest.typ, src)
|
||||
}
|
||||
@@ -710,8 +739,6 @@ func assign(n *node) {
|
||||
for i := range types {
|
||||
var t reflect.Type
|
||||
switch typ := n.child[sbase+i].typ; {
|
||||
case isFuncSrc(typ):
|
||||
t = reflect.TypeOf((*node)(nil))
|
||||
case isInterfaceSrc(typ):
|
||||
t = valueInterfaceType
|
||||
default:
|
||||
@@ -948,9 +975,6 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
var def *node
|
||||
var ok bool
|
||||
|
||||
if n.kind == basicLit {
|
||||
return func(f *frame) reflect.Value { return n.rval }
|
||||
}
|
||||
if def, ok = n.val.(*node); !ok {
|
||||
return genValueAsFunctionWrapper(n)
|
||||
}
|
||||
@@ -959,11 +983,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
var rcvr func(*frame) reflect.Value
|
||||
|
||||
if n.recv != nil {
|
||||
if n.recv.node.typ.cat != defRecvType(def).cat {
|
||||
rcvr = genValueRecvIndirect(n)
|
||||
} else {
|
||||
rcvr = genValueRecv(n)
|
||||
}
|
||||
rcvr = genValueRecv(n)
|
||||
}
|
||||
funcType := n.typ.TypeOf()
|
||||
|
||||
@@ -985,15 +1005,20 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
// Copy method receiver as first argument.
|
||||
src, dest := rcvr(f), d[numRet]
|
||||
sk, dk := src.Kind(), dest.Kind()
|
||||
for {
|
||||
vs, ok := src.Interface().(valueInterface)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
src = vs.value
|
||||
sk = src.Kind()
|
||||
}
|
||||
switch {
|
||||
case sk == reflect.Ptr && dk != reflect.Ptr:
|
||||
dest.Set(src.Elem())
|
||||
case sk != reflect.Ptr && dk == reflect.Ptr:
|
||||
dest.Set(src.Addr())
|
||||
default:
|
||||
if wrappedSrc, ok := src.Interface().(valueInterface); ok {
|
||||
src = wrappedSrc.value
|
||||
}
|
||||
dest.Set(src)
|
||||
}
|
||||
d = d[numRet+1:]
|
||||
@@ -1011,8 +1036,6 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
d[i].Set(arg)
|
||||
case isInterfaceSrc(typ):
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
case isFuncSrc(typ) && arg.Kind() == reflect.Func:
|
||||
d[i].Set(reflect.ValueOf(genFunctionNode(arg)))
|
||||
default:
|
||||
d[i].Set(arg)
|
||||
}
|
||||
@@ -1021,21 +1044,11 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
// Interpreter code execution.
|
||||
runCfg(start, fr, def, n)
|
||||
|
||||
result := fr.data[:numRet]
|
||||
for i, r := range result {
|
||||
if v, ok := r.Interface().(*node); ok {
|
||||
result[i] = genFunctionWrapper(v)(f)
|
||||
}
|
||||
}
|
||||
return result
|
||||
return fr.data[:numRet]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func genFunctionNode(v reflect.Value) *node {
|
||||
return &node{kind: funcType, action: aNop, rval: v, typ: valueTOf(v.Type())}
|
||||
}
|
||||
|
||||
func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
if typ == nil || typ.Kind() != reflect.Interface || typ.NumMethod() == 0 || n.typ.cat == valueT {
|
||||
@@ -1163,28 +1176,10 @@ func checkFieldIndex(typ reflect.Type, index []int) bool {
|
||||
|
||||
func call(n *node) {
|
||||
goroutine := n.anc.kind == goStmt
|
||||
var method bool
|
||||
c0 := n.child[0]
|
||||
value := genValue(c0)
|
||||
var values []func(*frame) reflect.Value
|
||||
|
||||
recvIndexLater := false
|
||||
switch {
|
||||
case c0.recv != nil:
|
||||
// Compute method receiver value.
|
||||
values = append(values, genValueRecv(c0))
|
||||
method = true
|
||||
case len(c0.child) > 0 && c0.child[0].typ != nil && isInterfaceSrc(c0.child[0].typ):
|
||||
recvIndexLater = true
|
||||
values = append(values, genValueBinRecv(c0, &receiver{node: c0.child[0]}))
|
||||
value = genValueBinMethodOnInterface(n, value)
|
||||
method = true
|
||||
case c0.action == aMethod:
|
||||
// Add a place holder for interface method receiver.
|
||||
values = append(values, nil)
|
||||
method = true
|
||||
}
|
||||
|
||||
numRet := len(c0.typ.ret)
|
||||
variadic := variadicPos(n)
|
||||
child := n.child[1:]
|
||||
@@ -1240,7 +1235,7 @@ func call(n *node) {
|
||||
case isInterfaceBin(arg):
|
||||
values = append(values, genInterfaceWrapper(c, arg.rtype))
|
||||
case isFuncSrc(arg):
|
||||
values = append(values, genValueNode(c))
|
||||
values = append(values, genFuncValue(c))
|
||||
default:
|
||||
values = append(values, genValue(c))
|
||||
}
|
||||
@@ -1284,10 +1279,6 @@ func call(n *node) {
|
||||
if n.anc.kind == deferStmt {
|
||||
// Store function call in frame for deferred execution.
|
||||
value = genFunctionWrapper(c0)
|
||||
if method {
|
||||
// The receiver is already passed in the function wrapper, skip it.
|
||||
values = values[1:]
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := make([]reflect.Value, len(values)+1)
|
||||
val[0] = value(f)
|
||||
@@ -1305,21 +1296,42 @@ func call(n *node) {
|
||||
var ok bool
|
||||
|
||||
bf := value(f)
|
||||
|
||||
if def, ok = bf.Interface().(*node); ok {
|
||||
bf = def.rval
|
||||
}
|
||||
|
||||
// Call bin func if defined
|
||||
if bf.IsValid() {
|
||||
var callf func([]reflect.Value) []reflect.Value
|
||||
|
||||
// Lambda definitions are necessary here. Due to reflect internals,
|
||||
// having `callf = bf.Call` or `callf = bf.CallSlice` does not work.
|
||||
//nolint:gocritic
|
||||
if hasVariadicArgs {
|
||||
callf = func(in []reflect.Value) []reflect.Value { return bf.CallSlice(in) }
|
||||
} else {
|
||||
callf = func(in []reflect.Value) []reflect.Value { return bf.Call(in) }
|
||||
}
|
||||
|
||||
if goroutine {
|
||||
// Goroutine's arguments should be copied.
|
||||
in := make([]reflect.Value, len(values))
|
||||
for i, v := range values {
|
||||
value := v(f)
|
||||
in[i] = reflect.New(value.Type()).Elem()
|
||||
in[i].Set(value)
|
||||
}
|
||||
|
||||
go callf(in)
|
||||
return tnext
|
||||
}
|
||||
|
||||
in := make([]reflect.Value, len(values))
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
}
|
||||
if goroutine {
|
||||
go bf.Call(in)
|
||||
return tnext
|
||||
}
|
||||
out := bf.Call(in)
|
||||
out := callf(in)
|
||||
for i, v := range rvalues {
|
||||
if v != nil {
|
||||
v(f).Set(out[i])
|
||||
@@ -1354,55 +1366,15 @@ func call(n *node) {
|
||||
}
|
||||
|
||||
// Init variadic argument vector
|
||||
varIndex := variadic
|
||||
if variadic >= 0 {
|
||||
if method {
|
||||
vararg = nf.data[numRet+variadic+1]
|
||||
varIndex++
|
||||
} else {
|
||||
vararg = nf.data[numRet+variadic]
|
||||
}
|
||||
vararg = nf.data[numRet+variadic]
|
||||
}
|
||||
|
||||
// Copy input parameters from caller
|
||||
if dest := nf.data[numRet:]; len(dest) > 0 {
|
||||
for i, v := range values {
|
||||
switch {
|
||||
case method && i == 0:
|
||||
// compute receiver
|
||||
var src reflect.Value
|
||||
if v == nil {
|
||||
src = def.recv.val
|
||||
} else {
|
||||
src = v(f)
|
||||
for src.IsValid() {
|
||||
// traverse interface indirections to find out concrete type
|
||||
vi, ok := src.Interface().(valueInterface)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
src = vi.value
|
||||
}
|
||||
}
|
||||
if recvIndexLater && def.recv != nil && len(def.recv.index) > 0 {
|
||||
if src.Kind() == reflect.Ptr {
|
||||
src = src.Elem().FieldByIndex(def.recv.index)
|
||||
} else {
|
||||
src = src.FieldByIndex(def.recv.index)
|
||||
}
|
||||
}
|
||||
// Accommodate to receiver type
|
||||
d := dest[0]
|
||||
if ks, kd := src.Kind(), d.Kind(); ks != kd {
|
||||
if kd == reflect.Ptr {
|
||||
d.Set(src.Addr())
|
||||
} else {
|
||||
d.Set(src.Elem())
|
||||
}
|
||||
} else {
|
||||
d.Set(src)
|
||||
}
|
||||
case variadic >= 0 && i >= varIndex:
|
||||
case variadic >= 0 && i >= variadic:
|
||||
if v(f).Type() == vararg.Type() {
|
||||
vararg.Set(v(f))
|
||||
} else {
|
||||
@@ -1553,12 +1525,12 @@ func callBin(n *node) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case isFuncSrc(c.typ):
|
||||
values = append(values, genFunctionWrapper(c))
|
||||
case isEmptyInterface(c.typ):
|
||||
values = append(values, genValue(c))
|
||||
case isInterfaceSrc(c.typ):
|
||||
values = append(values, genValueInterfaceValue(c))
|
||||
case isFuncSrc(c.typ):
|
||||
values = append(values, genFunctionWrapper(c))
|
||||
case c.typ.cat == arrayT || c.typ.cat == variadicT:
|
||||
if isEmptyInterface(c.typ.val) {
|
||||
values = append(values, genValueArray(c))
|
||||
@@ -1645,9 +1617,20 @@ func callBin(n *node) {
|
||||
}
|
||||
out := callFn(value(f), in)
|
||||
for i, v := range rvalues {
|
||||
if v != nil {
|
||||
v(f).Set(out[i])
|
||||
if v == nil {
|
||||
continue // Skip assign "_".
|
||||
}
|
||||
c := n.anc.child[i]
|
||||
if n.anc.kind == defineXStmt && !c.redeclared {
|
||||
// In case of a define statement, the destination value in the frame
|
||||
// must be recreated. This is necessary to preserve the previous value
|
||||
// which may be still used in a separate closure.
|
||||
data := getFrame(f, c.level).data
|
||||
data[c.findex] = reflect.New(data[c.findex].Type()).Elem()
|
||||
data[c.findex].Set(out[i])
|
||||
continue
|
||||
}
|
||||
v(f).Set(out[i])
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
@@ -1906,8 +1889,10 @@ func getIndexMap2(n *node) {
|
||||
|
||||
const fork = true // Duplicate frame in frame.clone().
|
||||
|
||||
// getFunc compiles a closure function generator for anonymous functions.
|
||||
func getFunc(n *node) {
|
||||
dest := genValue(n)
|
||||
i := n.findex
|
||||
l := n.level
|
||||
next := getExec(n.tnext)
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
@@ -1915,7 +1900,42 @@ func getFunc(n *node) {
|
||||
nod := *n
|
||||
nod.val = &nod
|
||||
nod.frame = fr
|
||||
dest(f).Set(reflect.ValueOf(&nod))
|
||||
def := &nod
|
||||
numRet := len(def.typ.ret)
|
||||
|
||||
fct := reflect.MakeFunc(nod.typ.TypeOf(), func(in []reflect.Value) []reflect.Value {
|
||||
// Allocate and init local frame. All values to be settable and addressable.
|
||||
fr2 := newFrame(fr, len(def.types), fr.runid())
|
||||
d := fr2.data
|
||||
for i, t := range def.types {
|
||||
d[i] = reflect.New(t).Elem()
|
||||
}
|
||||
d = d[numRet:]
|
||||
|
||||
// Copy function input arguments in local frame.
|
||||
for i, arg := range in {
|
||||
if i >= len(d) {
|
||||
// In case of unused arg, there may be not even a frame entry allocated, just skip.
|
||||
break
|
||||
}
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case isEmptyInterface(typ) || typ.TypeOf() == valueInterfaceType:
|
||||
d[i].Set(arg)
|
||||
case isInterfaceSrc(typ):
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
default:
|
||||
d[i].Set(arg)
|
||||
}
|
||||
}
|
||||
|
||||
// Interpreter code execution.
|
||||
runCfg(def.child[3].start, fr2, def, n)
|
||||
|
||||
return fr2.data[:numRet]
|
||||
})
|
||||
|
||||
getFrame(f, l).data[i] = fct
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1931,7 +1951,7 @@ func getMethod(n *node) {
|
||||
nod.val = &nod
|
||||
nod.recv = n.recv
|
||||
nod.frame = fr
|
||||
getFrame(f, l).data[i] = reflect.ValueOf(&nod)
|
||||
getFrame(f, l).data[i] = genFuncValue(&nod)(f)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1944,7 +1964,30 @@ func getMethodByName(n *node) {
|
||||
l := n.level
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := value0(f).Interface().(valueInterface)
|
||||
// The interface object must be directly accessible, or embedded in a struct (exported anonymous field).
|
||||
val0 := value0(f)
|
||||
val, ok := value0(f).Interface().(valueInterface)
|
||||
if !ok {
|
||||
// Search the first embedded valueInterface.
|
||||
for val0.Kind() == reflect.Ptr {
|
||||
val0 = val0.Elem()
|
||||
}
|
||||
for i := 0; i < val0.NumField(); i++ {
|
||||
fld := val0.Type().Field(i)
|
||||
if !fld.Anonymous || !fld.IsExported() {
|
||||
continue
|
||||
}
|
||||
if val, ok = val0.Field(i).Interface().(valueInterface); ok {
|
||||
break
|
||||
// TODO: should we keep track of all the vals that are indeed valueInterface,
|
||||
// so that later on we can call MethodByName on all of them until one matches?
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
panic(n.cfgErrorf("invalid interface value %v", val0))
|
||||
}
|
||||
}
|
||||
// Traverse nested interface values to get the concrete value.
|
||||
for {
|
||||
v, ok := val.value.Interface().(valueInterface)
|
||||
if !ok {
|
||||
@@ -1960,7 +2003,7 @@ func getMethodByName(n *node) {
|
||||
|
||||
typ := val.node.typ
|
||||
if typ.node == nil && typ.cat == valueT {
|
||||
// happens with a var of empty interface type, that has value of concrete type
|
||||
// It happens with a var of empty interface type, that has value of concrete type
|
||||
// from runtime, being asserted to "user-defined" interface.
|
||||
if _, ok := typ.rtype.MethodByName(name); !ok {
|
||||
panic(n.cfgErrorf("method not found: %s", name))
|
||||
@@ -1968,27 +2011,12 @@ func getMethodByName(n *node) {
|
||||
return next
|
||||
}
|
||||
|
||||
m, li := typ.lookupMethod(name)
|
||||
|
||||
// Try harder to find a matching embedded valueInterface.
|
||||
// TODO (marc): make sure it works for arbitrary depth and breadth.
|
||||
if m == nil && isStruct(val.node.typ) {
|
||||
v := val.value
|
||||
for v.Type().Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
nf := v.NumField()
|
||||
for i := 0; i < nf; i++ {
|
||||
var ok bool
|
||||
if val, ok = v.Field(i).Interface().(valueInterface); !ok {
|
||||
continue
|
||||
}
|
||||
if m, li = val.node.typ.lookupMethod(name); m != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Finally search method recursively in embedded valueInterfaces.
|
||||
r, m, li := lookupMethodValue(val, name)
|
||||
if r.IsValid() {
|
||||
getFrame(f, l).data[i] = r
|
||||
return next
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
panic(n.cfgErrorf("method not found: %s", name))
|
||||
}
|
||||
@@ -1998,11 +2026,42 @@ func getMethodByName(n *node) {
|
||||
nod.val = &nod
|
||||
nod.recv = &receiver{nil, val.value, li}
|
||||
nod.frame = fr
|
||||
getFrame(f, l).data[i] = reflect.ValueOf(&nod)
|
||||
getFrame(f, l).data[i] = genFuncValue(&nod)(f)
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
// lookupMethodValue recursively looks within val for the method with the given
|
||||
// name. If a runtime value is found, it is returned in r, otherwise it is returned
|
||||
// in m, with li as the list of recursive field indexes.
|
||||
func lookupMethodValue(val valueInterface, name string) (r reflect.Value, m *node, li []int) {
|
||||
if r = val.value.MethodByName(name); r.IsValid() {
|
||||
return
|
||||
}
|
||||
if m, li = val.node.typ.lookupMethod(name); m != nil {
|
||||
return
|
||||
}
|
||||
if !isStruct(val.node.typ) {
|
||||
return
|
||||
}
|
||||
v := val.value
|
||||
for v.Type().Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
nf := v.NumField()
|
||||
for i := 0; i < nf; i++ {
|
||||
vi, ok := v.Field(i).Interface().(valueInterface)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if r, m, li = lookupMethodValue(vi, name); m != nil {
|
||||
li = append([]int{i}, li...)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getIndexSeq(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
index := n.val.([]int)
|
||||
@@ -2380,27 +2439,12 @@ func _return(n *node) {
|
||||
switch t := def.typ.ret[i]; t.cat {
|
||||
case errorT:
|
||||
values[i] = genInterfaceWrapper(c, t.TypeOf())
|
||||
case aliasT:
|
||||
if isInterfaceSrc(t) {
|
||||
values[i] = genValueInterface(c)
|
||||
} else {
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
case funcT:
|
||||
values[i] = genValue(c)
|
||||
case interfaceT:
|
||||
if len(t.field) == 0 {
|
||||
// empty interface case.
|
||||
// we can't let genValueInterface deal with it, because we call on c,
|
||||
// not on n, which means that the interfaceT knowledge is lost.
|
||||
values[i] = genValue(c)
|
||||
break
|
||||
}
|
||||
values[i] = genValueInterface(c)
|
||||
case valueT:
|
||||
switch t.rtype.Kind() {
|
||||
case reflect.Interface:
|
||||
values[i] = genInterfaceWrapper(c, t.rtype)
|
||||
values[i] = genInterfaceWrapper(c, t.TypeOf())
|
||||
continue
|
||||
case reflect.Func:
|
||||
values[i] = genFunctionWrapper(c)
|
||||
@@ -2408,9 +2452,19 @@ func _return(n *node) {
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
if c.typ.untyped {
|
||||
values[i] = genValueAs(c, def.typ.ret[i].TypeOf())
|
||||
} else {
|
||||
switch {
|
||||
case isInterfaceSrc(t):
|
||||
if len(t.field) == 0 {
|
||||
// empty interface case.
|
||||
// we can't let genValueInterface deal with it, because we call on c,
|
||||
// not on n, which means that the interfaceT knowledge is lost.
|
||||
values[i] = genValue(c)
|
||||
break
|
||||
}
|
||||
values[i] = genValueInterface(c)
|
||||
case c.typ.untyped:
|
||||
values[i] = genValueAs(c, t.TypeOf())
|
||||
default:
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
@@ -2605,7 +2659,7 @@ func doCompositeBinStruct(n *node, hasType bool) {
|
||||
next := getExec(n.tnext)
|
||||
value := valueGenerator(n, n.findex)
|
||||
typ := n.typ.rtype
|
||||
if n.typ.cat == ptrT || n.typ.cat == aliasT {
|
||||
if n.typ.cat == ptrT || n.typ.cat == linkedT {
|
||||
typ = n.typ.val.rtype
|
||||
}
|
||||
child := n.child
|
||||
@@ -2672,7 +2726,7 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
typ := n.typ
|
||||
if typ.cat == ptrT || typ.cat == aliasT {
|
||||
if typ.cat == ptrT || typ.cat == linkedT {
|
||||
typ = typ.val
|
||||
}
|
||||
child := n.child
|
||||
@@ -2698,7 +2752,7 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
switch {
|
||||
case val.typ.cat == nilT:
|
||||
values[fieldIndex] = func(*frame) reflect.Value { return reflect.New(rft).Elem() }
|
||||
case isFuncSrc(val.typ):
|
||||
case isNamedFuncSrc(val.typ):
|
||||
values[fieldIndex] = genValueAsFunctionWrapper(val)
|
||||
case isInterfaceSrc(ft) && (!isEmptyInterface(ft) || len(val.typ.method) > 0):
|
||||
values[fieldIndex] = genValueInterface(val)
|
||||
@@ -2996,15 +3050,17 @@ func _case(n *node) {
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
vi := v.Interface().(valueInterface)
|
||||
if vi.node == nil {
|
||||
if typ.cat == nilT {
|
||||
return tnext
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
if vi.node != nil {
|
||||
if vi.node.typ.id() == typ.id() {
|
||||
destValue(f).Set(vi.value)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if vi.node.typ.id() == typ.id() {
|
||||
destValue(f).Set(vi.value)
|
||||
if v.Type() == typ.TypeOf() {
|
||||
destValue(f).Set(v)
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -3039,13 +3095,23 @@ func _case(n *node) {
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if v := val.Interface().(valueInterface).node; v != nil {
|
||||
for _, typ := range types {
|
||||
if v.typ.id() == typ.id() {
|
||||
destValue(f).Set(val)
|
||||
return tnext
|
||||
if vi, ok := val.Interface().(valueInterface); ok {
|
||||
if v := vi.node; v != nil {
|
||||
for _, typ := range types {
|
||||
if v.typ.id() == typ.id() {
|
||||
destValue(f).Set(val)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
vt := val.Type()
|
||||
for _, typ := range types {
|
||||
if vt == typ.TypeOf() {
|
||||
destValue(f).Set(val)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
@@ -3611,7 +3677,7 @@ func convertConstantValue(n *node) {
|
||||
case constant.Int:
|
||||
i, x := constant.Int64Val(c)
|
||||
if !x {
|
||||
panic(fmt.Sprintf("constant %s overflows int64", c.ExactString()))
|
||||
panic(n.cfgErrorf("constant %s overflows int64", c.ExactString()))
|
||||
}
|
||||
v = reflect.ValueOf(int(i))
|
||||
case constant.Float:
|
||||
@@ -3838,35 +3904,44 @@ func slice0(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func isNil(n *node) {
|
||||
var value func(*frame) reflect.Value
|
||||
c0 := n.child[0]
|
||||
if isFuncSrc(c0.typ) {
|
||||
value = genValueAsFunctionWrapper(c0)
|
||||
} else {
|
||||
value = genValue(c0)
|
||||
}
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
tnext := getExec(n.tnext)
|
||||
dest := genValue(n)
|
||||
|
||||
if n.fnext == nil {
|
||||
if !isInterfaceSrc(c0.typ) {
|
||||
if isInterface {
|
||||
func isNilChild(child int) func(n *node) {
|
||||
return func(n *node) {
|
||||
var value func(*frame) reflect.Value
|
||||
child := n.child[child]
|
||||
value = genValue(child)
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
tnext := getExec(n.tnext)
|
||||
dest := genValue(n)
|
||||
if n.fnext == nil {
|
||||
if !isInterfaceSrc(child.typ) {
|
||||
if isInterface {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(value(f).IsNil()).Convert(typ))
|
||||
return tnext
|
||||
}
|
||||
return
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(value(f).IsNil()).Convert(typ))
|
||||
dest(f).SetBool(value(f).IsNil())
|
||||
return tnext
|
||||
}
|
||||
return
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetBool(value(f).IsNil())
|
||||
return tnext
|
||||
if isInterface {
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f)
|
||||
var r bool
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
r = (vi == valueInterface{} || vi.node.kind == basicLit && vi.node.typ.cat == nilT)
|
||||
} else {
|
||||
r = v.IsNil()
|
||||
}
|
||||
dest(f).Set(reflect.ValueOf(r).Convert(typ))
|
||||
return tnext
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
if isInterface {
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f)
|
||||
var r bool
|
||||
@@ -3875,66 +3950,50 @@ func isNil(n *node) {
|
||||
} else {
|
||||
r = v.IsNil()
|
||||
}
|
||||
dest(f).Set(reflect.ValueOf(r).Convert(typ))
|
||||
dest(f).SetBool(r)
|
||||
return tnext
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fnext := getExec(n.fnext)
|
||||
|
||||
if !isInterfaceSrc(child.typ) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f)
|
||||
var r bool
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
r = (vi == valueInterface{} || vi.node.kind == basicLit && vi.node.typ.cat == nilT)
|
||||
} else {
|
||||
r = v.IsNil()
|
||||
if (vi == valueInterface{} || vi.node.kind == basicLit && vi.node.typ.cat == nilT) {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
dest(f).SetBool(r)
|
||||
return tnext
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fnext := getExec(n.fnext)
|
||||
|
||||
if !isInterfaceSrc(c0.typ) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
if v.IsNil() {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f)
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
if (vi == valueInterface{} || vi.node.kind == basicLit && vi.node.typ.cat == nilT) {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
if v.IsNil() {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
}
|
||||
|
||||
func isNotNil(n *node) {
|
||||
var value func(*frame) reflect.Value
|
||||
c0 := n.child[0]
|
||||
if isFuncSrc(c0.typ) {
|
||||
value = genValueAsFunctionWrapper(c0)
|
||||
} else {
|
||||
value = genValue(c0)
|
||||
}
|
||||
value = genValue(c0)
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
tnext := getExec(n.tnext)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
@@ -133,6 +134,7 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
interp.mutex.Lock()
|
||||
gs := interp.scopes[importPath]
|
||||
if gs == nil {
|
||||
interp.mutex.Unlock()
|
||||
// A nil scope means that no even an empty package is created from source.
|
||||
return "", fmt.Errorf("no Go files in %s", dir)
|
||||
}
|
||||
@@ -207,7 +209,7 @@ func (interp *Interpreter) pkgDir(goPath string, root, importPath string) (strin
|
||||
return dir, root, nil // found!
|
||||
}
|
||||
|
||||
if len(root) == 0 {
|
||||
if root == "" {
|
||||
if interp.context.GOPATH == "" {
|
||||
return "", "", fmt.Errorf("unable to find source related to: %q. Either the GOPATH environment variable, or the Interpreter.Options.GoPath needs to be set", importPath)
|
||||
}
|
||||
@@ -244,9 +246,13 @@ func previousRoot(filesystem fs.FS, rootPath, root string) (string, error) {
|
||||
vendored = strings.TrimPrefix(strings.TrimPrefix(parent, prefix), string(filepath.Separator))
|
||||
break
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return "", err
|
||||
}
|
||||
// stop when we reach GOPATH/src
|
||||
if parent == prefix {
|
||||
break
|
||||
}
|
||||
|
||||
// stop when we reach GOPATH/src/blah
|
||||
parent = filepath.Dir(parent)
|
||||
@@ -258,7 +264,8 @@ func previousRoot(filesystem fs.FS, rootPath, root string) (string, error) {
|
||||
// we are dealing with relative paths).
|
||||
// TODO(mpl): It should probably be a critical error actually,
|
||||
// as we shouldn't have gone that high up in the tree.
|
||||
if parent == string(filepath.Separator) || parent == "." {
|
||||
// TODO(dennwc): This partially fails on Windows, since it cannot recognize drive letters as "root".
|
||||
if parent == string(filepath.Separator) || parent == "." || parent == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,13 +49,7 @@ func Test_effectivePkg(t *testing.T) {
|
||||
|
||||
func Test_pkgDir(t *testing.T) {
|
||||
// create GOPATH
|
||||
goPath, err := os.MkdirTemp("", "pkdir")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(goPath)
|
||||
}()
|
||||
goPath := t.TempDir()
|
||||
|
||||
// Create project
|
||||
project := filepath.Join(goPath, "src", "guthib.com", "foo", "root")
|
||||
|
||||
410
interp/type.go
410
interp/type.go
@@ -17,7 +17,6 @@ type tcat uint
|
||||
// Types for go language.
|
||||
const (
|
||||
nilT tcat = iota
|
||||
aliasT
|
||||
arrayT
|
||||
binT
|
||||
binPkgT
|
||||
@@ -41,6 +40,7 @@ const (
|
||||
int16T
|
||||
int32T
|
||||
int64T
|
||||
linkedT
|
||||
mapT
|
||||
ptrT
|
||||
sliceT
|
||||
@@ -60,7 +60,6 @@ const (
|
||||
|
||||
var cats = [...]string{
|
||||
nilT: "nilT",
|
||||
aliasT: "aliasT",
|
||||
arrayT: "arrayT",
|
||||
binT: "binT",
|
||||
binPkgT: "binPkgT",
|
||||
@@ -75,14 +74,15 @@ var cats = [...]string{
|
||||
float32T: "float32",
|
||||
float64T: "float64T",
|
||||
funcT: "funcT",
|
||||
genericT: "genericT",
|
||||
interfaceT: "interfaceT",
|
||||
intT: "intT",
|
||||
int8T: "int8T",
|
||||
int16T: "int16T",
|
||||
int32T: "int32T",
|
||||
int64T: "int64T",
|
||||
linkedT: "linkedT",
|
||||
mapT: "mapT",
|
||||
genericT: "genericT",
|
||||
ptrT: "ptrT",
|
||||
sliceT: "sliceT",
|
||||
srcPkgT: "srcPkgT",
|
||||
@@ -118,7 +118,7 @@ type itype struct {
|
||||
cat tcat // Type category
|
||||
field []structField // Array of struct fields if structT or interfaceT
|
||||
key *itype // Type of key element if MapT or nil
|
||||
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT, sliceT, variadicT or genericT
|
||||
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, linkedT, arrayT, sliceT, variadicT or genericT
|
||||
recv *itype // Receiver type for funcT or nil
|
||||
arg []*itype // Argument types if funcT or nil
|
||||
ret []*itype // Return types if funcT or nil
|
||||
@@ -126,6 +126,7 @@ type itype struct {
|
||||
method []*node // Associated methods or nil
|
||||
constraint []*itype // For interfaceT: list of types part of interface set
|
||||
ulconstraint []*itype // For interfaceT: list of underlying types part of interface set
|
||||
instance []*itype // For genericT: list of instantiated types
|
||||
name string // name of type within its package for a defined type
|
||||
path string // for a defined type, the package import path
|
||||
length int // length of array if ArrayT
|
||||
@@ -248,7 +249,7 @@ func namedOf(val *itype, path, name string, opts ...itypeOption) *itype {
|
||||
if path != "" {
|
||||
str = path + "." + name
|
||||
}
|
||||
t := &itype{cat: aliasT, val: val, path: path, name: name, str: str}
|
||||
t := &itype{cat: linkedT, val: val, path: path, name: name, str: str}
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
@@ -373,8 +374,8 @@ func structOf(t *itype, fields []structField, opts ...itypeOption) *itype {
|
||||
}
|
||||
|
||||
// genericOf returns a generic type.
|
||||
func genericOf(val *itype, name string, opts ...itypeOption) *itype {
|
||||
t := &itype{cat: genericT, name: name, str: name, val: val}
|
||||
func genericOf(val *itype, name, path string, opts ...itypeOption) *itype {
|
||||
t := &itype{cat: genericT, name: name, path: path, str: name, val: val}
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
@@ -446,12 +447,19 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
)
|
||||
switch v := c0.rval; {
|
||||
case v.IsValid():
|
||||
// Size if defined by a constant litteral value.
|
||||
// Size if defined by a constant literal value.
|
||||
if isConstantValue(v.Type()) {
|
||||
c := v.Interface().(constant.Value)
|
||||
length = constToInt(c)
|
||||
} else {
|
||||
length = int(v.Int())
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
length = int(v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
length = int(v.Uint())
|
||||
default:
|
||||
return nil, c0.cfgErrorf("non integer constant %v", v)
|
||||
}
|
||||
}
|
||||
case c0.kind == ellipsisExpr:
|
||||
// [...]T expression, get size from the length of composite array.
|
||||
@@ -476,15 +484,25 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
length = int(vInt(sym.rval))
|
||||
default:
|
||||
// Size is defined by a numeric constant expression.
|
||||
if _, err = interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
var ok bool
|
||||
if _, err := interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
if strings.Contains(err.Error(), " undefined: ") {
|
||||
incomplete = true
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
v, ok := c0.rval.Interface().(constant.Value)
|
||||
if !ok {
|
||||
incomplete = true
|
||||
break
|
||||
if !c0.rval.IsValid() {
|
||||
return nil, c0.cfgErrorf("undefined array size")
|
||||
}
|
||||
if length, ok = c0.rval.Interface().(int); !ok {
|
||||
v, ok := c0.rval.Interface().(constant.Value)
|
||||
if !ok {
|
||||
incomplete = true
|
||||
break
|
||||
}
|
||||
length = constToInt(v)
|
||||
}
|
||||
length = constToInt(v)
|
||||
}
|
||||
val, err := nodeType2(interp, sc, n.child[1], seen)
|
||||
if err != nil {
|
||||
@@ -587,7 +605,10 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
}
|
||||
|
||||
if isInterfaceSrc(dt) {
|
||||
dt.val = t
|
||||
// Set a new interface type preserving the concrete type (.val field).
|
||||
t2 := *dt
|
||||
t2.val = t
|
||||
dt = &t2
|
||||
}
|
||||
t = dt
|
||||
|
||||
@@ -768,11 +789,16 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
}
|
||||
}
|
||||
if sym.kind == varTypeSym {
|
||||
t = genericOf(sym.typ, n.ident, withNode(n), withScope(sc))
|
||||
t = genericOf(sym.typ, n.ident, sc.pkgName, withNode(n), withScope(sc))
|
||||
} else {
|
||||
t = sym.typ
|
||||
}
|
||||
if t.incomplete && t.cat == aliasT && t.val != nil && t.val.cat != nilT {
|
||||
if t == nil {
|
||||
if t, err = nodeType2(interp, sc, sym.node, seen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if t.incomplete && t.cat == linkedT && t.val != nil && t.val.cat != nilT {
|
||||
t.incomplete = false
|
||||
}
|
||||
if t.incomplete && t.node != n {
|
||||
@@ -793,7 +819,11 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
return nil, err
|
||||
}
|
||||
if lt.incomplete {
|
||||
t.incomplete = true
|
||||
if t == nil {
|
||||
t = lt
|
||||
} else {
|
||||
t.incomplete = true
|
||||
}
|
||||
break
|
||||
}
|
||||
switch lt.cat {
|
||||
@@ -814,46 +844,59 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
break
|
||||
}
|
||||
// A generic type is being instantiated. Generate it.
|
||||
g, err := genAST(sc, lt.node.anc, []*node{t1.node})
|
||||
t, err = genType(interp, sc, name, lt, []*itype{t1}, seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, err = nodeType2(interp, sc, g.lastChild(), seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sc.sym[name] = &symbol{index: -1, kind: typeSym, typ: t, node: g}
|
||||
|
||||
// Instantiate type methods (if any).
|
||||
var pt *itype
|
||||
if len(lt.method) > 0 {
|
||||
pt = ptrOf(t, withNode(g), withScope(sc))
|
||||
}
|
||||
for _, nod := range lt.method {
|
||||
gm, err := genAST(sc, nod, []*node{t1.node})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gm.typ, err = nodeType(interp, sc, gm.child[2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.addMethod(gm)
|
||||
if rtn := gm.child[0].child[0].lastChild(); rtn.kind == starExpr {
|
||||
// The receiver is a pointer on a generic type.
|
||||
pt.addMethod(gm)
|
||||
rtn.typ = pt
|
||||
}
|
||||
// Compile method CFG.
|
||||
if _, err = interp.cfg(gm, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Generate closures for function body.
|
||||
if err = genRun(gm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case indexListExpr:
|
||||
// Similar to above indexExpr for generic types, but handle multiple type parameters.
|
||||
var lt *itype
|
||||
if lt, err = nodeType2(interp, sc, n.child[0], seen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lt.incomplete {
|
||||
if t == nil {
|
||||
t = lt
|
||||
} else {
|
||||
t.incomplete = true
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Index list expressions can be used only in context of generic types.
|
||||
if lt.cat != genericT {
|
||||
err = n.cfgErrorf("not a generic type: %s", lt.id())
|
||||
return nil, err
|
||||
}
|
||||
name := lt.id() + "["
|
||||
out := false
|
||||
types := []*itype{}
|
||||
for _, c := range n.child[1:] {
|
||||
t1, err := nodeType2(interp, sc, c, seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t1.cat == genericT || t1.incomplete {
|
||||
t = lt
|
||||
out = true
|
||||
break
|
||||
}
|
||||
types = append(types, t1)
|
||||
name += t1.id() + ","
|
||||
}
|
||||
if out {
|
||||
break
|
||||
}
|
||||
name = strings.TrimSuffix(name, ",") + "]"
|
||||
if sym, _, found := sc.lookup(name); found {
|
||||
t = sym.typ
|
||||
break
|
||||
}
|
||||
// A generic type is being instantiated. Generate it.
|
||||
t, err = genType(interp, sc, name, lt, types, seen)
|
||||
|
||||
case interfaceType:
|
||||
if sname := typeName(n); sname != "" {
|
||||
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
|
||||
@@ -946,16 +989,18 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
rtype = rtype.Elem()
|
||||
}
|
||||
t = valueTOf(rtype, withNode(n), withScope(sc))
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector %s.%s", lt.path, name)
|
||||
break
|
||||
}
|
||||
// Continue search in source package, as it may exist if package contains generics.
|
||||
fallthrough
|
||||
case srcPkgT:
|
||||
pkg := interp.srcPkg[lt.path]
|
||||
if s, ok := pkg[name]; ok {
|
||||
t = s.typ
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector %s.%s", lt.path, name)
|
||||
if pkg, ok := interp.srcPkg[lt.path]; ok {
|
||||
if s, ok := pkg[name]; ok {
|
||||
t = s.typ
|
||||
break
|
||||
}
|
||||
}
|
||||
err = n.cfgErrorf("undefined selector %s.%s", lt.path, name)
|
||||
default:
|
||||
if m, _ := lt.lookupMethod(name); m != nil {
|
||||
t, err = nodeType2(interp, sc, m.child[2], seen)
|
||||
@@ -998,7 +1043,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
sname := structName(n)
|
||||
if sname != "" {
|
||||
sym, _, found = sc.lookup(sname)
|
||||
if found && sym.kind == typeSym {
|
||||
if found && sym.kind == typeSym && sym.typ != nil {
|
||||
t = structOf(sym.typ, sym.typ.field, withNode(n), withScope(sc))
|
||||
} else {
|
||||
t = structOf(nil, nil, withNode(n), withScope(sc))
|
||||
@@ -1044,9 +1089,15 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
t = structOf(t, fields, withNode(n), withScope(sc))
|
||||
t.incomplete = incomplete
|
||||
if sname != "" {
|
||||
if sc.sym[sname] == nil {
|
||||
sc.sym[sname] = &symbol{index: -1, kind: typeSym, node: n}
|
||||
}
|
||||
sc.sym[sname].typ = t
|
||||
}
|
||||
|
||||
case typeAssertExpr:
|
||||
t, err = nodeType2(interp, sc, n.child[1], seen)
|
||||
|
||||
default:
|
||||
err = n.cfgErrorf("type definition not implemented: %s", n.kind)
|
||||
}
|
||||
@@ -1076,6 +1127,58 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
return t, err
|
||||
}
|
||||
|
||||
func genType(interp *Interpreter, sc *scope, name string, lt *itype, types []*itype, seen []*node) (t *itype, err error) {
|
||||
// A generic type is being instantiated. Generate it.
|
||||
g, _, err := genAST(sc, lt.node.anc, types)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, err = nodeType2(interp, sc, g.lastChild(), seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lt.instance = append(lt.instance, t)
|
||||
// Add generated symbol in the scope of generic source and user.
|
||||
sc.sym[name] = &symbol{index: -1, kind: typeSym, typ: t, node: g}
|
||||
if lt.scope.sym[name] == nil {
|
||||
lt.scope.sym[name] = sc.sym[name]
|
||||
}
|
||||
|
||||
for _, nod := range lt.method {
|
||||
if err := genMethod(interp, sc, t, nod, types); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
|
||||
func genMethod(interp *Interpreter, sc *scope, t *itype, nod *node, types []*itype) error {
|
||||
gm, _, err := genAST(sc, nod, types)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if gm.typ, err = nodeType(interp, sc, gm.child[2]); err != nil {
|
||||
return err
|
||||
}
|
||||
t.addMethod(gm)
|
||||
|
||||
// If the receiver is a pointer to a generic type, generate also the pointer type.
|
||||
if rtn := gm.child[0].child[0].lastChild(); rtn != nil && rtn.kind == starExpr {
|
||||
pt := ptrOf(t, withNode(t.node), withScope(sc))
|
||||
pt.addMethod(gm)
|
||||
rtn.typ = pt
|
||||
}
|
||||
|
||||
// Compile the method AST in the scope of the generic type.
|
||||
scop := nod.typ.scope
|
||||
if _, err = interp.cfg(gm, scop, scop.pkgID, scop.pkgName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate closures for function body.
|
||||
return genRun(gm)
|
||||
}
|
||||
|
||||
// findPackageType searches the top level scope for a package type.
|
||||
func findPackageType(interp *Interpreter, sc *scope, n *node) *itype {
|
||||
// Find the root scope, the package symbols will exist there.
|
||||
@@ -1129,6 +1232,8 @@ func fieldName(n *node) string {
|
||||
return fieldName(n.child[1])
|
||||
case starExpr:
|
||||
return fieldName(n.child[0])
|
||||
case indexExpr:
|
||||
return fieldName(n.child[0])
|
||||
case identExpr:
|
||||
return n.ident
|
||||
default:
|
||||
@@ -1242,8 +1347,13 @@ func (t *itype) numOut() int {
|
||||
if t.rtype.Kind() == reflect.Func {
|
||||
return t.rtype.NumOut()
|
||||
}
|
||||
case builtinT:
|
||||
switch t.name {
|
||||
case "append", "cap", "complex", "copy", "imag", "len", "make", "new", "real", "recover", "unsafe.Alignof", "unsafe.Offsetof", "unsafe.Sizeof":
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
func (t *itype) out(i int) *itype {
|
||||
@@ -1266,7 +1376,7 @@ func (t *itype) concrete() *itype {
|
||||
}
|
||||
|
||||
func (t *itype) underlying() *itype {
|
||||
if t.cat == aliasT {
|
||||
if t.cat == linkedT {
|
||||
return t.val.underlying()
|
||||
}
|
||||
return t
|
||||
@@ -1274,10 +1384,10 @@ func (t *itype) underlying() *itype {
|
||||
|
||||
// typeDefined returns true if type t1 is defined from type t2 or t2 from t1.
|
||||
func typeDefined(t1, t2 *itype) bool {
|
||||
if t1.cat == aliasT && t1.val == t2 {
|
||||
if t1.cat == linkedT && t1.val == t2 {
|
||||
return true
|
||||
}
|
||||
if t2.cat == aliasT && t2.val == t1 {
|
||||
if t2.cat == linkedT && t2.val == t1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -1313,7 +1423,7 @@ func isComplete(t *itype, visited map[string]bool) bool {
|
||||
visited[name] = true
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
if t.val != nil && t.val.cat != nilT {
|
||||
// A type aliased to a partially defined type is considered complete, to allow recursivity.
|
||||
return true
|
||||
@@ -1357,7 +1467,7 @@ func (t *itype) assignableTo(o *itype) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if t.cat == aliasT && o.cat == aliasT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
|
||||
if t.cat == linkedT && o.cat == linkedT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1425,7 +1535,7 @@ func (t *itype) ordered() bool {
|
||||
return isInt(typ) || isFloat(typ) || isString(typ)
|
||||
}
|
||||
|
||||
// Equals returns true if the given type is identical to the receiver one.
|
||||
// equals returns true if the given type is identical to the receiver one.
|
||||
func (t *itype) equals(o *itype) bool {
|
||||
switch ti, oi := isInterface(t), isInterface(o); {
|
||||
case ti && oi:
|
||||
@@ -1439,13 +1549,21 @@ func (t *itype) equals(o *itype) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// matchDefault returns true if the receiver default type is the same as the given one.
|
||||
func (t *itype) matchDefault(o *itype) bool {
|
||||
return t.untyped && t.id() == "untyped "+o.id()
|
||||
}
|
||||
|
||||
// MethodSet defines the set of methods signatures as strings, indexed per method name.
|
||||
type methodSet map[string]string
|
||||
|
||||
// Contains returns true if the method set m contains the method set n.
|
||||
func (m methodSet) contains(n methodSet) bool {
|
||||
for k, v := range n {
|
||||
if m[k] != v {
|
||||
for k := range n {
|
||||
// Only check the presence of method, not its complete signature,
|
||||
// as the receiver may be part of the arguments, which makes a
|
||||
// robust check complex.
|
||||
if _, ok := m[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1472,7 +1590,7 @@ func (t *itype) methods() methodSet {
|
||||
seen[typ] = true
|
||||
|
||||
switch typ.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
for k, v := range getMethods(typ.val) {
|
||||
res[k] = v
|
||||
}
|
||||
@@ -1563,7 +1681,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
|
||||
return v, err
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
v, err = t.val.zero()
|
||||
|
||||
case arrayT, ptrT, structT, sliceT:
|
||||
@@ -1581,7 +1699,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
|
||||
// fieldIndex returns the field index from name in a struct, or -1 if not found.
|
||||
func (t *itype) fieldIndex(name string) int {
|
||||
switch t.cat {
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
return t.val.fieldIndex(name)
|
||||
}
|
||||
for i, field := range t.field {
|
||||
@@ -1608,6 +1726,7 @@ func (t *itype) fieldSeq(seq []int) *itype {
|
||||
func (t *itype) lookupField(name string) []int {
|
||||
seen := map[*itype]bool{}
|
||||
var lookup func(*itype) []int
|
||||
tias := isStruct(t)
|
||||
|
||||
lookup = func(typ *itype) []int {
|
||||
if seen[typ] {
|
||||
@@ -1616,7 +1735,7 @@ func (t *itype) lookupField(name string) []int {
|
||||
seen[typ] = true
|
||||
|
||||
switch typ.cat {
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
return lookup(typ.val)
|
||||
}
|
||||
if fi := typ.fieldIndex(name); fi >= 0 {
|
||||
@@ -1625,7 +1744,12 @@ func (t *itype) lookupField(name string) []int {
|
||||
|
||||
for i, f := range typ.field {
|
||||
switch f.typ.cat {
|
||||
case ptrT, structT, interfaceT, aliasT:
|
||||
case ptrT, structT, interfaceT, linkedT:
|
||||
if tias != isStruct(f.typ) {
|
||||
// Interface fields are not valid embedded struct fields.
|
||||
// Struct fields are not valid interface fields.
|
||||
break
|
||||
}
|
||||
if index2 := lookup(f.typ); len(index2) > 0 {
|
||||
return append([]int{i}, index2...)
|
||||
}
|
||||
@@ -1684,7 +1808,7 @@ func (t *itype) methodCallType() reflect.Type {
|
||||
}
|
||||
|
||||
func (t *itype) resolveAlias() *itype {
|
||||
for t.cat == aliasT {
|
||||
for t.cat == linkedT {
|
||||
t = t.val
|
||||
}
|
||||
return t
|
||||
@@ -1728,13 +1852,46 @@ func (t *itype) lookupMethod2(name string, seen map[*itype]bool) (*node, []int)
|
||||
}
|
||||
}
|
||||
}
|
||||
if t.cat == aliasT || isInterfaceSrc(t) && t.val != nil {
|
||||
if t.cat == linkedT || isInterfaceSrc(t) && t.val != nil {
|
||||
return t.val.lookupMethod2(name, seen)
|
||||
}
|
||||
}
|
||||
return m, index
|
||||
}
|
||||
|
||||
// interfaceMethod returns type of method matching an interface method name (not as a concrete method).
|
||||
func (t *itype) interfaceMethod(name string) *itype {
|
||||
return t.interfaceMethod2(name, nil)
|
||||
}
|
||||
|
||||
func (t *itype) interfaceMethod2(name string, seen map[*itype]bool) *itype {
|
||||
if seen == nil {
|
||||
seen = map[*itype]bool{}
|
||||
}
|
||||
if seen[t] {
|
||||
return nil
|
||||
}
|
||||
seen[t] = true
|
||||
if t.cat == ptrT {
|
||||
return t.val.interfaceMethod2(name, seen)
|
||||
}
|
||||
for _, f := range t.field {
|
||||
if f.name == name && isInterface(t) {
|
||||
return f.typ
|
||||
}
|
||||
if !f.embed {
|
||||
continue
|
||||
}
|
||||
if typ := f.typ.interfaceMethod2(name, seen); typ != nil {
|
||||
return typ
|
||||
}
|
||||
}
|
||||
if t.cat == linkedT || isInterfaceSrc(t) && t.val != nil {
|
||||
return t.val.interfaceMethod2(name, seen)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// methodDepth returns a depth greater or equal to 0, or -1 if no match.
|
||||
func (t *itype) methodDepth(name string) int {
|
||||
if m, lint := t.lookupMethod(name); m != nil {
|
||||
@@ -1822,11 +1979,6 @@ var (
|
||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
)
|
||||
|
||||
type fieldRebuild struct {
|
||||
typ *itype
|
||||
idx int
|
||||
}
|
||||
|
||||
type refTypeContext struct {
|
||||
defined map[string]*itype
|
||||
|
||||
@@ -1834,7 +1986,7 @@ type refTypeContext struct {
|
||||
// type name (as key) is used as a field of another (or possibly the same) struct
|
||||
// type. Each of these fields will then live as an unsafe2.dummy type until the
|
||||
// whole recursion is fully resolved, and the type is fixed.
|
||||
refs map[string][]fieldRebuild
|
||||
refs map[string][]*itype
|
||||
|
||||
// When we detect for the first time that we are in a recursive type (thanks to
|
||||
// defined), we keep track of the first occurrence of the type where the recursion
|
||||
@@ -1842,6 +1994,7 @@ type refTypeContext struct {
|
||||
// "top-level" point.
|
||||
rect *itype
|
||||
rebuilding bool
|
||||
slevel int
|
||||
}
|
||||
|
||||
// Clone creates a copy of the ref type context.
|
||||
@@ -1906,7 +2059,7 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
if ctx == nil {
|
||||
ctx = &refTypeContext{
|
||||
defined: map[string]*itype{},
|
||||
refs: map[string][]fieldRebuild{},
|
||||
refs: map[string][]*itype{},
|
||||
}
|
||||
}
|
||||
if t.incomplete || t.cat == nilT {
|
||||
@@ -1931,21 +2084,21 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
|
||||
// The recursion has not been fully resolved yet.
|
||||
// To indicate that a rebuild is needed on the englobing struct,
|
||||
// return a dummy field type and create an entry with an empty fieldRebuild.
|
||||
// return a dummy field type and create an empty entry.
|
||||
flds := ctx.refs[name]
|
||||
ctx.rect = dt
|
||||
|
||||
// We know we are used as a field by someone, but we don't know by who
|
||||
// at this point in the code, so we just mark it as an empty fieldRebuild for now.
|
||||
// We'll complete the fieldRebuild in the caller.
|
||||
ctx.refs[name] = append(flds, fieldRebuild{})
|
||||
// at this point in the code, so we just mark it as an empty *itype for now.
|
||||
// We'll complete the *itype in the caller.
|
||||
ctx.refs[name] = append(flds, (*itype)(nil))
|
||||
return unsafe2.DummyType
|
||||
}
|
||||
if isGeneric(t) {
|
||||
return reflect.TypeOf((*generic)(nil)).Elem()
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
t.rtype = t.val.refType(ctx)
|
||||
case arrayT:
|
||||
t.rtype = reflect.ArrayOf(t.length, t.val.refType(ctx))
|
||||
@@ -1981,28 +2134,47 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
case mapT:
|
||||
t.rtype = reflect.MapOf(t.key.refType(ctx), t.val.refType(ctx))
|
||||
case ptrT:
|
||||
t.rtype = reflect.PtrTo(t.val.refType(ctx))
|
||||
rt := t.val.refType(ctx)
|
||||
if rt == unsafe2.DummyType && ctx.slevel > 1 {
|
||||
// We have a pointer to a recursive struct which is not yet fully computed.
|
||||
// Return it but do not yet store it in rtype, so the complete version can
|
||||
// be stored in future.
|
||||
return reflect.PtrTo(rt)
|
||||
}
|
||||
t.rtype = reflect.PtrTo(rt)
|
||||
case structT:
|
||||
if t.name != "" {
|
||||
ctx.defined[name] = t
|
||||
}
|
||||
ctx.slevel++
|
||||
var fields []reflect.StructField
|
||||
for i, f := range t.field {
|
||||
for _, f := range t.field {
|
||||
field := reflect.StructField{
|
||||
Name: exportName(f.name), Type: f.typ.refType(ctx),
|
||||
Tag: reflect.StructTag(f.tag), Anonymous: f.embed,
|
||||
Name: exportName(f.name),
|
||||
Type: f.typ.refType(ctx),
|
||||
Tag: reflect.StructTag(f.tag),
|
||||
}
|
||||
if len(t.field) == 1 && f.embed {
|
||||
// Mark the field as embedded (anonymous) only if it is the
|
||||
// only one, to avoid a panic due to golang/go#15924 issue.
|
||||
field.Anonymous = true
|
||||
}
|
||||
fields = append(fields, field)
|
||||
// Find any nil type refs that indicates a rebuild is needed on this field.
|
||||
for _, flds := range ctx.refs {
|
||||
for j, fld := range flds {
|
||||
if fld.typ == nil {
|
||||
flds[j] = fieldRebuild{typ: t, idx: i}
|
||||
if fld == nil {
|
||||
flds[j] = t
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fieldFix := []int{} // Slice of field indices to fix for recursivity.
|
||||
ctx.slevel--
|
||||
type fixStructField struct {
|
||||
name string
|
||||
index int
|
||||
}
|
||||
fieldFix := []fixStructField{} // Slice of field indices to fix for recursivity.
|
||||
t.rtype = reflect.StructOf(fields)
|
||||
if ctx.isComplete() {
|
||||
for _, s := range ctx.defined {
|
||||
@@ -2011,8 +2183,12 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
if strings.HasSuffix(f.Type.String(), "unsafe2.dummy") {
|
||||
unsafe2.SetFieldType(s.rtype, i, ctx.rect.fixDummy(s.rtype.Field(i).Type))
|
||||
if name == s.path+"/"+s.name {
|
||||
fieldFix = append(fieldFix, i)
|
||||
fieldFix = append(fieldFix, fixStructField{s.name, i})
|
||||
}
|
||||
continue
|
||||
}
|
||||
if f.Type.Kind() == reflect.Func && strings.Contains(f.Type.String(), "unsafe2.dummy") {
|
||||
fieldFix = append(fieldFix, fixStructField{s.name, i})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2027,9 +2203,11 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
// and we need both the loop above, around all the struct fields, and the loop
|
||||
// below, around the ctx.refs.
|
||||
for _, f := range ctx.refs[name] {
|
||||
for _, index := range fieldFix {
|
||||
ftyp := f.typ.field[index].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
|
||||
unsafe2.SetFieldType(f.typ.rtype, index, ftyp)
|
||||
for _, ff := range fieldFix {
|
||||
if ff.name == f.name {
|
||||
ftyp := f.field[ff.index].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
|
||||
unsafe2.SetFieldType(f.rtype, ff.index, ftyp)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -2051,14 +2229,12 @@ func (t *itype) frameType() (r reflect.Type) {
|
||||
panic(err)
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
r = t.val.frameType()
|
||||
case arrayT:
|
||||
r = reflect.ArrayOf(t.length, t.val.frameType())
|
||||
case sliceT, variadicT:
|
||||
r = reflect.SliceOf(t.val.frameType())
|
||||
case funcT:
|
||||
r = reflect.TypeOf((*node)(nil))
|
||||
case interfaceT:
|
||||
if len(t.field) == 0 {
|
||||
// empty interface, do not wrap it
|
||||
@@ -2182,16 +2358,6 @@ func constToString(v reflect.Value) string {
|
||||
return constant.StringVal(c)
|
||||
}
|
||||
|
||||
func defRecvType(n *node) *itype {
|
||||
if n.kind != funcDecl || len(n.child[0].child) == 0 {
|
||||
return nil
|
||||
}
|
||||
if r := n.child[0].child[0].lastChild(); r != nil {
|
||||
return r.typ
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func wrappedType(n *node) *itype {
|
||||
if n.typ.cat != valueT {
|
||||
return nil
|
||||
@@ -2210,7 +2376,7 @@ func isShiftNode(n *node) bool {
|
||||
// chanElement returns the channel element type.
|
||||
func chanElement(t *itype) *itype {
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
case linkedT:
|
||||
return chanElement(t.val)
|
||||
case chanT, chanSendT, chanRecvT:
|
||||
return t.val
|
||||
@@ -2234,12 +2400,16 @@ func isGeneric(t *itype) bool {
|
||||
return t.cat == funcT && t.node != nil && len(t.node.child) > 0 && len(t.node.child[0].child) > 0
|
||||
}
|
||||
|
||||
func isNamedFuncSrc(t *itype) bool {
|
||||
return isFuncSrc(t) && t.node.anc.kind == funcDecl
|
||||
}
|
||||
|
||||
func isFuncSrc(t *itype) bool {
|
||||
return t.cat == funcT || (t.cat == aliasT && isFuncSrc(t.val))
|
||||
return t.cat == funcT || (t.cat == linkedT && isFuncSrc(t.val))
|
||||
}
|
||||
|
||||
func isPtrSrc(t *itype) bool {
|
||||
return t.cat == ptrT || (t.cat == aliasT && isPtrSrc(t.val))
|
||||
return t.cat == ptrT || (t.cat == linkedT && isPtrSrc(t.val))
|
||||
}
|
||||
|
||||
func isSendChan(t *itype) bool {
|
||||
@@ -2256,7 +2426,7 @@ func isArray(t *itype) bool {
|
||||
}
|
||||
|
||||
func isInterfaceSrc(t *itype) bool {
|
||||
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
|
||||
return t.cat == interfaceT || (t.cat == linkedT && isInterfaceSrc(t.val))
|
||||
}
|
||||
|
||||
func isInterfaceBin(t *itype) bool {
|
||||
@@ -2271,7 +2441,7 @@ func isBin(t *itype) bool {
|
||||
switch t.cat {
|
||||
case valueT:
|
||||
return true
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
return isBin(t.val)
|
||||
default:
|
||||
return false
|
||||
@@ -2284,7 +2454,7 @@ func isStruct(t *itype) bool {
|
||||
switch t.cat {
|
||||
case structT:
|
||||
return true
|
||||
case aliasT, ptrT:
|
||||
case linkedT, ptrT:
|
||||
return isStruct(t.val)
|
||||
case valueT:
|
||||
k := t.rtype.Kind()
|
||||
|
||||
@@ -625,6 +625,11 @@ func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
|
||||
return n.cfgErrorf("impossible type assertion: %s does not implement %s as %q method has a pointer receiver", typ.id(), n.typ.id(), name)
|
||||
}
|
||||
|
||||
if im.cat != funcT || tm.cat != funcT {
|
||||
// It only makes sense to compare in/out parameter types if both types are functions.
|
||||
continue
|
||||
}
|
||||
|
||||
err := n.cfgErrorf("impossible type assertion: %s does not implement %s", typ.id(), n.typ.id())
|
||||
if im.numIn() != tm.numIn() || im.numOut() != tm.numOut() {
|
||||
return err
|
||||
@@ -718,21 +723,24 @@ var builtinFuncs = map[string]struct {
|
||||
args int
|
||||
variadic bool
|
||||
}{
|
||||
bltnAppend: {args: 1, variadic: true},
|
||||
bltnCap: {args: 1, variadic: false},
|
||||
bltnClose: {args: 1, variadic: false},
|
||||
bltnComplex: {args: 2, variadic: false},
|
||||
bltnImag: {args: 1, variadic: false},
|
||||
bltnCopy: {args: 2, variadic: false},
|
||||
bltnDelete: {args: 2, variadic: false},
|
||||
bltnLen: {args: 1, variadic: false},
|
||||
bltnMake: {args: 1, variadic: true},
|
||||
bltnNew: {args: 1, variadic: false},
|
||||
bltnPanic: {args: 1, variadic: false},
|
||||
bltnPrint: {args: 0, variadic: true},
|
||||
bltnPrintln: {args: 0, variadic: true},
|
||||
bltnReal: {args: 1, variadic: false},
|
||||
bltnRecover: {args: 0, variadic: false},
|
||||
bltnAlignof: {args: 1, variadic: false},
|
||||
bltnAppend: {args: 1, variadic: true},
|
||||
bltnCap: {args: 1, variadic: false},
|
||||
bltnClose: {args: 1, variadic: false},
|
||||
bltnComplex: {args: 2, variadic: false},
|
||||
bltnImag: {args: 1, variadic: false},
|
||||
bltnCopy: {args: 2, variadic: false},
|
||||
bltnDelete: {args: 2, variadic: false},
|
||||
bltnLen: {args: 1, variadic: false},
|
||||
bltnMake: {args: 1, variadic: true},
|
||||
bltnNew: {args: 1, variadic: false},
|
||||
bltnOffsetof: {args: 1, variadic: false},
|
||||
bltnPanic: {args: 1, variadic: false},
|
||||
bltnPrint: {args: 0, variadic: true},
|
||||
bltnPrintln: {args: 0, variadic: true},
|
||||
bltnReal: {args: 1, variadic: false},
|
||||
bltnRecover: {args: 0, variadic: false},
|
||||
bltnSizeof: {args: 1, variadic: false},
|
||||
}
|
||||
|
||||
func (check typecheck) builtin(name string, n *node, child []*node, ellipsis bool) error {
|
||||
@@ -922,7 +930,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
|
||||
return err
|
||||
}
|
||||
}
|
||||
case bltnRecover, bltnNew:
|
||||
case bltnRecover, bltnNew, bltnAlignof, bltnOffsetof, bltnSizeof:
|
||||
// Nothing to do.
|
||||
default:
|
||||
return n.cfgErrorf("unsupported builtin %s", name)
|
||||
@@ -1088,6 +1096,9 @@ func (check typecheck) convertUntyped(n *node, typ *itype) error {
|
||||
return convErr
|
||||
}
|
||||
return nil
|
||||
case n.typ.isNil() && typ.id() == "unsafe.Pointer":
|
||||
n.typ = typ
|
||||
return nil
|
||||
default:
|
||||
return convErr
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
|
||||
gen "github.com/traefik/yaegi/stdlib/generic"
|
||||
)
|
||||
|
||||
// Symbols returns a map of interpreter exported symbol values for the given
|
||||
@@ -139,6 +141,13 @@ func (interp *Interpreter) Use(values Exports) error {
|
||||
// well known stdlib package path.
|
||||
if _, ok := values["fmt/fmt"]; ok {
|
||||
fixStdlib(interp)
|
||||
|
||||
// Load stdlib generic source.
|
||||
for _, s := range gen.Sources {
|
||||
if _, err := interp.Compile(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
130
interp/value.go
130
interp/value.go
@@ -40,59 +40,13 @@ func valueOf(data []reflect.Value, i int) reflect.Value {
|
||||
return data[i]
|
||||
}
|
||||
|
||||
func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value) func(*frame) reflect.Value {
|
||||
if n == nil || n.child == nil || n.child[0] == nil ||
|
||||
n.child[0].child == nil || n.child[0].child[0] == nil {
|
||||
return defaultGen
|
||||
}
|
||||
c0 := n.child[0]
|
||||
if c0.child[1] == nil || c0.child[1].ident == "" {
|
||||
return defaultGen
|
||||
}
|
||||
value0 := genValue(c0.child[0])
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value0(f)
|
||||
var nod *node
|
||||
|
||||
for v.IsValid() {
|
||||
// Traverse interface indirections to find out concrete type.
|
||||
vi, ok := v.Interface().(valueInterface)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
v = vi.value
|
||||
nod = vi.node
|
||||
}
|
||||
|
||||
if nod == nil || nod.typ.rtype == nil {
|
||||
return defaultGen(f)
|
||||
}
|
||||
|
||||
// Try to get the bin method, if it doesnt exist, fall back to
|
||||
// the default generator function.
|
||||
meth, ok := nod.typ.rtype.MethodByName(c0.child[1].ident)
|
||||
if !ok {
|
||||
return defaultGen(f)
|
||||
}
|
||||
|
||||
return meth.Func
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecvIndirect(n *node) func(*frame) reflect.Value {
|
||||
vr := genValueRecv(n)
|
||||
return func(f *frame) reflect.Value {
|
||||
v := vr(f)
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
return vi.value
|
||||
}
|
||||
return v.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecv(n *node) func(*frame) reflect.Value {
|
||||
v := genValue(n.recv.node)
|
||||
var v func(*frame) reflect.Value
|
||||
if n.recv.node == nil {
|
||||
v = func(*frame) reflect.Value { return n.recv.val }
|
||||
} else {
|
||||
v = genValue(n.recv.node)
|
||||
}
|
||||
fi := n.recv.index
|
||||
|
||||
if len(fi) == 0 {
|
||||
@@ -101,39 +55,19 @@ func genValueRecv(n *node) func(*frame) reflect.Value {
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
r := v(f)
|
||||
if r.Kind() == reflect.Ptr {
|
||||
r = r.Elem()
|
||||
}
|
||||
return r.FieldByIndex(fi)
|
||||
}
|
||||
}
|
||||
|
||||
func genValueBinRecv(n *node, recv *receiver) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
binValue := genValue(recv.node)
|
||||
|
||||
v := func(f *frame) reflect.Value {
|
||||
if def, ok := value(f).Interface().(*node); ok {
|
||||
if def != nil && def.recv != nil && def.recv.val.IsValid() {
|
||||
return def.recv.val
|
||||
for _, i := range fi {
|
||||
if r.Kind() == reflect.Ptr {
|
||||
r = r.Elem()
|
||||
}
|
||||
// Note that we can't use reflect FieldByIndex method, as we may
|
||||
// traverse valueInterface wrappers to access the embedded receiver.
|
||||
r = r.Field(i)
|
||||
vi, ok := r.Interface().(valueInterface)
|
||||
if ok {
|
||||
r = vi.value
|
||||
}
|
||||
}
|
||||
|
||||
ival, _ := binValue(f).Interface().(valueInterface)
|
||||
return ival.value
|
||||
}
|
||||
|
||||
fi := recv.index
|
||||
if len(fi) == 0 {
|
||||
return v
|
||||
}
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
r := v(f)
|
||||
if r.Kind() == reflect.Ptr {
|
||||
r = r.Elem()
|
||||
}
|
||||
return r.FieldByIndex(fi)
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +80,9 @@ func genValueAsFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
if v.IsNil() {
|
||||
return reflect.New(typ).Elem()
|
||||
}
|
||||
if v.Kind() == reflect.Func {
|
||||
return v
|
||||
}
|
||||
vn, ok := v.Interface().(*node)
|
||||
if ok && vn.rval.Kind() == reflect.Func {
|
||||
// The node value is already a callable func, no need to wrap it.
|
||||
@@ -221,9 +158,7 @@ func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
|
||||
switch {
|
||||
case isInterfaceSrc(typ) && (!isEmptyInterface(typ) || len(n.typ.method) > 0):
|
||||
return genValueInterface(n)
|
||||
case isFuncSrc(typ) && (n.typ.cat == valueT || n.typ.cat == nilT):
|
||||
return genValueNode(n)
|
||||
case typ.cat == valueT && isFuncSrc(n.typ):
|
||||
case isNamedFuncSrc(n.typ):
|
||||
return genFunctionWrapper(n)
|
||||
case isInterfaceBin(typ):
|
||||
return genInterfaceWrapper(n, typ.rtype)
|
||||
@@ -237,6 +172,17 @@ func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
|
||||
return genValue(n)
|
||||
}
|
||||
|
||||
func genFuncValue(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if nod, ok := v.Interface().(*node); ok {
|
||||
return genFunctionWrapper(nod)(f)
|
||||
}
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func genValueArray(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
// dereference array pointer, to support array operations on array pointer
|
||||
@@ -419,18 +365,6 @@ func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueNode(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if _, ok := v.Interface().(*node); ok {
|
||||
return v
|
||||
}
|
||||
return reflect.ValueOf(&node{rval: v})
|
||||
}
|
||||
}
|
||||
|
||||
func vInt(v reflect.Value) (i int64) {
|
||||
if c := vConstantValue(v); c != nil {
|
||||
i, _ = constant.Int64Val(constant.ToInt(c))
|
||||
|
||||
59
stdlib/generic/go1_21_cmp.go.txt
Normal file
59
stdlib/generic/go1_21_cmp.go.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2023 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 cmp provides types and functions related to comparing
|
||||
// ordered values.
|
||||
package cmp
|
||||
|
||||
// Ordered is a constraint that permits any ordered type: any type
|
||||
// that supports the operators < <= >= >.
|
||||
// If future releases of Go add new ordered types,
|
||||
// this constraint will be modified to include them.
|
||||
//
|
||||
// Note that floating-point types may contain NaN ("not-a-number") values.
|
||||
// An operator such as == or < will always report false when
|
||||
// comparing a NaN value with any other value, NaN or not.
|
||||
// See the [Compare] function for a consistent way to compare NaN values.
|
||||
type Ordered interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 |
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
|
||||
~float32 | ~float64 |
|
||||
~string
|
||||
}
|
||||
|
||||
// Less reports whether x is less than y.
|
||||
// For floating-point types, a NaN is considered less than any non-NaN,
|
||||
// and -0.0 is not less than (is equal to) 0.0.
|
||||
func Less[T Ordered](x, y T) bool {
|
||||
return (isNaN(x) && !isNaN(y)) || x < y
|
||||
}
|
||||
|
||||
// Compare returns
|
||||
//
|
||||
// -1 if x is less than y,
|
||||
// 0 if x equals y,
|
||||
// +1 if x is greater than y.
|
||||
//
|
||||
// For floating-point types, a NaN is considered less than any non-NaN,
|
||||
// a NaN is considered equal to a NaN, and -0.0 is equal to 0.0.
|
||||
func Compare[T Ordered](x, y T) int {
|
||||
xNaN := isNaN(x)
|
||||
yNaN := isNaN(y)
|
||||
if xNaN && yNaN {
|
||||
return 0
|
||||
}
|
||||
if xNaN || x < y {
|
||||
return -1
|
||||
}
|
||||
if yNaN || x > y {
|
||||
return +1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// isNaN reports whether x is a NaN without requiring the math package.
|
||||
// This will always return false if T is not floating-point.
|
||||
func isNaN[T Ordered](x T) bool {
|
||||
return x != x
|
||||
}
|
||||
33
stdlib/generic/go1_21_generic.go
Normal file
33
stdlib/generic/go1_21_generic.go
Normal file
@@ -0,0 +1,33 @@
|
||||
//go:build go1.21 && !go1.22
|
||||
// +build go1.21,!go1.22
|
||||
|
||||
package generic
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed go1_21_cmp.go.txt
|
||||
var cmpSource string
|
||||
|
||||
//go:embed go1_21_maps.go.txt
|
||||
var mapsSource string
|
||||
|
||||
//go:embed go1_21_slices.go.txt
|
||||
var slicesSource string
|
||||
|
||||
/*
|
||||
//go:embed go1_21_sync.go.txt
|
||||
var syncSource string
|
||||
|
||||
//go:embed go1_21_sync_atomic.go.txt
|
||||
var syncAtomicSource string
|
||||
*/
|
||||
|
||||
// Sources contains the list of generic packages source strings.
|
||||
var Sources = [...]string{
|
||||
cmpSource,
|
||||
mapsSource,
|
||||
slicesSource,
|
||||
// FIXME(marc): support the following.
|
||||
// syncAtomicSource,
|
||||
// syncSource,
|
||||
}
|
||||
66
stdlib/generic/go1_21_maps.go.txt
Normal file
66
stdlib/generic/go1_21_maps.go.txt
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2021 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 maps defines various functions useful with maps of any type.
|
||||
package maps
|
||||
|
||||
// Equal reports whether two maps contain the same key/value pairs.
|
||||
// Values are compared using ==.
|
||||
func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
for k, v1 := range m1 {
|
||||
if v2, ok := m2[k]; !ok || v1 != v2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// EqualFunc is like Equal, but compares values using eq.
|
||||
// Keys are still compared with ==.
|
||||
func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
for k, v1 := range m1 {
|
||||
if v2, ok := m2[k]; !ok || !eq(v1, v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// clone is implemented in the runtime package.
|
||||
func clone(m any) any { return m }
|
||||
|
||||
// Clone returns a copy of m. This is a shallow clone:
|
||||
// the new keys and values are set using ordinary assignment.
|
||||
func Clone[M ~map[K]V, K comparable, V any](m M) M {
|
||||
// Preserve nil in case it matters.
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
return clone(m).(M)
|
||||
}
|
||||
|
||||
// Copy copies all key/value pairs in src adding them to dst.
|
||||
// When a key in src is already present in dst,
|
||||
// the value in dst will be overwritten by the value associated
|
||||
// with the key in src.
|
||||
func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) {
|
||||
for k, v := range src {
|
||||
dst[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteFunc deletes any key/value pairs from m for which del returns true.
|
||||
func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool) {
|
||||
for k, v := range m {
|
||||
if del(k, v) {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
1651
stdlib/generic/go1_21_slices.go.txt
Normal file
1651
stdlib/generic/go1_21_slices.go.txt
Normal file
File diff suppressed because it is too large
Load Diff
177
stdlib/generic/go1_21_sync.go.txt
Normal file
177
stdlib/generic/go1_21_sync.go.txt
Normal file
@@ -0,0 +1,177 @@
|
||||
// Copyright 2022 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 sync
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// OnceFunc returns a function that invokes f only once. The returned function
|
||||
// may be called concurrently.
|
||||
//
|
||||
// If f panics, the returned function will panic with the same value on every call.
|
||||
func OnceFunc(f func()) func() {
|
||||
var (
|
||||
once Once
|
||||
valid bool
|
||||
p any
|
||||
)
|
||||
// Construct the inner closure just once to reduce costs on the fast path.
|
||||
g := func() {
|
||||
defer func() {
|
||||
p = recover()
|
||||
if !valid {
|
||||
// Re-panic immediately so on the first call the user gets a
|
||||
// complete stack trace into f.
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
f()
|
||||
valid = true // Set only if f does not panic
|
||||
}
|
||||
return func() {
|
||||
once.Do(g)
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OnceValue returns a function that invokes f only once and returns the value
|
||||
// returned by f. The returned function may be called concurrently.
|
||||
//
|
||||
// If f panics, the returned function will panic with the same value on every call.
|
||||
func OnceValue[T any](f func() T) func() T {
|
||||
var (
|
||||
once Once
|
||||
valid bool
|
||||
p any
|
||||
result T
|
||||
)
|
||||
g := func() {
|
||||
defer func() {
|
||||
p = recover()
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
result = f()
|
||||
valid = true
|
||||
}
|
||||
return func() T {
|
||||
once.Do(g)
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// OnceValues returns a function that invokes f only once and returns the values
|
||||
// returned by f. The returned function may be called concurrently.
|
||||
//
|
||||
// If f panics, the returned function will panic with the same value on every call.
|
||||
func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
|
||||
var (
|
||||
once Once
|
||||
valid bool
|
||||
p any
|
||||
r1 T1
|
||||
r2 T2
|
||||
)
|
||||
g := func() {
|
||||
defer func() {
|
||||
p = recover()
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
r1, r2 = f()
|
||||
valid = true
|
||||
}
|
||||
return func() (T1, T2) {
|
||||
once.Do(g)
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
return r1, r2
|
||||
}
|
||||
}
|
||||
// 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 sync
|
||||
|
||||
// import (
|
||||
// "sync/atomic"
|
||||
// )
|
||||
|
||||
// Once is an object that will perform exactly one action.
|
||||
//
|
||||
// A Once must not be copied after first use.
|
||||
//
|
||||
// In the terminology of the Go memory model,
|
||||
// the return from f “synchronizes before”
|
||||
// the return from any call of once.Do(f).
|
||||
type Once struct {
|
||||
// done indicates whether the action has been performed.
|
||||
// It is first in the struct because it is used in the hot path.
|
||||
// The hot path is inlined at every call site.
|
||||
// Placing done first allows more compact instructions on some architectures (amd64/386),
|
||||
// and fewer instructions (to calculate offset) on other architectures.
|
||||
done uint32
|
||||
m Mutex
|
||||
}
|
||||
|
||||
// Do calls the function f if and only if Do is being called for the
|
||||
// first time for this instance of Once. In other words, given
|
||||
//
|
||||
// var once Once
|
||||
//
|
||||
// if once.Do(f) is called multiple times, only the first call will invoke f,
|
||||
// even if f has a different value in each invocation. A new instance of
|
||||
// Once is required for each function to execute.
|
||||
//
|
||||
// Do is intended for initialization that must be run exactly once. Since f
|
||||
// is niladic, it may be necessary to use a function literal to capture the
|
||||
// arguments to a function to be invoked by Do:
|
||||
//
|
||||
// config.once.Do(func() { config.init(filename) })
|
||||
//
|
||||
// Because no call to Do returns until the one call to f returns, if f causes
|
||||
// Do to be called, it will deadlock.
|
||||
//
|
||||
// If f panics, Do considers it to have returned; future calls of Do return
|
||||
// without calling f.
|
||||
func (o *Once) Do(f func()) {
|
||||
// Note: Here is an incorrect implementation of Do:
|
||||
//
|
||||
// if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
|
||||
// f()
|
||||
// }
|
||||
//
|
||||
// Do guarantees that when it returns, f has finished.
|
||||
// This implementation would not implement that guarantee:
|
||||
// given two simultaneous calls, the winner of the cas would
|
||||
// call f, and the second would return immediately, without
|
||||
// waiting for the first's call to f to complete.
|
||||
// This is why the slow path falls back to a mutex, and why
|
||||
// the atomic.StoreUint32 must be delayed until after f returns.
|
||||
|
||||
if atomic.LoadUint32(&o.done) == 0 {
|
||||
// Outlined slow-path to allow inlining of the fast-path.
|
||||
o.doSlow(f)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Once) doSlow(f func()) {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
if o.done == 0 {
|
||||
defer atomic.StoreUint32(&o.done, 1)
|
||||
f()
|
||||
}
|
||||
}
|
||||
200
stdlib/generic/go1_21_sync_atomic.go.txt
Normal file
200
stdlib/generic/go1_21_sync_atomic.go.txt
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright 2022 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 atomic
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// A Bool is an atomic boolean value.
|
||||
// The zero value is false.
|
||||
type Bool struct {
|
||||
_ noCopy
|
||||
v uint32
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Bool) Load() bool { return LoadUint32(&x.v) != 0 }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Bool) Store(val bool) { StoreUint32(&x.v, b32(val)) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Bool) Swap(new bool) (old bool) { return SwapUint32(&x.v, b32(new)) != 0 }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for the boolean value x.
|
||||
func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) {
|
||||
return CompareAndSwapUint32(&x.v, b32(old), b32(new))
|
||||
}
|
||||
|
||||
// b32 returns a uint32 0 or 1 representing b.
|
||||
func b32(b bool) uint32 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// For testing *Pointer[T]'s methods can be inlined.
|
||||
// Keep in sync with cmd/compile/internal/test/inl_test.go:TestIntendedInlining.
|
||||
var _ = &Pointer[int]{}
|
||||
|
||||
// A Pointer is an atomic pointer of type *T. The zero value is a nil *T.
|
||||
type Pointer[T any] struct {
|
||||
// Mention *T in a field to disallow conversion between Pointer types.
|
||||
// See go.dev/issue/56603 for more details.
|
||||
// Use *T, not T, to avoid spurious recursive type definition errors.
|
||||
_ [0]*T
|
||||
|
||||
_ noCopy
|
||||
v unsafe.Pointer
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Pointer[T]) Load() *T { return (*T)(LoadPointer(&x.v)) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Pointer[T]) Store(val *T) { StorePointer(&x.v, unsafe.Pointer(val)) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Pointer[T]) Swap(new *T) (old *T) { return (*T)(SwapPointer(&x.v, unsafe.Pointer(new))) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) {
|
||||
return CompareAndSwapPointer(&x.v, unsafe.Pointer(old), unsafe.Pointer(new))
|
||||
}
|
||||
|
||||
// An Int32 is an atomic int32. The zero value is zero.
|
||||
type Int32 struct {
|
||||
_ noCopy
|
||||
v int32
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Int32) Load() int32 { return LoadInt32(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Int32) Store(val int32) { StoreInt32(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Int32) Swap(new int32) (old int32) { return SwapInt32(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Int32) CompareAndSwap(old, new int32) (swapped bool) {
|
||||
return CompareAndSwapInt32(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Int32) Add(delta int32) (new int32) { return AddInt32(&x.v, delta) }
|
||||
|
||||
// An Int64 is an atomic int64. The zero value is zero.
|
||||
type Int64 struct {
|
||||
_ noCopy
|
||||
_ align64
|
||||
v int64
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Int64) Load() int64 { return LoadInt64(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Int64) Store(val int64) { StoreInt64(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Int64) Swap(new int64) (old int64) { return SwapInt64(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Int64) CompareAndSwap(old, new int64) (swapped bool) {
|
||||
return CompareAndSwapInt64(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Int64) Add(delta int64) (new int64) { return AddInt64(&x.v, delta) }
|
||||
|
||||
// A Uint32 is an atomic uint32. The zero value is zero.
|
||||
type Uint32 struct {
|
||||
_ noCopy
|
||||
v uint32
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Uint32) Load() uint32 { return LoadUint32(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Uint32) Store(val uint32) { StoreUint32(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Uint32) Swap(new uint32) (old uint32) { return SwapUint32(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Uint32) CompareAndSwap(old, new uint32) (swapped bool) {
|
||||
return CompareAndSwapUint32(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Uint32) Add(delta uint32) (new uint32) { return AddUint32(&x.v, delta) }
|
||||
|
||||
// A Uint64 is an atomic uint64. The zero value is zero.
|
||||
type Uint64 struct {
|
||||
_ noCopy
|
||||
_ align64
|
||||
v uint64
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Uint64) Load() uint64 { return LoadUint64(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Uint64) Store(val uint64) { StoreUint64(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Uint64) Swap(new uint64) (old uint64) { return SwapUint64(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
|
||||
return CompareAndSwapUint64(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Uint64) Add(delta uint64) (new uint64) { return AddUint64(&x.v, delta) }
|
||||
|
||||
// A Uintptr is an atomic uintptr. The zero value is zero.
|
||||
type Uintptr struct {
|
||||
_ noCopy
|
||||
v uintptr
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Uintptr) Load() uintptr { return LoadUintptr(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Uintptr) Store(val uintptr) { StoreUintptr(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Uintptr) Swap(new uintptr) (old uintptr) { return SwapUintptr(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) {
|
||||
return CompareAndSwapUintptr(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Uintptr) Add(delta uintptr) (new uintptr) { return AddUintptr(&x.v, delta) }
|
||||
|
||||
// noCopy may be added to structs which must not be copied
|
||||
// after the first use.
|
||||
//
|
||||
// See https://golang.org/issues/8005#issuecomment-190753527
|
||||
// for details.
|
||||
//
|
||||
// Note that it must not be embedded, due to the Lock and Unlock methods.
|
||||
type noCopy struct{}
|
||||
|
||||
// Lock is a no-op used by -copylocks checker from `go vet`.
|
||||
func (*noCopy) Lock() {}
|
||||
func (*noCopy) Unlock() {}
|
||||
|
||||
// align64 may be added to structs that must be 64-bit aligned.
|
||||
// This struct is recognized by a special case in the compiler
|
||||
// and will not work if copied to any other package.
|
||||
type align64 struct{}
|
||||
71
stdlib/generic/go1_22_cmp_cmp.go.txt
Normal file
71
stdlib/generic/go1_22_cmp_cmp.go.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2023 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 cmp provides types and functions related to comparing
|
||||
// ordered values.
|
||||
package cmp
|
||||
|
||||
// Ordered is a constraint that permits any ordered type: any type
|
||||
// that supports the operators < <= >= >.
|
||||
// If future releases of Go add new ordered types,
|
||||
// this constraint will be modified to include them.
|
||||
//
|
||||
// Note that floating-point types may contain NaN ("not-a-number") values.
|
||||
// An operator such as == or < will always report false when
|
||||
// comparing a NaN value with any other value, NaN or not.
|
||||
// See the [Compare] function for a consistent way to compare NaN values.
|
||||
type Ordered interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 |
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
|
||||
~float32 | ~float64 |
|
||||
~string
|
||||
}
|
||||
|
||||
// Less reports whether x is less than y.
|
||||
// For floating-point types, a NaN is considered less than any non-NaN,
|
||||
// and -0.0 is not less than (is equal to) 0.0.
|
||||
func Less[T Ordered](x, y T) bool {
|
||||
return (isNaN(x) && !isNaN(y)) || x < y
|
||||
}
|
||||
|
||||
// Compare returns
|
||||
//
|
||||
// -1 if x is less than y,
|
||||
// 0 if x equals y,
|
||||
// +1 if x is greater than y.
|
||||
//
|
||||
// For floating-point types, a NaN is considered less than any non-NaN,
|
||||
// a NaN is considered equal to a NaN, and -0.0 is equal to 0.0.
|
||||
func Compare[T Ordered](x, y T) int {
|
||||
xNaN := isNaN(x)
|
||||
yNaN := isNaN(y)
|
||||
if xNaN && yNaN {
|
||||
return 0
|
||||
}
|
||||
if xNaN || x < y {
|
||||
return -1
|
||||
}
|
||||
if yNaN || x > y {
|
||||
return +1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// isNaN reports whether x is a NaN without requiring the math package.
|
||||
// This will always return false if T is not floating-point.
|
||||
func isNaN[T Ordered](x T) bool {
|
||||
return x != x
|
||||
}
|
||||
|
||||
// Or returns the first of its arguments that is not equal to the zero value.
|
||||
// If no argument is non-zero, it returns the zero value.
|
||||
func Or[T comparable](vals ...T) T {
|
||||
var zero T
|
||||
for _, val := range vals {
|
||||
if val != zero {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return zero
|
||||
}
|
||||
41
stdlib/generic/go1_22_generic.go
Normal file
41
stdlib/generic/go1_22_generic.go
Normal file
@@ -0,0 +1,41 @@
|
||||
//go:build go1.22
|
||||
// +build go1.22
|
||||
|
||||
package generic
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed go1_22_cmp_cmp.go.txt
|
||||
var cmpSource string
|
||||
|
||||
//go:embed go1_22_maps_maps.go.txt
|
||||
var mapsSource string
|
||||
|
||||
//go:embed go1_22_slices_slices.go.txt
|
||||
var slicesSource string
|
||||
|
||||
/*
|
||||
//go:embed go1_22_slices_sort.go.txt
|
||||
var slicesSource1 string
|
||||
|
||||
//go:embed go1_22_slices_zsortanyfunc.go.txt
|
||||
var slicesSource2 string
|
||||
|
||||
//go:embed go1_22_sync_oncefunc.go.txt
|
||||
var syncSource string
|
||||
|
||||
//go:embed go1_22_sync_atomic_type.go.txt
|
||||
var syncAtomicSource string
|
||||
*/
|
||||
|
||||
// Sources contains the list of generic packages source strings.
|
||||
var Sources = [...]string{
|
||||
cmpSource,
|
||||
mapsSource,
|
||||
slicesSource,
|
||||
// FIXME(marc): support the following.
|
||||
// slicesSource1,
|
||||
// slicesSource2,
|
||||
// syncAtomicSource,
|
||||
// syncSource,
|
||||
}
|
||||
68
stdlib/generic/go1_22_maps_maps.go.txt
Normal file
68
stdlib/generic/go1_22_maps_maps.go.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2021 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 maps defines various functions useful with maps of any type.
|
||||
package maps
|
||||
|
||||
// Equal reports whether two maps contain the same key/value pairs.
|
||||
// Values are compared using ==.
|
||||
func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
for k, v1 := range m1 {
|
||||
if v2, ok := m2[k]; !ok || v1 != v2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// EqualFunc is like Equal, but compares values using eq.
|
||||
// Keys are still compared with ==.
|
||||
func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
for k, v1 := range m1 {
|
||||
if v2, ok := m2[k]; !ok || !eq(v1, v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// clone is implemented in the runtime package.
|
||||
func clone(m any) any {
|
||||
return m
|
||||
}
|
||||
|
||||
// Clone returns a copy of m. This is a shallow clone:
|
||||
// the new keys and values are set using ordinary assignment.
|
||||
func Clone[M ~map[K]V, K comparable, V any](m M) M {
|
||||
// Preserve nil in case it matters.
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
return clone(m).(M)
|
||||
}
|
||||
|
||||
// Copy copies all key/value pairs in src adding them to dst.
|
||||
// When a key in src is already present in dst,
|
||||
// the value in dst will be overwritten by the value associated
|
||||
// with the key in src.
|
||||
func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) {
|
||||
for k, v := range src {
|
||||
dst[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteFunc deletes any key/value pairs from m for which del returns true.
|
||||
func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool) {
|
||||
for k, v := range m {
|
||||
if del(k, v) {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
518
stdlib/generic/go1_22_slices_slices.go.txt
Normal file
518
stdlib/generic/go1_22_slices_slices.go.txt
Normal file
@@ -0,0 +1,518 @@
|
||||
// Copyright 2021 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 slices defines various functions useful with slices of any type.
|
||||
package slices
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
// TODO(marc) fix this. "unsafe"
|
||||
)
|
||||
|
||||
// Equal reports whether two slices are equal: the same length and all
|
||||
// elements equal. If the lengths are different, Equal returns false.
|
||||
// Otherwise, the elements are compared in increasing index order, and the
|
||||
// comparison stops at the first unequal pair.
|
||||
// Floating point NaNs are not considered equal.
|
||||
func Equal[S ~[]E, E comparable](s1, s2 S) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for i := range s1 {
|
||||
if s1[i] != s2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// EqualFunc reports whether two slices are equal using an equality
|
||||
// function on each pair of elements. If the lengths are different,
|
||||
// EqualFunc returns false. Otherwise, the elements are compared in
|
||||
// increasing index order, and the comparison stops at the first index
|
||||
// for which eq returns false.
|
||||
func EqualFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, eq func(E1, E2) bool) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for i, v1 := range s1 {
|
||||
v2 := s2[i]
|
||||
if !eq(v1, v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Compare compares the elements of s1 and s2, using [cmp.Compare] on each pair
|
||||
// of elements. The elements are compared sequentially, starting at index 0,
|
||||
// until one element is not equal to the other.
|
||||
// The result of comparing the first non-matching elements is returned.
|
||||
// If both slices are equal until one of them ends, the shorter slice is
|
||||
// considered less than the longer one.
|
||||
// The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2.
|
||||
func Compare[S ~[]E, E cmp.Ordered](s1, s2 S) int {
|
||||
for i, v1 := range s1 {
|
||||
if i >= len(s2) {
|
||||
return +1
|
||||
}
|
||||
v2 := s2[i]
|
||||
if c := cmp.Compare(v1, v2); c != 0 {
|
||||
return c
|
||||
}
|
||||
}
|
||||
if len(s1) < len(s2) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// CompareFunc is like [Compare] but uses a custom comparison function on each
|
||||
// pair of elements.
|
||||
// The result is the first non-zero result of cmp; if cmp always
|
||||
// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
|
||||
// and +1 if len(s1) > len(s2).
|
||||
func CompareFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, cmp func(E1, E2) int) int {
|
||||
for i, v1 := range s1 {
|
||||
if i >= len(s2) {
|
||||
return +1
|
||||
}
|
||||
v2 := s2[i]
|
||||
if c := cmp(v1, v2); c != 0 {
|
||||
return c
|
||||
}
|
||||
}
|
||||
if len(s1) < len(s2) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Index returns the index of the first occurrence of v in s,
|
||||
// or -1 if not present.
|
||||
func Index[S ~[]E, E comparable](s S, v E) int {
|
||||
for i := range s {
|
||||
if v == s[i] {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// IndexFunc returns the first index i satisfying f(s[i]),
|
||||
// or -1 if none do.
|
||||
func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
|
||||
for i := range s {
|
||||
if f(s[i]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Contains reports whether v is present in s.
|
||||
func Contains[S ~[]E, E comparable](s S, v E) bool {
|
||||
return Index(s, v) >= 0
|
||||
}
|
||||
|
||||
// ContainsFunc reports whether at least one
|
||||
// element e of s satisfies f(e).
|
||||
func ContainsFunc[S ~[]E, E any](s S, f func(E) bool) bool {
|
||||
return IndexFunc(s, f) >= 0
|
||||
}
|
||||
|
||||
// Insert inserts the values v... into s at index i,
|
||||
// returning the modified slice.
|
||||
// The elements at s[i:] are shifted up to make room.
|
||||
// In the returned slice r, r[i] == v[0],
|
||||
// and r[i+len(v)] == value originally at r[i].
|
||||
// Insert panics if i is out of range.
|
||||
// This function is O(len(s) + len(v)).
|
||||
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
|
||||
_ = s[i:] // bounds check
|
||||
|
||||
m := len(v)
|
||||
if m == 0 {
|
||||
return s
|
||||
}
|
||||
n := len(s)
|
||||
if i == n {
|
||||
return append(s, v...)
|
||||
}
|
||||
if n+m > cap(s) {
|
||||
// Use append rather than make so that we bump the size of
|
||||
// the slice up to the next storage class.
|
||||
// This is what Grow does but we don't call Grow because
|
||||
// that might copy the values twice.
|
||||
s2 := append(s[:i], make(S, n+m-i)...)
|
||||
copy(s2[i:], v)
|
||||
copy(s2[i+m:], s[i:])
|
||||
return s2
|
||||
}
|
||||
s = s[:n+m]
|
||||
|
||||
// before:
|
||||
// s: aaaaaaaabbbbccccccccdddd
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
// after:
|
||||
// s: aaaaaaaavvvvbbbbcccccccc
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
//
|
||||
// a are the values that don't move in s.
|
||||
// v are the values copied in from v.
|
||||
// b and c are the values from s that are shifted up in index.
|
||||
// d are the values that get overwritten, never to be seen again.
|
||||
|
||||
if !overlaps(v, s[i+m:]) {
|
||||
// Easy case - v does not overlap either the c or d regions.
|
||||
// (It might be in some of a or b, or elsewhere entirely.)
|
||||
// The data we copy up doesn't write to v at all, so just do it.
|
||||
|
||||
copy(s[i+m:], s[i:])
|
||||
|
||||
// Now we have
|
||||
// s: aaaaaaaabbbbbbbbcccccccc
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
// Note the b values are duplicated.
|
||||
|
||||
copy(s[i:], v)
|
||||
|
||||
// Now we have
|
||||
// s: aaaaaaaavvvvbbbbcccccccc
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
// That's the result we want.
|
||||
return s
|
||||
}
|
||||
|
||||
// The hard case - v overlaps c or d. We can't just shift up
|
||||
// the data because we'd move or clobber the values we're trying
|
||||
// to insert.
|
||||
// So instead, write v on top of d, then rotate.
|
||||
copy(s[n:], v)
|
||||
|
||||
// Now we have
|
||||
// s: aaaaaaaabbbbccccccccvvvv
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
|
||||
rotateRight(s[i:], m)
|
||||
|
||||
// Now we have
|
||||
// s: aaaaaaaavvvvbbbbcccccccc
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
// That's the result we want.
|
||||
return s
|
||||
}
|
||||
|
||||
// Delete removes the elements s[i:j] from s, returning the modified slice.
|
||||
// Delete panics if j > len(s) or s[i:j] is not a valid slice of s.
|
||||
// Delete is O(len(s)-i), so if many items must be deleted, it is better to
|
||||
// make a single call deleting them all together than to delete one at a time.
|
||||
// Delete zeroes the elements s[len(s)-(j-i):len(s)].
|
||||
func Delete[S ~[]E, E any](s S, i, j int) S {
|
||||
_ = s[i:j:len(s)] // bounds check
|
||||
|
||||
if i == j {
|
||||
return s
|
||||
}
|
||||
|
||||
oldlen := len(s)
|
||||
s = append(s[:i], s[j:]...)
|
||||
clear(s[len(s):oldlen]) // zero/nil out the obsolete elements, for GC
|
||||
return s
|
||||
}
|
||||
|
||||
// DeleteFunc removes any elements from s for which del returns true,
|
||||
// returning the modified slice.
|
||||
// DeleteFunc zeroes the elements between the new length and the original length.
|
||||
func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
|
||||
i := IndexFunc(s, del)
|
||||
if i == -1 {
|
||||
return s
|
||||
}
|
||||
// Don't start copying elements until we find one to delete.
|
||||
for j := i + 1; j < len(s); j++ {
|
||||
if v := s[j]; !del(v) {
|
||||
s[i] = v
|
||||
i++
|
||||
}
|
||||
}
|
||||
clear(s[i:]) // zero/nil out the obsolete elements, for GC
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
// Replace replaces the elements s[i:j] by the given v, and returns the
|
||||
// modified slice.
|
||||
// Replace panics if j > len(s) or s[i:j] is not a valid slice of s.
|
||||
// When len(v) < (j-i), Replace zeroes the elements between the new length and the original length.
|
||||
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
|
||||
_ = s[i:j] // bounds check
|
||||
|
||||
if i == j {
|
||||
return Insert(s, i, v...)
|
||||
}
|
||||
if j == len(s) {
|
||||
return append(s[:i], v...)
|
||||
}
|
||||
|
||||
tot := len(s[:i]) + len(v) + len(s[j:])
|
||||
if tot > cap(s) {
|
||||
// Too big to fit, allocate and copy over.
|
||||
s2 := append(s[:i], make(S, tot-i)...) // See Insert
|
||||
copy(s2[i:], v)
|
||||
copy(s2[i+len(v):], s[j:])
|
||||
return s2
|
||||
}
|
||||
|
||||
r := s[:tot]
|
||||
|
||||
if i+len(v) <= j {
|
||||
// Easy, as v fits in the deleted portion.
|
||||
copy(r[i:], v)
|
||||
copy(r[i+len(v):], s[j:])
|
||||
clear(s[tot:]) // zero/nil out the obsolete elements, for GC
|
||||
return r
|
||||
}
|
||||
|
||||
// We are expanding (v is bigger than j-i).
|
||||
// The situation is something like this:
|
||||
// (example has i=4,j=8,len(s)=16,len(v)=6)
|
||||
// s: aaaaxxxxbbbbbbbbyy
|
||||
// ^ ^ ^ ^
|
||||
// i j len(s) tot
|
||||
// a: prefix of s
|
||||
// x: deleted range
|
||||
// b: more of s
|
||||
// y: area to expand into
|
||||
|
||||
if !overlaps(r[i+len(v):], v) {
|
||||
// Easy, as v is not clobbered by the first copy.
|
||||
copy(r[i+len(v):], s[j:])
|
||||
copy(r[i:], v)
|
||||
return r
|
||||
}
|
||||
|
||||
// This is a situation where we don't have a single place to which
|
||||
// we can copy v. Parts of it need to go to two different places.
|
||||
// We want to copy the prefix of v into y and the suffix into x, then
|
||||
// rotate |y| spots to the right.
|
||||
//
|
||||
// v[2:] v[:2]
|
||||
// | |
|
||||
// s: aaaavvvvbbbbbbbbvv
|
||||
// ^ ^ ^ ^
|
||||
// i j len(s) tot
|
||||
//
|
||||
// If either of those two destinations don't alias v, then we're good.
|
||||
y := len(v) - (j - i) // length of y portion
|
||||
|
||||
if !overlaps(r[i:j], v) {
|
||||
copy(r[i:j], v[y:])
|
||||
copy(r[len(s):], v[:y])
|
||||
rotateRight(r[i:], y)
|
||||
return r
|
||||
}
|
||||
if !overlaps(r[len(s):], v) {
|
||||
copy(r[len(s):], v[:y])
|
||||
copy(r[i:j], v[y:])
|
||||
rotateRight(r[i:], y)
|
||||
return r
|
||||
}
|
||||
|
||||
// Now we know that v overlaps both x and y.
|
||||
// That means that the entirety of b is *inside* v.
|
||||
// So we don't need to preserve b at all; instead we
|
||||
// can copy v first, then copy the b part of v out of
|
||||
// v to the right destination.
|
||||
k := startIdx(v, s[j:])
|
||||
copy(r[i:], v)
|
||||
copy(r[i+len(v):], r[i+k:])
|
||||
return r
|
||||
}
|
||||
|
||||
// Clone returns a copy of the slice.
|
||||
// The elements are copied using assignment, so this is a shallow clone.
|
||||
func Clone[S ~[]E, E any](s S) S {
|
||||
// The s[:0:0] preserves nil in case it matters.
|
||||
return append(s[:0:0], s...)
|
||||
}
|
||||
|
||||
// Compact replaces consecutive runs of equal elements with a single copy.
|
||||
// This is like the uniq command found on Unix.
|
||||
// Compact modifies the contents of the slice s and returns the modified slice,
|
||||
// which may have a smaller length.
|
||||
// Compact zeroes the elements between the new length and the original length.
|
||||
func Compact[S ~[]E, E comparable](s S) S {
|
||||
if len(s) < 2 {
|
||||
return s
|
||||
}
|
||||
i := 1
|
||||
for k := 1; k < len(s); k++ {
|
||||
if s[k] != s[k-1] {
|
||||
if i != k {
|
||||
s[i] = s[k]
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
clear(s[i:]) // zero/nil out the obsolete elements, for GC
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
// CompactFunc is like [Compact] but uses an equality function to compare elements.
|
||||
// For runs of elements that compare equal, CompactFunc keeps the first one.
|
||||
// CompactFunc zeroes the elements between the new length and the original length.
|
||||
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
|
||||
if len(s) < 2 {
|
||||
return s
|
||||
}
|
||||
i := 1
|
||||
for k := 1; k < len(s); k++ {
|
||||
if !eq(s[k], s[k-1]) {
|
||||
if i != k {
|
||||
s[i] = s[k]
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
clear(s[i:]) // zero/nil out the obsolete elements, for GC
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
// Grow increases the slice's capacity, if necessary, to guarantee space for
|
||||
// another n elements. After Grow(n), at least n elements can be appended
|
||||
// to the slice without another allocation. If n is negative or too large to
|
||||
// allocate the memory, Grow panics.
|
||||
func Grow[S ~[]E, E any](s S, n int) S {
|
||||
if n < 0 {
|
||||
panic("cannot be negative")
|
||||
}
|
||||
if n -= cap(s) - len(s); n > 0 {
|
||||
s = append(s[:cap(s)], make([]E, n)...)[:len(s)]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
|
||||
func Clip[S ~[]E, E any](s S) S {
|
||||
return s[:len(s):len(s)]
|
||||
}
|
||||
|
||||
// Rotation algorithm explanation:
|
||||
//
|
||||
// rotate left by 2
|
||||
// start with
|
||||
// 0123456789
|
||||
// split up like this
|
||||
// 01 234567 89
|
||||
// swap first 2 and last 2
|
||||
// 89 234567 01
|
||||
// join first parts
|
||||
// 89234567 01
|
||||
// recursively rotate first left part by 2
|
||||
// 23456789 01
|
||||
// join at the end
|
||||
// 2345678901
|
||||
//
|
||||
// rotate left by 8
|
||||
// start with
|
||||
// 0123456789
|
||||
// split up like this
|
||||
// 01 234567 89
|
||||
// swap first 2 and last 2
|
||||
// 89 234567 01
|
||||
// join last parts
|
||||
// 89 23456701
|
||||
// recursively rotate second part left by 6
|
||||
// 89 01234567
|
||||
// join at the end
|
||||
// 8901234567
|
||||
|
||||
// TODO: There are other rotate algorithms.
|
||||
// This algorithm has the desirable property that it moves each element exactly twice.
|
||||
// The triple-reverse algorithm is simpler and more cache friendly, but takes more writes.
|
||||
// The follow-cycles algorithm can be 1-write but it is not very cache friendly.
|
||||
|
||||
// rotateLeft rotates b left by n spaces.
|
||||
// s_final[i] = s_orig[i+r], wrapping around.
|
||||
func rotateLeft[E any](s []E, r int) {
|
||||
for r != 0 && r != len(s) {
|
||||
if r*2 <= len(s) {
|
||||
swap(s[:r], s[len(s)-r:])
|
||||
s = s[:len(s)-r]
|
||||
} else {
|
||||
swap(s[:len(s)-r], s[r:])
|
||||
s, r = s[len(s)-r:], r*2-len(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
func rotateRight[E any](s []E, r int) {
|
||||
rotateLeft(s, len(s)-r)
|
||||
}
|
||||
|
||||
// swap swaps the contents of x and y. x and y must be equal length and disjoint.
|
||||
func swap[E any](x, y []E) {
|
||||
for i := 0; i < len(x); i++ {
|
||||
x[i], y[i] = y[i], x[i]
|
||||
}
|
||||
}
|
||||
|
||||
// overlaps reports whether the memory ranges a[0:len(a)] and b[0:len(b)] overlap.
|
||||
func overlaps[E any](a, b []E) bool {
|
||||
return false
|
||||
/* TODO(marc): restore the following
|
||||
if len(a) == 0 || len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
elemSize := unsafe.Sizeof(a[0])
|
||||
if elemSize == 0 {
|
||||
return false
|
||||
}
|
||||
// TODO: use a runtime/unsafe facility once one becomes available. See issue 12445.
|
||||
// Also see crypto/internal/alias/alias.go:AnyOverlap
|
||||
return uintptr(unsafe.Pointer(&a[0])) <= uintptr(unsafe.Pointer(&b[len(b)-1]))+(elemSize-1) &&
|
||||
uintptr(unsafe.Pointer(&b[0])) <= uintptr(unsafe.Pointer(&a[len(a)-1]))+(elemSize-1)
|
||||
*/
|
||||
}
|
||||
|
||||
// startIdx returns the index in haystack where the needle starts.
|
||||
// prerequisite: the needle must be aliased entirely inside the haystack.
|
||||
func startIdx[E any](haystack, needle []E) int {
|
||||
p := &needle[0]
|
||||
for i := range haystack {
|
||||
if p == &haystack[i] {
|
||||
return i
|
||||
}
|
||||
}
|
||||
// TODO: what if the overlap is by a non-integral number of Es?
|
||||
panic("needle not found")
|
||||
}
|
||||
|
||||
// Reverse reverses the elements of the slice in place.
|
||||
func Reverse[S ~[]E, E any](s S) {
|
||||
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Concat returns a new slice concatenating the passed in slices.
|
||||
func Concat[S ~[]E, E any](slices ...S) S {
|
||||
size := 0
|
||||
for _, s := range slices {
|
||||
size += len(s)
|
||||
if size < 0 {
|
||||
panic("len out of range")
|
||||
}
|
||||
}
|
||||
newslice := Grow[S](nil, size)
|
||||
for _, s := range slices {
|
||||
newslice = append(newslice, s...)
|
||||
}
|
||||
return newslice
|
||||
}
|
||||
194
stdlib/generic/go1_22_slices_sort.go.txt
Normal file
194
stdlib/generic/go1_22_slices_sort.go.txt
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright 2023 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.
|
||||
|
||||
//go:generate go run $GOROOT/src/sort/gen_sort_variants.go -generic
|
||||
|
||||
package slices
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Sort sorts a slice of any ordered type in ascending order.
|
||||
// When sorting floating-point numbers, NaNs are ordered before other values.
|
||||
func Sort[S ~[]E, E cmp.Ordered](x S) {
|
||||
n := len(x)
|
||||
pdqsortOrdered(x, 0, n, bits.Len(uint(n)))
|
||||
}
|
||||
|
||||
// SortFunc sorts the slice x in ascending order as determined by the cmp
|
||||
// function. This sort is not guaranteed to be stable.
|
||||
// cmp(a, b) should return a negative number when a < b, a positive number when
|
||||
// a > b and zero when a == b.
|
||||
//
|
||||
// SortFunc requires that cmp is a strict weak ordering.
|
||||
// See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings.
|
||||
func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
|
||||
n := len(x)
|
||||
pdqsortCmpFunc(x, 0, n, bits.Len(uint(n)), cmp)
|
||||
}
|
||||
|
||||
// SortStableFunc sorts the slice x while keeping the original order of equal
|
||||
// elements, using cmp to compare elements in the same way as [SortFunc].
|
||||
func SortStableFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
|
||||
stableCmpFunc(x, len(x), cmp)
|
||||
}
|
||||
|
||||
// IsSorted reports whether x is sorted in ascending order.
|
||||
func IsSorted[S ~[]E, E cmp.Ordered](x S) bool {
|
||||
for i := len(x) - 1; i > 0; i-- {
|
||||
if cmp.Less(x[i], x[i-1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsSortedFunc reports whether x is sorted in ascending order, with cmp as the
|
||||
// comparison function as defined by [SortFunc].
|
||||
func IsSortedFunc[S ~[]E, E any](x S, cmp func(a, b E) int) bool {
|
||||
for i := len(x) - 1; i > 0; i-- {
|
||||
if cmp(x[i], x[i-1]) < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Min returns the minimal value in x. It panics if x is empty.
|
||||
// For floating-point numbers, Min propagates NaNs (any NaN value in x
|
||||
// forces the output to be NaN).
|
||||
func Min[S ~[]E, E cmp.Ordered](x S) E {
|
||||
if len(x) < 1 {
|
||||
panic("slices.Min: empty list")
|
||||
}
|
||||
m := x[0]
|
||||
for i := 1; i < len(x); i++ {
|
||||
m = min(m, x[i])
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// MinFunc returns the minimal value in x, using cmp to compare elements.
|
||||
// It panics if x is empty. If there is more than one minimal element
|
||||
// according to the cmp function, MinFunc returns the first one.
|
||||
func MinFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
|
||||
if len(x) < 1 {
|
||||
panic("slices.MinFunc: empty list")
|
||||
}
|
||||
m := x[0]
|
||||
for i := 1; i < len(x); i++ {
|
||||
if cmp(x[i], m) < 0 {
|
||||
m = x[i]
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Max returns the maximal value in x. It panics if x is empty.
|
||||
// For floating-point E, Max propagates NaNs (any NaN value in x
|
||||
// forces the output to be NaN).
|
||||
func Max[S ~[]E, E cmp.Ordered](x S) E {
|
||||
if len(x) < 1 {
|
||||
panic("slices.Max: empty list")
|
||||
}
|
||||
m := x[0]
|
||||
for i := 1; i < len(x); i++ {
|
||||
m = max(m, x[i])
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// MaxFunc returns the maximal value in x, using cmp to compare elements.
|
||||
// It panics if x is empty. If there is more than one maximal element
|
||||
// according to the cmp function, MaxFunc returns the first one.
|
||||
func MaxFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
|
||||
if len(x) < 1 {
|
||||
panic("slices.MaxFunc: empty list")
|
||||
}
|
||||
m := x[0]
|
||||
for i := 1; i < len(x); i++ {
|
||||
if cmp(x[i], m) > 0 {
|
||||
m = x[i]
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// BinarySearch searches for target in a sorted slice and returns the position
|
||||
// where target is found, or the position where target would appear in the
|
||||
// sort order; it also returns a bool saying whether the target is really found
|
||||
// in the slice. The slice must be sorted in increasing order.
|
||||
func BinarySearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool) {
|
||||
// Inlining is faster than calling BinarySearchFunc with a lambda.
|
||||
n := len(x)
|
||||
// Define x[-1] < target and x[n] >= target.
|
||||
// Invariant: x[i-1] < target, x[j] >= target.
|
||||
i, j := 0, n
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
||||
// i ≤ h < j
|
||||
if cmp.Less(x[h], target) {
|
||||
i = h + 1 // preserves x[i-1] < target
|
||||
} else {
|
||||
j = h // preserves x[j] >= target
|
||||
}
|
||||
}
|
||||
// i == j, x[i-1] < target, and x[j] (= x[i]) >= target => answer is i.
|
||||
return i, i < n && (x[i] == target || (isNaN(x[i]) && isNaN(target)))
|
||||
}
|
||||
|
||||
// BinarySearchFunc works like [BinarySearch], but uses a custom comparison
|
||||
// function. The slice must be sorted in increasing order, where "increasing"
|
||||
// is defined by cmp. cmp should return 0 if the slice element matches
|
||||
// the target, a negative number if the slice element precedes the target,
|
||||
// or a positive number if the slice element follows the target.
|
||||
// cmp must implement the same ordering as the slice, such that if
|
||||
// cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice.
|
||||
func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool) {
|
||||
n := len(x)
|
||||
// Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 .
|
||||
// Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0.
|
||||
i, j := 0, n
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
||||
// i ≤ h < j
|
||||
if cmp(x[h], target) < 0 {
|
||||
i = h + 1 // preserves cmp(x[i - 1], target) < 0
|
||||
} else {
|
||||
j = h // preserves cmp(x[j], target) >= 0
|
||||
}
|
||||
}
|
||||
// i == j, cmp(x[i-1], target) < 0, and cmp(x[j], target) (= cmp(x[i], target)) >= 0 => answer is i.
|
||||
return i, i < n && cmp(x[i], target) == 0
|
||||
}
|
||||
|
||||
type sortedHint int // hint for pdqsort when choosing the pivot
|
||||
|
||||
const (
|
||||
unknownHint sortedHint = iota
|
||||
increasingHint
|
||||
decreasingHint
|
||||
)
|
||||
|
||||
// xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
|
||||
type xorshift uint64
|
||||
|
||||
func (r *xorshift) Next() uint64 {
|
||||
*r ^= *r << 13
|
||||
*r ^= *r >> 17
|
||||
*r ^= *r << 5
|
||||
return uint64(*r)
|
||||
}
|
||||
|
||||
func nextPowerOfTwo(length int) uint {
|
||||
return 1 << bits.Len(uint(length))
|
||||
}
|
||||
|
||||
// isNaN reports whether x is a NaN without requiring the math package.
|
||||
// This will always return false if T is not floating-point.
|
||||
func isNaN[T cmp.Ordered](x T) bool {
|
||||
return x != x
|
||||
}
|
||||
479
stdlib/generic/go1_22_slices_zsortanyfunc.go.txt
Normal file
479
stdlib/generic/go1_22_slices_zsortanyfunc.go.txt
Normal file
@@ -0,0 +1,479 @@
|
||||
// Code generated by gen_sort_variants.go; DO NOT EDIT.
|
||||
|
||||
// Copyright 2022 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 slices
|
||||
|
||||
// insertionSortCmpFunc sorts data[a:b] using insertion sort.
|
||||
func insertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
|
||||
for i := a + 1; i < b; i++ {
|
||||
for j := i; j > a && (cmp(data[j], data[j-1]) < 0); j-- {
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// siftDownCmpFunc implements the heap property on data[lo:hi].
|
||||
// first is an offset into the array where the root of the heap lies.
|
||||
func siftDownCmpFunc[E any](data []E, lo, hi, first int, cmp func(a, b E) int) {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
if child >= hi {
|
||||
break
|
||||
}
|
||||
if child+1 < hi && (cmp(data[first+child], data[first+child+1]) < 0) {
|
||||
child++
|
||||
}
|
||||
if !(cmp(data[first+root], data[first+child]) < 0) {
|
||||
return
|
||||
}
|
||||
data[first+root], data[first+child] = data[first+child], data[first+root]
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
func heapSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
|
||||
first := a
|
||||
lo := 0
|
||||
hi := b - a
|
||||
|
||||
// Build heap with greatest element at top.
|
||||
for i := (hi - 1) / 2; i >= 0; i-- {
|
||||
siftDownCmpFunc(data, i, hi, first, cmp)
|
||||
}
|
||||
|
||||
// Pop elements, largest first, into end of data.
|
||||
for i := hi - 1; i >= 0; i-- {
|
||||
data[first], data[first+i] = data[first+i], data[first]
|
||||
siftDownCmpFunc(data, lo, i, first, cmp)
|
||||
}
|
||||
}
|
||||
|
||||
// pdqsortCmpFunc sorts data[a:b].
|
||||
// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
|
||||
// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
|
||||
// C++ implementation: https://github.com/orlp/pdqsort
|
||||
// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
|
||||
// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
|
||||
func pdqsortCmpFunc[E any](data []E, a, b, limit int, cmp func(a, b E) int) {
|
||||
const maxInsertion = 12
|
||||
|
||||
var (
|
||||
wasBalanced = true // whether the last partitioning was reasonably balanced
|
||||
wasPartitioned = true // whether the slice was already partitioned
|
||||
)
|
||||
|
||||
for {
|
||||
length := b - a
|
||||
|
||||
if length <= maxInsertion {
|
||||
insertionSortCmpFunc(data, a, b, cmp)
|
||||
return
|
||||
}
|
||||
|
||||
// Fall back to heapsort if too many bad choices were made.
|
||||
if limit == 0 {
|
||||
heapSortCmpFunc(data, a, b, cmp)
|
||||
return
|
||||
}
|
||||
|
||||
// If the last partitioning was imbalanced, we need to breaking patterns.
|
||||
if !wasBalanced {
|
||||
breakPatternsCmpFunc(data, a, b, cmp)
|
||||
limit--
|
||||
}
|
||||
|
||||
pivot, hint := choosePivotCmpFunc(data, a, b, cmp)
|
||||
if hint == decreasingHint {
|
||||
reverseRangeCmpFunc(data, a, b, cmp)
|
||||
// The chosen pivot was pivot-a elements after the start of the array.
|
||||
// After reversing it is pivot-a elements before the end of the array.
|
||||
// The idea came from Rust's implementation.
|
||||
pivot = (b - 1) - (pivot - a)
|
||||
hint = increasingHint
|
||||
}
|
||||
|
||||
// The slice is likely already sorted.
|
||||
if wasBalanced && wasPartitioned && hint == increasingHint {
|
||||
if partialInsertionSortCmpFunc(data, a, b, cmp) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Probably the slice contains many duplicate elements, partition the slice into
|
||||
// elements equal to and elements greater than the pivot.
|
||||
if a > 0 && !(cmp(data[a-1], data[pivot]) < 0) {
|
||||
mid := partitionEqualCmpFunc(data, a, b, pivot, cmp)
|
||||
a = mid
|
||||
continue
|
||||
}
|
||||
|
||||
mid, alreadyPartitioned := partitionCmpFunc(data, a, b, pivot, cmp)
|
||||
wasPartitioned = alreadyPartitioned
|
||||
|
||||
leftLen, rightLen := mid-a, b-mid
|
||||
balanceThreshold := length / 8
|
||||
if leftLen < rightLen {
|
||||
wasBalanced = leftLen >= balanceThreshold
|
||||
pdqsortCmpFunc(data, a, mid, limit, cmp)
|
||||
a = mid + 1
|
||||
} else {
|
||||
wasBalanced = rightLen >= balanceThreshold
|
||||
pdqsortCmpFunc(data, mid+1, b, limit, cmp)
|
||||
b = mid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partitionCmpFunc does one quicksort partition.
|
||||
// Let p = data[pivot]
|
||||
// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
|
||||
// On return, data[newpivot] = p
|
||||
func partitionCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int, alreadyPartitioned bool) {
|
||||
data[a], data[pivot] = data[pivot], data[a]
|
||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||
|
||||
for i <= j && (cmp(data[i], data[a]) < 0) {
|
||||
i++
|
||||
}
|
||||
for i <= j && !(cmp(data[j], data[a]) < 0) {
|
||||
j--
|
||||
}
|
||||
if i > j {
|
||||
data[j], data[a] = data[a], data[j]
|
||||
return j, true
|
||||
}
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
|
||||
for {
|
||||
for i <= j && (cmp(data[i], data[a]) < 0) {
|
||||
i++
|
||||
}
|
||||
for i <= j && !(cmp(data[j], data[a]) < 0) {
|
||||
j--
|
||||
}
|
||||
if i > j {
|
||||
break
|
||||
}
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
}
|
||||
data[j], data[a] = data[a], data[j]
|
||||
return j, false
|
||||
}
|
||||
|
||||
// partitionEqualCmpFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
|
||||
// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
|
||||
func partitionEqualCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int) {
|
||||
data[a], data[pivot] = data[pivot], data[a]
|
||||
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||
|
||||
for {
|
||||
for i <= j && !(cmp(data[a], data[i]) < 0) {
|
||||
i++
|
||||
}
|
||||
for i <= j && (cmp(data[a], data[j]) < 0) {
|
||||
j--
|
||||
}
|
||||
if i > j {
|
||||
break
|
||||
}
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// partialInsertionSortCmpFunc partially sorts a slice, returns true if the slice is sorted at the end.
|
||||
func partialInsertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) bool {
|
||||
const (
|
||||
maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
|
||||
shortestShifting = 50 // don't shift any elements on short arrays
|
||||
)
|
||||
i := a + 1
|
||||
for j := 0; j < maxSteps; j++ {
|
||||
for i < b && !(cmp(data[i], data[i-1]) < 0) {
|
||||
i++
|
||||
}
|
||||
|
||||
if i == b {
|
||||
return true
|
||||
}
|
||||
|
||||
if b-a < shortestShifting {
|
||||
return false
|
||||
}
|
||||
|
||||
data[i], data[i-1] = data[i-1], data[i]
|
||||
|
||||
// Shift the smaller one to the left.
|
||||
if i-a >= 2 {
|
||||
for j := i - 1; j >= 1; j-- {
|
||||
if !(cmp(data[j], data[j-1]) < 0) {
|
||||
break
|
||||
}
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
// Shift the greater one to the right.
|
||||
if b-i >= 2 {
|
||||
for j := i + 1; j < b; j++ {
|
||||
if !(cmp(data[j], data[j-1]) < 0) {
|
||||
break
|
||||
}
|
||||
data[j], data[j-1] = data[j-1], data[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// breakPatternsCmpFunc scatters some elements around in an attempt to break some patterns
|
||||
// that might cause imbalanced partitions in quicksort.
|
||||
func breakPatternsCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
|
||||
length := b - a
|
||||
if length >= 8 {
|
||||
random := xorshift(length)
|
||||
modulus := nextPowerOfTwo(length)
|
||||
|
||||
for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
|
||||
other := int(uint(random.Next()) & (modulus - 1))
|
||||
if other >= length {
|
||||
other -= length
|
||||
}
|
||||
data[idx], data[a+other] = data[a+other], data[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// choosePivotCmpFunc chooses a pivot in data[a:b].
|
||||
//
|
||||
// [0,8): chooses a static pivot.
|
||||
// [8,shortestNinther): uses the simple median-of-three method.
|
||||
// [shortestNinther,∞): uses the Tukey ninther method.
|
||||
func choosePivotCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) (pivot int, hint sortedHint) {
|
||||
const (
|
||||
shortestNinther = 50
|
||||
maxSwaps = 4 * 3
|
||||
)
|
||||
|
||||
l := b - a
|
||||
|
||||
var (
|
||||
swaps int
|
||||
i = a + l/4*1
|
||||
j = a + l/4*2
|
||||
k = a + l/4*3
|
||||
)
|
||||
|
||||
if l >= 8 {
|
||||
if l >= shortestNinther {
|
||||
// Tukey ninther method, the idea came from Rust's implementation.
|
||||
i = medianAdjacentCmpFunc(data, i, &swaps, cmp)
|
||||
j = medianAdjacentCmpFunc(data, j, &swaps, cmp)
|
||||
k = medianAdjacentCmpFunc(data, k, &swaps, cmp)
|
||||
}
|
||||
// Find the median among i, j, k and stores it into j.
|
||||
j = medianCmpFunc(data, i, j, k, &swaps, cmp)
|
||||
}
|
||||
|
||||
switch swaps {
|
||||
case 0:
|
||||
return j, increasingHint
|
||||
case maxSwaps:
|
||||
return j, decreasingHint
|
||||
default:
|
||||
return j, unknownHint
|
||||
}
|
||||
}
|
||||
|
||||
// order2CmpFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
|
||||
func order2CmpFunc[E any](data []E, a, b int, swaps *int, cmp func(a, b E) int) (int, int) {
|
||||
if cmp(data[b], data[a]) < 0 {
|
||||
*swaps++
|
||||
return b, a
|
||||
}
|
||||
return a, b
|
||||
}
|
||||
|
||||
// medianCmpFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
|
||||
func medianCmpFunc[E any](data []E, a, b, c int, swaps *int, cmp func(a, b E) int) int {
|
||||
a, b = order2CmpFunc(data, a, b, swaps, cmp)
|
||||
b, c = order2CmpFunc(data, b, c, swaps, cmp)
|
||||
a, b = order2CmpFunc(data, a, b, swaps, cmp)
|
||||
return b
|
||||
}
|
||||
|
||||
// medianAdjacentCmpFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
|
||||
func medianAdjacentCmpFunc[E any](data []E, a int, swaps *int, cmp func(a, b E) int) int {
|
||||
return medianCmpFunc(data, a-1, a, a+1, swaps, cmp)
|
||||
}
|
||||
|
||||
func reverseRangeCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
|
||||
i := a
|
||||
j := b - 1
|
||||
for i < j {
|
||||
data[i], data[j] = data[j], data[i]
|
||||
i++
|
||||
j--
|
||||
}
|
||||
}
|
||||
|
||||
func swapRangeCmpFunc[E any](data []E, a, b, n int, cmp func(a, b E) int) {
|
||||
for i := 0; i < n; i++ {
|
||||
data[a+i], data[b+i] = data[b+i], data[a+i]
|
||||
}
|
||||
}
|
||||
|
||||
func stableCmpFunc[E any](data []E, n int, cmp func(a, b E) int) {
|
||||
blockSize := 20 // must be > 0
|
||||
a, b := 0, blockSize
|
||||
for b <= n {
|
||||
insertionSortCmpFunc(data, a, b, cmp)
|
||||
a = b
|
||||
b += blockSize
|
||||
}
|
||||
insertionSortCmpFunc(data, a, n, cmp)
|
||||
|
||||
for blockSize < n {
|
||||
a, b = 0, 2*blockSize
|
||||
for b <= n {
|
||||
symMergeCmpFunc(data, a, a+blockSize, b, cmp)
|
||||
a = b
|
||||
b += 2 * blockSize
|
||||
}
|
||||
if m := a + blockSize; m < n {
|
||||
symMergeCmpFunc(data, a, m, n, cmp)
|
||||
}
|
||||
blockSize *= 2
|
||||
}
|
||||
}
|
||||
|
||||
// symMergeCmpFunc merges the two sorted subsequences data[a:m] and data[m:b] using
|
||||
// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
|
||||
// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
|
||||
// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
|
||||
// Computer Science, pages 714-723. Springer, 2004.
|
||||
//
|
||||
// Let M = m-a and N = b-n. Wolog M < N.
|
||||
// The recursion depth is bound by ceil(log(N+M)).
|
||||
// The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
|
||||
// The algorithm needs O((M+N)*log(M)) calls to data.Swap.
|
||||
//
|
||||
// The paper gives O((M+N)*log(M)) as the number of assignments assuming a
|
||||
// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
|
||||
// in the paper carries through for Swap operations, especially as the block
|
||||
// swapping rotate uses only O(M+N) Swaps.
|
||||
//
|
||||
// symMerge assumes non-degenerate arguments: a < m && m < b.
|
||||
// Having the caller check this condition eliminates many leaf recursion calls,
|
||||
// which improves performance.
|
||||
func symMergeCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) {
|
||||
// Avoid unnecessary recursions of symMerge
|
||||
// by direct insertion of data[a] into data[m:b]
|
||||
// if data[a:m] only contains one element.
|
||||
if m-a == 1 {
|
||||
// Use binary search to find the lowest index i
|
||||
// such that data[i] >= data[a] for m <= i < b.
|
||||
// Exit the search loop with i == b in case no such index exists.
|
||||
i := m
|
||||
j := b
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1)
|
||||
if cmp(data[h], data[a]) < 0 {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
// Swap values until data[a] reaches the position before i.
|
||||
for k := a; k < i-1; k++ {
|
||||
data[k], data[k+1] = data[k+1], data[k]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Avoid unnecessary recursions of symMerge
|
||||
// by direct insertion of data[m] into data[a:m]
|
||||
// if data[m:b] only contains one element.
|
||||
if b-m == 1 {
|
||||
// Use binary search to find the lowest index i
|
||||
// such that data[i] > data[m] for a <= i < m.
|
||||
// Exit the search loop with i == m in case no such index exists.
|
||||
i := a
|
||||
j := m
|
||||
for i < j {
|
||||
h := int(uint(i+j) >> 1)
|
||||
if !(cmp(data[m], data[h]) < 0) {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
// Swap values until data[m] reaches the position i.
|
||||
for k := m; k > i; k-- {
|
||||
data[k], data[k-1] = data[k-1], data[k]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mid := int(uint(a+b) >> 1)
|
||||
n := mid + m
|
||||
var start, r int
|
||||
if m > mid {
|
||||
start = n - b
|
||||
r = mid
|
||||
} else {
|
||||
start = a
|
||||
r = m
|
||||
}
|
||||
p := n - 1
|
||||
|
||||
for start < r {
|
||||
c := int(uint(start+r) >> 1)
|
||||
if !(cmp(data[p-c], data[c]) < 0) {
|
||||
start = c + 1
|
||||
} else {
|
||||
r = c
|
||||
}
|
||||
}
|
||||
|
||||
end := n - start
|
||||
if start < m && m < end {
|
||||
rotateCmpFunc(data, start, m, end, cmp)
|
||||
}
|
||||
if a < start && start < mid {
|
||||
symMergeCmpFunc(data, a, start, mid, cmp)
|
||||
}
|
||||
if mid < end && end < b {
|
||||
symMergeCmpFunc(data, mid, end, b, cmp)
|
||||
}
|
||||
}
|
||||
|
||||
// rotateCmpFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
|
||||
// Data of the form 'x u v y' is changed to 'x v u y'.
|
||||
// rotate performs at most b-a many calls to data.Swap,
|
||||
// and it assumes non-degenerate arguments: a < m && m < b.
|
||||
func rotateCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) {
|
||||
i := m - a
|
||||
j := b - m
|
||||
|
||||
for i != j {
|
||||
if i > j {
|
||||
swapRangeCmpFunc(data, m-i, m, j, cmp)
|
||||
i -= j
|
||||
} else {
|
||||
swapRangeCmpFunc(data, m-i, m+j-i, i, cmp)
|
||||
j -= i
|
||||
}
|
||||
}
|
||||
// i == j
|
||||
swapRangeCmpFunc(data, m-i, m, i, cmp)
|
||||
}
|
||||
200
stdlib/generic/go1_22_sync_atomic_type.go.txt
Normal file
200
stdlib/generic/go1_22_sync_atomic_type.go.txt
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright 2022 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 atomic
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// A Bool is an atomic boolean value.
|
||||
// The zero value is false.
|
||||
type Bool struct {
|
||||
_ noCopy
|
||||
v uint32
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Bool) Load() bool { return LoadUint32(&x.v) != 0 }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Bool) Store(val bool) { StoreUint32(&x.v, b32(val)) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Bool) Swap(new bool) (old bool) { return SwapUint32(&x.v, b32(new)) != 0 }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for the boolean value x.
|
||||
func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) {
|
||||
return CompareAndSwapUint32(&x.v, b32(old), b32(new))
|
||||
}
|
||||
|
||||
// b32 returns a uint32 0 or 1 representing b.
|
||||
func b32(b bool) uint32 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// For testing *Pointer[T]'s methods can be inlined.
|
||||
// Keep in sync with cmd/compile/internal/test/inl_test.go:TestIntendedInlining.
|
||||
var _ = &Pointer[int]{}
|
||||
|
||||
// A Pointer is an atomic pointer of type *T. The zero value is a nil *T.
|
||||
type Pointer[T any] struct {
|
||||
// Mention *T in a field to disallow conversion between Pointer types.
|
||||
// See go.dev/issue/56603 for more details.
|
||||
// Use *T, not T, to avoid spurious recursive type definition errors.
|
||||
_ [0]*T
|
||||
|
||||
_ noCopy
|
||||
v unsafe.Pointer
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Pointer[T]) Load() *T { return (*T)(LoadPointer(&x.v)) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Pointer[T]) Store(val *T) { StorePointer(&x.v, unsafe.Pointer(val)) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Pointer[T]) Swap(new *T) (old *T) { return (*T)(SwapPointer(&x.v, unsafe.Pointer(new))) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) {
|
||||
return CompareAndSwapPointer(&x.v, unsafe.Pointer(old), unsafe.Pointer(new))
|
||||
}
|
||||
|
||||
// An Int32 is an atomic int32. The zero value is zero.
|
||||
type Int32 struct {
|
||||
_ noCopy
|
||||
v int32
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Int32) Load() int32 { return LoadInt32(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Int32) Store(val int32) { StoreInt32(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Int32) Swap(new int32) (old int32) { return SwapInt32(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Int32) CompareAndSwap(old, new int32) (swapped bool) {
|
||||
return CompareAndSwapInt32(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Int32) Add(delta int32) (new int32) { return AddInt32(&x.v, delta) }
|
||||
|
||||
// An Int64 is an atomic int64. The zero value is zero.
|
||||
type Int64 struct {
|
||||
_ noCopy
|
||||
_ align64
|
||||
v int64
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Int64) Load() int64 { return LoadInt64(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Int64) Store(val int64) { StoreInt64(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Int64) Swap(new int64) (old int64) { return SwapInt64(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Int64) CompareAndSwap(old, new int64) (swapped bool) {
|
||||
return CompareAndSwapInt64(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Int64) Add(delta int64) (new int64) { return AddInt64(&x.v, delta) }
|
||||
|
||||
// A Uint32 is an atomic uint32. The zero value is zero.
|
||||
type Uint32 struct {
|
||||
_ noCopy
|
||||
v uint32
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Uint32) Load() uint32 { return LoadUint32(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Uint32) Store(val uint32) { StoreUint32(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Uint32) Swap(new uint32) (old uint32) { return SwapUint32(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Uint32) CompareAndSwap(old, new uint32) (swapped bool) {
|
||||
return CompareAndSwapUint32(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Uint32) Add(delta uint32) (new uint32) { return AddUint32(&x.v, delta) }
|
||||
|
||||
// A Uint64 is an atomic uint64. The zero value is zero.
|
||||
type Uint64 struct {
|
||||
_ noCopy
|
||||
_ align64
|
||||
v uint64
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Uint64) Load() uint64 { return LoadUint64(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Uint64) Store(val uint64) { StoreUint64(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Uint64) Swap(new uint64) (old uint64) { return SwapUint64(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
|
||||
return CompareAndSwapUint64(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Uint64) Add(delta uint64) (new uint64) { return AddUint64(&x.v, delta) }
|
||||
|
||||
// A Uintptr is an atomic uintptr. The zero value is zero.
|
||||
type Uintptr struct {
|
||||
_ noCopy
|
||||
v uintptr
|
||||
}
|
||||
|
||||
// Load atomically loads and returns the value stored in x.
|
||||
func (x *Uintptr) Load() uintptr { return LoadUintptr(&x.v) }
|
||||
|
||||
// Store atomically stores val into x.
|
||||
func (x *Uintptr) Store(val uintptr) { StoreUintptr(&x.v, val) }
|
||||
|
||||
// Swap atomically stores new into x and returns the previous value.
|
||||
func (x *Uintptr) Swap(new uintptr) (old uintptr) { return SwapUintptr(&x.v, new) }
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for x.
|
||||
func (x *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) {
|
||||
return CompareAndSwapUintptr(&x.v, old, new)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to x and returns the new value.
|
||||
func (x *Uintptr) Add(delta uintptr) (new uintptr) { return AddUintptr(&x.v, delta) }
|
||||
|
||||
// noCopy may be added to structs which must not be copied
|
||||
// after the first use.
|
||||
//
|
||||
// See https://golang.org/issues/8005#issuecomment-190753527
|
||||
// for details.
|
||||
//
|
||||
// Note that it must not be embedded, due to the Lock and Unlock methods.
|
||||
type noCopy struct{}
|
||||
|
||||
// Lock is a no-op used by -copylocks checker from `go vet`.
|
||||
func (*noCopy) Lock() {}
|
||||
func (*noCopy) Unlock() {}
|
||||
|
||||
// align64 may be added to structs that must be 64-bit aligned.
|
||||
// This struct is recognized by a special case in the compiler
|
||||
// and will not work if copied to any other package.
|
||||
type align64 struct{}
|
||||
100
stdlib/generic/go1_22_sync_oncefunc.go.txt
Normal file
100
stdlib/generic/go1_22_sync_oncefunc.go.txt
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2022 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 sync
|
||||
|
||||
// OnceFunc returns a function that invokes f only once. The returned function
|
||||
// may be called concurrently.
|
||||
//
|
||||
// If f panics, the returned function will panic with the same value on every call.
|
||||
func OnceFunc(f func()) func() {
|
||||
var (
|
||||
once Once
|
||||
valid bool
|
||||
p any
|
||||
)
|
||||
// Construct the inner closure just once to reduce costs on the fast path.
|
||||
g := func() {
|
||||
defer func() {
|
||||
p = recover()
|
||||
if !valid {
|
||||
// Re-panic immediately so on the first call the user gets a
|
||||
// complete stack trace into f.
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
f()
|
||||
f = nil // Do not keep f alive after invoking it.
|
||||
valid = true // Set only if f does not panic.
|
||||
}
|
||||
return func() {
|
||||
once.Do(g)
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OnceValue returns a function that invokes f only once and returns the value
|
||||
// returned by f. The returned function may be called concurrently.
|
||||
//
|
||||
// If f panics, the returned function will panic with the same value on every call.
|
||||
func OnceValue[T any](f func() T) func() T {
|
||||
var (
|
||||
once Once
|
||||
valid bool
|
||||
p any
|
||||
result T
|
||||
)
|
||||
g := func() {
|
||||
defer func() {
|
||||
p = recover()
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
result = f()
|
||||
f = nil
|
||||
valid = true
|
||||
}
|
||||
return func() T {
|
||||
once.Do(g)
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// OnceValues returns a function that invokes f only once and returns the values
|
||||
// returned by f. The returned function may be called concurrently.
|
||||
//
|
||||
// If f panics, the returned function will panic with the same value on every call.
|
||||
func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
|
||||
var (
|
||||
once Once
|
||||
valid bool
|
||||
p any
|
||||
r1 T1
|
||||
r2 T2
|
||||
)
|
||||
g := func() {
|
||||
defer func() {
|
||||
p = recover()
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
r1, r2 = f()
|
||||
f = nil
|
||||
valid = true
|
||||
}
|
||||
return func() (T1, T2) {
|
||||
once.Do(g)
|
||||
if !valid {
|
||||
panic(p)
|
||||
}
|
||||
return r1, r2
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Code generated by 'yaegi extract context'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["context/context"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Background": reflect.ValueOf(context.Background),
|
||||
"Canceled": reflect.ValueOf(&context.Canceled).Elem(),
|
||||
"DeadlineExceeded": reflect.ValueOf(&context.DeadlineExceeded).Elem(),
|
||||
"TODO": reflect.ValueOf(context.TODO),
|
||||
"WithCancel": reflect.ValueOf(context.WithCancel),
|
||||
"WithDeadline": reflect.ValueOf(context.WithDeadline),
|
||||
"WithTimeout": reflect.ValueOf(context.WithTimeout),
|
||||
"WithValue": reflect.ValueOf(context.WithValue),
|
||||
|
||||
// type definitions
|
||||
"CancelFunc": reflect.ValueOf((*context.CancelFunc)(nil)),
|
||||
"Context": reflect.ValueOf((*context.Context)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
"_Context": reflect.ValueOf((*_context_Context)(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
// _context_Context is an interface wrapper for Context type
|
||||
type _context_Context struct {
|
||||
IValue interface{}
|
||||
WDeadline func() (deadline time.Time, ok bool)
|
||||
WDone func() <-chan struct{}
|
||||
WErr func() error
|
||||
WValue func(key any) any
|
||||
}
|
||||
|
||||
func (W _context_Context) Deadline() (deadline time.Time, ok bool) {
|
||||
return W.WDeadline()
|
||||
}
|
||||
func (W _context_Context) Done() <-chan struct{} {
|
||||
return W.WDone()
|
||||
}
|
||||
func (W _context_Context) Err() error {
|
||||
return W.WErr()
|
||||
}
|
||||
func (W _context_Context) Value(key any) any {
|
||||
return W.WValue(key)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Code generated by 'yaegi extract crypto/ed25519'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["crypto/ed25519/ed25519"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"GenerateKey": reflect.ValueOf(ed25519.GenerateKey),
|
||||
"NewKeyFromSeed": reflect.ValueOf(ed25519.NewKeyFromSeed),
|
||||
"PrivateKeySize": reflect.ValueOf(constant.MakeFromLiteral("64", token.INT, 0)),
|
||||
"PublicKeySize": reflect.ValueOf(constant.MakeFromLiteral("32", token.INT, 0)),
|
||||
"SeedSize": reflect.ValueOf(constant.MakeFromLiteral("32", token.INT, 0)),
|
||||
"Sign": reflect.ValueOf(ed25519.Sign),
|
||||
"SignatureSize": reflect.ValueOf(constant.MakeFromLiteral("64", token.INT, 0)),
|
||||
"Verify": reflect.ValueOf(ed25519.Verify),
|
||||
|
||||
// type definitions
|
||||
"PrivateKey": reflect.ValueOf((*ed25519.PrivateKey)(nil)),
|
||||
"PublicKey": reflect.ValueOf((*ed25519.PublicKey)(nil)),
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
// Code generated by 'yaegi extract crypto/tls'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["crypto/tls/tls"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"CipherSuiteName": reflect.ValueOf(tls.CipherSuiteName),
|
||||
"CipherSuites": reflect.ValueOf(tls.CipherSuites),
|
||||
"Client": reflect.ValueOf(tls.Client),
|
||||
"CurveP256": reflect.ValueOf(tls.CurveP256),
|
||||
"CurveP384": reflect.ValueOf(tls.CurveP384),
|
||||
"CurveP521": reflect.ValueOf(tls.CurveP521),
|
||||
"Dial": reflect.ValueOf(tls.Dial),
|
||||
"DialWithDialer": reflect.ValueOf(tls.DialWithDialer),
|
||||
"ECDSAWithP256AndSHA256": reflect.ValueOf(tls.ECDSAWithP256AndSHA256),
|
||||
"ECDSAWithP384AndSHA384": reflect.ValueOf(tls.ECDSAWithP384AndSHA384),
|
||||
"ECDSAWithP521AndSHA512": reflect.ValueOf(tls.ECDSAWithP521AndSHA512),
|
||||
"ECDSAWithSHA1": reflect.ValueOf(tls.ECDSAWithSHA1),
|
||||
"Ed25519": reflect.ValueOf(tls.Ed25519),
|
||||
"InsecureCipherSuites": reflect.ValueOf(tls.InsecureCipherSuites),
|
||||
"Listen": reflect.ValueOf(tls.Listen),
|
||||
"LoadX509KeyPair": reflect.ValueOf(tls.LoadX509KeyPair),
|
||||
"NewLRUClientSessionCache": reflect.ValueOf(tls.NewLRUClientSessionCache),
|
||||
"NewListener": reflect.ValueOf(tls.NewListener),
|
||||
"NoClientCert": reflect.ValueOf(tls.NoClientCert),
|
||||
"PKCS1WithSHA1": reflect.ValueOf(tls.PKCS1WithSHA1),
|
||||
"PKCS1WithSHA256": reflect.ValueOf(tls.PKCS1WithSHA256),
|
||||
"PKCS1WithSHA384": reflect.ValueOf(tls.PKCS1WithSHA384),
|
||||
"PKCS1WithSHA512": reflect.ValueOf(tls.PKCS1WithSHA512),
|
||||
"PSSWithSHA256": reflect.ValueOf(tls.PSSWithSHA256),
|
||||
"PSSWithSHA384": reflect.ValueOf(tls.PSSWithSHA384),
|
||||
"PSSWithSHA512": reflect.ValueOf(tls.PSSWithSHA512),
|
||||
"RenegotiateFreelyAsClient": reflect.ValueOf(tls.RenegotiateFreelyAsClient),
|
||||
"RenegotiateNever": reflect.ValueOf(tls.RenegotiateNever),
|
||||
"RenegotiateOnceAsClient": reflect.ValueOf(tls.RenegotiateOnceAsClient),
|
||||
"RequestClientCert": reflect.ValueOf(tls.RequestClientCert),
|
||||
"RequireAndVerifyClientCert": reflect.ValueOf(tls.RequireAndVerifyClientCert),
|
||||
"RequireAnyClientCert": reflect.ValueOf(tls.RequireAnyClientCert),
|
||||
"Server": reflect.ValueOf(tls.Server),
|
||||
"TLS_AES_128_GCM_SHA256": reflect.ValueOf(tls.TLS_AES_128_GCM_SHA256),
|
||||
"TLS_AES_256_GCM_SHA384": reflect.ValueOf(tls.TLS_AES_256_GCM_SHA384),
|
||||
"TLS_CHACHA20_POLY1305_SHA256": reflect.ValueOf(tls.TLS_CHACHA20_POLY1305_SHA256),
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": reflect.ValueOf(tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA),
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": reflect.ValueOf(tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256),
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": reflect.ValueOf(tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": reflect.ValueOf(tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": reflect.ValueOf(tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": reflect.ValueOf(tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305),
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": reflect.ValueOf(tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": reflect.ValueOf(tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA),
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": reflect.ValueOf(tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA),
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": reflect.ValueOf(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA),
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": reflect.ValueOf(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256),
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": reflect.ValueOf(tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": reflect.ValueOf(tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA),
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": reflect.ValueOf(tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": reflect.ValueOf(tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305),
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": reflect.ValueOf(tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": reflect.ValueOf(tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA),
|
||||
"TLS_FALLBACK_SCSV": reflect.ValueOf(tls.TLS_FALLBACK_SCSV),
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": reflect.ValueOf(tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA),
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA": reflect.ValueOf(tls.TLS_RSA_WITH_AES_128_CBC_SHA),
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256": reflect.ValueOf(tls.TLS_RSA_WITH_AES_128_CBC_SHA256),
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": reflect.ValueOf(tls.TLS_RSA_WITH_AES_128_GCM_SHA256),
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA": reflect.ValueOf(tls.TLS_RSA_WITH_AES_256_CBC_SHA),
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": reflect.ValueOf(tls.TLS_RSA_WITH_AES_256_GCM_SHA384),
|
||||
"TLS_RSA_WITH_RC4_128_SHA": reflect.ValueOf(tls.TLS_RSA_WITH_RC4_128_SHA),
|
||||
"VerifyClientCertIfGiven": reflect.ValueOf(tls.VerifyClientCertIfGiven),
|
||||
"VersionSSL30": reflect.ValueOf(constant.MakeFromLiteral("768", token.INT, 0)),
|
||||
"VersionTLS10": reflect.ValueOf(constant.MakeFromLiteral("769", token.INT, 0)),
|
||||
"VersionTLS11": reflect.ValueOf(constant.MakeFromLiteral("770", token.INT, 0)),
|
||||
"VersionTLS12": reflect.ValueOf(constant.MakeFromLiteral("771", token.INT, 0)),
|
||||
"VersionTLS13": reflect.ValueOf(constant.MakeFromLiteral("772", token.INT, 0)),
|
||||
"X25519": reflect.ValueOf(tls.X25519),
|
||||
"X509KeyPair": reflect.ValueOf(tls.X509KeyPair),
|
||||
|
||||
// type definitions
|
||||
"Certificate": reflect.ValueOf((*tls.Certificate)(nil)),
|
||||
"CertificateRequestInfo": reflect.ValueOf((*tls.CertificateRequestInfo)(nil)),
|
||||
"CipherSuite": reflect.ValueOf((*tls.CipherSuite)(nil)),
|
||||
"ClientAuthType": reflect.ValueOf((*tls.ClientAuthType)(nil)),
|
||||
"ClientHelloInfo": reflect.ValueOf((*tls.ClientHelloInfo)(nil)),
|
||||
"ClientSessionCache": reflect.ValueOf((*tls.ClientSessionCache)(nil)),
|
||||
"ClientSessionState": reflect.ValueOf((*tls.ClientSessionState)(nil)),
|
||||
"Config": reflect.ValueOf((*tls.Config)(nil)),
|
||||
"Conn": reflect.ValueOf((*tls.Conn)(nil)),
|
||||
"ConnectionState": reflect.ValueOf((*tls.ConnectionState)(nil)),
|
||||
"CurveID": reflect.ValueOf((*tls.CurveID)(nil)),
|
||||
"Dialer": reflect.ValueOf((*tls.Dialer)(nil)),
|
||||
"RecordHeaderError": reflect.ValueOf((*tls.RecordHeaderError)(nil)),
|
||||
"RenegotiationSupport": reflect.ValueOf((*tls.RenegotiationSupport)(nil)),
|
||||
"SignatureScheme": reflect.ValueOf((*tls.SignatureScheme)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
"_ClientSessionCache": reflect.ValueOf((*_crypto_tls_ClientSessionCache)(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
// _crypto_tls_ClientSessionCache is an interface wrapper for ClientSessionCache type
|
||||
type _crypto_tls_ClientSessionCache struct {
|
||||
IValue interface{}
|
||||
WGet func(sessionKey string) (session *tls.ClientSessionState, ok bool)
|
||||
WPut func(sessionKey string, cs *tls.ClientSessionState)
|
||||
}
|
||||
|
||||
func (W _crypto_tls_ClientSessionCache) Get(sessionKey string) (session *tls.ClientSessionState, ok bool) {
|
||||
return W.WGet(sessionKey)
|
||||
}
|
||||
func (W _crypto_tls_ClientSessionCache) Put(sessionKey string, cs *tls.ClientSessionState) {
|
||||
W.WPut(sessionKey, cs)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Code generated by 'yaegi extract embed'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["embed/embed"] = map[string]reflect.Value{
|
||||
// type definitions
|
||||
"FS": reflect.ValueOf((*embed.FS)(nil)),
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user