Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14bc3b56b8 | ||
|
|
255b1cf1de | ||
|
|
d3fc5e990e | ||
|
|
dc082b5ded | ||
|
|
d9c402e20d | ||
|
|
09a1617640 | ||
|
|
cb642c44ba | ||
|
|
f76db27c77 | ||
|
|
996b1e33c8 | ||
|
|
236a0effaf | ||
|
|
eaeb445e17 | ||
|
|
6933ba2b4e | ||
|
|
a61a7d5bcd | ||
|
|
259f64cfd4 | ||
|
|
6c74ab7bec | ||
|
|
d64563edee | ||
|
|
07039262a0 | ||
|
|
4ed9ccb5c4 | ||
|
|
25edcfee7a | ||
|
|
d183f4205e | ||
|
|
821e9ee006 | ||
|
|
00e3f924c1 | ||
|
|
2248851d77 | ||
|
|
f74d1ea6d8 | ||
|
|
606b4c3a37 | ||
|
|
4e77fc9436 | ||
|
|
ad9db379e7 | ||
|
|
7be17d393f | ||
|
|
5665c9a410 | ||
|
|
1cf9d345aa | ||
|
|
f07f25f1ba | ||
|
|
c93b836c77 | ||
|
|
371103f0d1 | ||
|
|
8bd7afbe62 | ||
|
|
8ea3a493f4 | ||
|
|
f2abd346c0 | ||
|
|
c784713aca | ||
|
|
14acf618af | ||
|
|
fbee2baf9d | ||
|
|
2819b4167b | ||
|
|
2af660cb1f | ||
|
|
8323068414 | ||
|
|
5b62f9fdb6 | ||
|
|
4f66e3fe6c | ||
|
|
1335b4c64f | ||
|
|
da03c922ca | ||
|
|
9620116c30 | ||
|
|
dd7197f2a2 | ||
|
|
9214806342 | ||
|
|
348e713a8e | ||
|
|
cb81fe41ab | ||
|
|
a876bb3673 | ||
|
|
afa46daccd | ||
|
|
3c00da291e | ||
|
|
c847481184 | ||
|
|
f46ef67180 | ||
|
|
229ddfdae1 | ||
|
|
aa7f0849e3 | ||
|
|
7617b8a090 | ||
|
|
4e06abe002 | ||
|
|
b1a758dd5a | ||
|
|
5bf4daef2d | ||
|
|
e56db3b82e | ||
|
|
d3bbe01d5c | ||
|
|
286d6c6359 | ||
|
|
84424b52bc | ||
|
|
98c2dcd3e5 | ||
|
|
808f0bde9d | ||
|
|
c5c6012947 | ||
|
|
836060c8ad | ||
|
|
7a54353c7b |
3
.github/workflows/go-cross.yml
vendored
3
.github/workflows/go-cross.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.16, 1.17 ]
|
||||
go-version: [ 1.18, 1.19 ]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
include:
|
||||
@@ -34,6 +34,7 @@ jobs:
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
stable: true
|
||||
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
- name: Checkout code
|
||||
|
||||
10
.github/workflows/main.yml
vendored
10
.github/workflows/main.yml
vendored
@@ -7,8 +7,8 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.17
|
||||
GOLANGCI_LINT_VERSION: v1.41.1
|
||||
GO_VERSION: 1.18
|
||||
GOLANGCI_LINT_VERSION: v1.47.1
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -45,12 +45,13 @@ jobs:
|
||||
needs: linting
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.16, 1.17 ]
|
||||
go-version: [ 1.18, 1.19 ]
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
stable: true
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
@@ -75,13 +76,14 @@ jobs:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/yaegi
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.16, 1.17 ]
|
||||
go-version: [ 1.18, 1.19 ]
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
stable: true
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
- v[0-9]+.[0-9]+*
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.17
|
||||
GO_VERSION: 1.19
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"scopelint", # deprecated
|
||||
"interfacer", # deprecated
|
||||
"maligned", # deprecated
|
||||
"exhaustivestruct", # deprecated
|
||||
"lll",
|
||||
"gas",
|
||||
"dupl",
|
||||
@@ -49,10 +50,15 @@
|
||||
"wrapcheck",
|
||||
"nestif",
|
||||
"exhaustive",
|
||||
"exhaustivestruct",
|
||||
"exhaustruct",
|
||||
"forbidigo",
|
||||
"ifshort",
|
||||
"forcetypeassert",
|
||||
"varnamelen",
|
||||
"nosnakecase",
|
||||
"nonamedreturns",
|
||||
"nilnil",
|
||||
"maintidx",
|
||||
"errorlint", # TODO: must be reactivate before fixes
|
||||
]
|
||||
|
||||
@@ -75,6 +81,12 @@
|
||||
[[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"]
|
||||
|
||||
@@ -18,7 +18,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.16 and Go 1.17 (the latest 2 major releases)
|
||||
* Support Go 1.18 and Go 1.19 (the latest 2 major releases)
|
||||
|
||||
## Install
|
||||
|
||||
|
||||
10
_test/a29.go
10
_test/a29.go
@@ -6,16 +6,18 @@ const (
|
||||
zero = iota
|
||||
one
|
||||
two
|
||||
three
|
||||
)
|
||||
|
||||
func main() {
|
||||
a := [...]string{
|
||||
zero: "zero",
|
||||
one: "one",
|
||||
two: "two",
|
||||
zero: "zero",
|
||||
one: "one",
|
||||
three: "three",
|
||||
three + 2: "five",
|
||||
}
|
||||
fmt.Printf("%v %T\n", a, a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [zero one two] [3]string
|
||||
// [zero one three five] [6]string
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package alias3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
A string
|
||||
}
|
||||
@@ -7,3 +12,20 @@ type T struct {
|
||||
func (t *T) Print() {
|
||||
println(t.A)
|
||||
}
|
||||
|
||||
type A http.Header
|
||||
|
||||
func (a A) ForeachKey() error {
|
||||
for k, vals := range a {
|
||||
for _, v := range vals {
|
||||
fmt.Println(k, v)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a A) Set(k string, v []string) {
|
||||
a[k] = v
|
||||
}
|
||||
|
||||
39
_test/alias4.go
Normal file
39
_test/alias4.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type A http.Header
|
||||
|
||||
func (a A) Test1() {
|
||||
fmt.Println("test1")
|
||||
}
|
||||
|
||||
type B A
|
||||
|
||||
func (b B) Test2() {
|
||||
fmt.Println("test2")
|
||||
}
|
||||
|
||||
func (b B) Test3() {
|
||||
for k, vals := range b {
|
||||
for _, v := range vals {
|
||||
fmt.Println(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
b := B{}
|
||||
|
||||
b.Test2()
|
||||
b["test"] = []string{"a", "b"}
|
||||
b.Test3()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test2
|
||||
// test a
|
||||
// test b
|
||||
24
_test/break0.go
Normal file
24
_test/break0.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
println("Boo")
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
break OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 15:5: invalid break label OuterLoop
|
||||
24
_test/break1.go
Normal file
24
_test/break1.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
break OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// I: 0
|
||||
// true
|
||||
25
_test/break2.go
Normal file
25
_test/break2.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
break OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
continue OuterLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// I: 0
|
||||
// true
|
||||
27
_test/break3.go
Normal file
27
_test/break3.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
goto OuterLoop
|
||||
println("Boo")
|
||||
OuterLoop:
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
break OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
goto OuterLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// I: 0
|
||||
// true
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -13,7 +13,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -21,7 +21,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -13,7 +13,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -40,7 +40,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -40,7 +40,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -41,7 +41,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
26
_test/cont2.go
Normal file
26
_test/cont2.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
continue OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// I: 0
|
||||
// true
|
||||
// I: 1
|
||||
// true
|
||||
24
_test/cont3.go
Normal file
24
_test/cont3.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
println("boo")
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
continue OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 15:5: invalid continue label OuterLoop
|
||||
@@ -2,12 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
file, err := ioutil.TempFile("", "yeagibench")
|
||||
file, err := os.CreateTemp("", "yeagibench")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -23,7 +22,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(file.Name())
|
||||
b, err := os.ReadFile(file.Name())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
39
_test/gen1.go
Normal file
39
_test/gen1.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// SumInts adds together the values of m.
|
||||
func SumInts(m map[string]int64) int64 {
|
||||
var s int64
|
||||
for _, v := range m {
|
||||
s += v
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SumFloats adds together the values of m.
|
||||
func SumFloats(m map[string]float64) float64 {
|
||||
var s float64
|
||||
for _, v := range m {
|
||||
s += v
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Initialize a map for the integer values
|
||||
ints := map[string]int64{
|
||||
"first": 34,
|
||||
"second": 12,
|
||||
}
|
||||
|
||||
// Initialize a map for the float values
|
||||
floats := map[string]float64{
|
||||
"first": 35.98,
|
||||
"second": 26.99,
|
||||
}
|
||||
|
||||
fmt.Printf("Non-Generic Sums: %v and %v\n",
|
||||
SumInts(ints),
|
||||
SumFloats(floats))
|
||||
}
|
||||
34
_test/gen2.go
Normal file
34
_test/gen2.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
|
||||
// as types for map values.
|
||||
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
|
||||
var s V
|
||||
for _, v := range m {
|
||||
s += v
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Initialize a map for the integer values
|
||||
ints := map[string]int64{
|
||||
"first": 34,
|
||||
"second": 12,
|
||||
}
|
||||
|
||||
// Initialize a map for the float values
|
||||
floats := map[string]float64{
|
||||
"first": 35.98,
|
||||
"second": 26.99,
|
||||
}
|
||||
|
||||
fmt.Printf("Generic Sums: %v and %v\n",
|
||||
SumIntsOrFloats[string, int64](ints),
|
||||
SumIntsOrFloats[string, float64](floats))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Generic Sums: 46 and 62.97
|
||||
22
_test/gen3.go
Normal file
22
_test/gen3.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type Number interface {
|
||||
int | int64 | ~float64
|
||||
}
|
||||
|
||||
func Sum[T Number](numbers []T) T {
|
||||
var total T
|
||||
for _, x := range numbers {
|
||||
total += x
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func main() {
|
||||
xs := []int{3, 5, 10}
|
||||
total := Sum(xs)
|
||||
println(total)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 18
|
||||
42
_test/gen4.go
Normal file
42
_test/gen4.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type List[T any] struct {
|
||||
head, tail *element[T]
|
||||
}
|
||||
|
||||
// A recursive generic type.
|
||||
type element[T any] struct {
|
||||
next *element[T]
|
||||
val T
|
||||
}
|
||||
|
||||
func (lst *List[T]) Push(v T) {
|
||||
if lst.tail == nil {
|
||||
lst.head = &element[T]{val: v}
|
||||
lst.tail = lst.head
|
||||
} else {
|
||||
lst.tail.next = &element[T]{val: v}
|
||||
lst.tail = lst.tail.next
|
||||
}
|
||||
}
|
||||
|
||||
func (lst *List[T]) GetAll() []T {
|
||||
var elems []T
|
||||
for e := lst.head; e != nil; e = e.next {
|
||||
elems = append(elems, e.val)
|
||||
}
|
||||
return elems
|
||||
}
|
||||
|
||||
func main() {
|
||||
lst := List[int]{}
|
||||
lst.Push(10)
|
||||
lst.Push(13)
|
||||
lst.Push(23)
|
||||
fmt.Println("list:", lst.GetAll())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// list: [10 13 23]
|
||||
24
_test/gen5.go
Normal file
24
_test/gen5.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Set[Elem comparable] struct {
|
||||
m map[Elem]struct{}
|
||||
}
|
||||
|
||||
func Make[Elem comparable]() Set[Elem] {
|
||||
return Set[Elem]{m: make(map[Elem]struct{})}
|
||||
}
|
||||
|
||||
func (s Set[Elem]) Add(v Elem) {
|
||||
s.m[v] = struct{}{}
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := Make[int]()
|
||||
s.Add(1)
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {map[1:{}]}
|
||||
19
_test/gen6.go
Normal file
19
_test/gen6.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
func MapKeys[K comparable, V any](m map[K]V) []K {
|
||||
r := make([]K, 0, len(m))
|
||||
for k := range m {
|
||||
r = append(r, k)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func main() {
|
||||
var m = map[int]string{1: "2", 2: "4", 4: "8"}
|
||||
|
||||
// Test type inference
|
||||
println(len(MapKeys(m)))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
19
_test/gen7.go
Normal file
19
_test/gen7.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
func MapKeys[K comparable, V any](m map[K]V) []K {
|
||||
r := make([]K, 0, len(m))
|
||||
for k := range m {
|
||||
r = append(r, k)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func main() {
|
||||
var m = map[int]string{1: "2", 2: "4", 4: "8"}
|
||||
|
||||
// Test type inference
|
||||
println(len(MapKeys))
|
||||
}
|
||||
|
||||
// Error:
|
||||
// invalid argument for len
|
||||
15
_test/gen8.go
Normal file
15
_test/gen8.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
type Float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
func add[T Float](a, b T) float64 { return float64(a) + float64(b) }
|
||||
|
||||
func main() {
|
||||
var x, y int = 1, 2
|
||||
println(add(x, y))
|
||||
}
|
||||
|
||||
// Error:
|
||||
// int does not implement main.Float
|
||||
14
_test/gen9.go
Normal file
14
_test/gen9.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
type Float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
func add[T Float](a, b T) float64 { return float64(a) + float64(b) }
|
||||
|
||||
func main() {
|
||||
println(add(1, 2))
|
||||
}
|
||||
|
||||
// Error:
|
||||
// untyped int does not implement main.Float
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func main() {
|
||||
r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.")
|
||||
|
||||
b, err := ioutil.ReadAll(r)
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -13,7 +12,7 @@ type sink interface {
|
||||
|
||||
func newSink() sink {
|
||||
// return os.Stdout // Stdout is special in yaegi tests
|
||||
file, err := ioutil.TempFile("", "yaegi-test.*")
|
||||
file, err := os.CreateTemp("", "yaegi-test.*")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
67
_test/issue-1260.go
Normal file
67
_test/issue-1260.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type WriteSyncer interface {
|
||||
io.Writer
|
||||
Sync() error
|
||||
}
|
||||
|
||||
type Sink interface {
|
||||
WriteSyncer
|
||||
io.Closer
|
||||
}
|
||||
|
||||
func newFileSink(path string) (Sink, error) {
|
||||
return os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
||||
}
|
||||
|
||||
type Sink1 struct{ name string }
|
||||
|
||||
func (s Sink1) Write(b []byte) (int, error) { println("in Write"); return 0, nil }
|
||||
func (s Sink1) Sync() error { println("in Sync"); return nil }
|
||||
func (s Sink1) Close() error { println("in Close", s.name); return nil }
|
||||
func newS1(name string) Sink { return Sink1{name} }
|
||||
func newS1p(name string) Sink { return &Sink1{name} }
|
||||
|
||||
type Sink2 struct{ name string }
|
||||
|
||||
func (s *Sink2) Write(b []byte) (int, error) { println("in Write"); return 0, nil }
|
||||
func (s *Sink2) Sync() error { println("in Sync"); return nil }
|
||||
func (s *Sink2) Close() error { println("in Close", s.name); return nil }
|
||||
func newS2(name string) Sink { return Sink1{name} }
|
||||
|
||||
func main() {
|
||||
tmpfile, err := os.CreateTemp("", "xxx")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.Remove(tmpfile.Name())
|
||||
closers := []io.Closer{}
|
||||
sink, err := newFileSink(tmpfile.Name())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
closers = append(closers, sink)
|
||||
|
||||
s1p := newS1p("ptr")
|
||||
s1 := newS1("struct")
|
||||
s2 := newS2("ptr2")
|
||||
closers = append(closers, s1p, s1, s2)
|
||||
for _, closer := range closers {
|
||||
fmt.Println(closer.Close())
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
// in Close ptr
|
||||
// <nil>
|
||||
// in Close struct
|
||||
// <nil>
|
||||
// in Close ptr2
|
||||
// <nil>
|
||||
24
_test/issue-1276.go
Normal file
24
_test/issue-1276.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
type customFlag struct{}
|
||||
|
||||
func (cf customFlag) String() string {
|
||||
return "custom flag"
|
||||
}
|
||||
|
||||
func (cf customFlag) Set(string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Var(customFlag{}, "cf", "custom flag")
|
||||
flag.Parse()
|
||||
println("Hello, playground")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello, playground
|
||||
30
_test/issue-1280.go
Normal file
30
_test/issue-1280.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type DBReader interface {
|
||||
io.ReadCloser
|
||||
io.ReaderAt
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
f DBReader
|
||||
}
|
||||
|
||||
func main() {
|
||||
f, err := os.Open("/dev/null")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
d := &DB{f}
|
||||
data := make([]byte, 1)
|
||||
_, _ = d.f.ReadAt(data, 0)
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
25
_test/issue-1285.go
Normal file
25
_test/issue-1285.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
type (
|
||||
T1 struct{ Path [12]int8 }
|
||||
T2 struct{ Path *[12]int8 }
|
||||
)
|
||||
|
||||
var (
|
||||
t11 = &T1{}
|
||||
t21 = &T2{}
|
||||
)
|
||||
|
||||
func main() {
|
||||
b := [12]byte{}
|
||||
t12 := &T1{}
|
||||
t22 := &T2{}
|
||||
b11 := (*[len(t11.Path)]byte)(&b)
|
||||
b12 := (*[len(t12.Path)]byte)(&b)
|
||||
b21 := (*[len(t21.Path)]byte)(&b)
|
||||
b22 := (*[len(t22.Path)]byte)(&b)
|
||||
println(len(b11), len(b12), len(b21), len(b22))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 12 12 12 12
|
||||
10
_test/issue-1288.go
Normal file
10
_test/issue-1288.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("Hi")
|
||||
goto done
|
||||
done:
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hi
|
||||
20
_test/issue-1300.go
Normal file
20
_test/issue-1300.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
const buflen = 512
|
||||
|
||||
type T struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func f(t *T) { *t = T{buf: make([]byte, 0, buflen)} }
|
||||
|
||||
func main() {
|
||||
s := T{}
|
||||
println(cap(s.buf))
|
||||
f(&s)
|
||||
println(cap(s.buf))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 512
|
||||
16
_test/issue-1304.go
Normal file
16
_test/issue-1304.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Alias *Node
|
||||
Child []*Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
n := &Node{Name: "parent"}
|
||||
n.Child = append(n.Child, &Node{Name: "child"})
|
||||
println(n.Name, n.Child[0].Name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// parent child
|
||||
15
_test/issue-1306.go
Normal file
15
_test/issue-1306.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func check() (result bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
result, error := check()
|
||||
fmt.Println(result, error)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true <nil>
|
||||
27
_test/issue-1308.go
Normal file
27
_test/issue-1308.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type test struct {
|
||||
v interface{}
|
||||
s string
|
||||
}
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := []test{
|
||||
{
|
||||
v: []interface{}{
|
||||
T{"hello"},
|
||||
},
|
||||
s: "world",
|
||||
},
|
||||
}
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [{[{hello}] world}]
|
||||
19
_test/issue-1311.go
Normal file
19
_test/issue-1311.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
v interface{}
|
||||
}
|
||||
|
||||
func f() (ret int64, err error) {
|
||||
ret += 2
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.v, _ = f()
|
||||
println(t.v.(int64))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
32
_test/issue-1315.go
Normal file
32
_test/issue-1315.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
type Intf interface {
|
||||
M()
|
||||
}
|
||||
|
||||
type T struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (t *T) M() { println("in M") }
|
||||
|
||||
func f(i interface{}) {
|
||||
switch j := i.(type) {
|
||||
case Intf:
|
||||
j.M()
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var i Intf
|
||||
var k interface{} = 1
|
||||
i = &T{"hello"}
|
||||
f(i)
|
||||
f(k)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in M
|
||||
// default
|
||||
29
_test/issue-1320.go
Normal file
29
_test/issue-1320.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
type Pooler interface {
|
||||
Get() string
|
||||
}
|
||||
|
||||
type baseClient struct {
|
||||
connPool Pooler
|
||||
}
|
||||
|
||||
type connPool struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (c *connPool) Get() string { return c.name }
|
||||
|
||||
func newBaseClient(i int, p Pooler) *baseClient {
|
||||
return &baseClient{connPool: p}
|
||||
}
|
||||
|
||||
func newConnPool() *connPool { return &connPool{name: "connPool"} }
|
||||
|
||||
func main() {
|
||||
b := newBaseClient(0, newConnPool())
|
||||
println(b.connPool.(*connPool).name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// connPool
|
||||
42
_test/issue-1326.go
Normal file
42
_test/issue-1326.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
type Option interface {
|
||||
apply(*T)
|
||||
}
|
||||
|
||||
type T struct {
|
||||
s string
|
||||
}
|
||||
|
||||
type opt struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (o *opt) apply(t *T) {
|
||||
println(o.name)
|
||||
}
|
||||
|
||||
func BuildOptions() []Option {
|
||||
return []Option{
|
||||
&opt{"opt1"},
|
||||
&opt{"opt2"},
|
||||
}
|
||||
}
|
||||
|
||||
func NewT(name string, options ...Option) *T {
|
||||
t := &T{name}
|
||||
for _, opt := range options {
|
||||
opt.apply(t)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := NewT("hello", BuildOptions()...)
|
||||
println(t.s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// opt1
|
||||
// opt2
|
||||
// hello
|
||||
16
_test/issue-1328.go
Normal file
16
_test/issue-1328.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
func main() {
|
||||
script := "hello"
|
||||
sumRaw := sha1.Sum([]byte(script))
|
||||
sum := hex.EncodeToString(sumRaw[:])
|
||||
println(sum)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
|
||||
42
_test/issue-1330.go
Normal file
42
_test/issue-1330.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type wrappedConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func main() {
|
||||
_, err := net.Listen("tcp", "127.0.0.1:49153")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
LocalAddr: &net.TCPAddr{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := dialer.Dial("tcp", "127.0.0.1:49153")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
t := &wrappedConn{conn}
|
||||
var w io.Writer = t
|
||||
if n, err := w.Write([]byte("hello")); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println(n)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
17
_test/issue-1332.go
Normal file
17
_test/issue-1332.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func run(fn func(name string)) { fn("test") }
|
||||
|
||||
type T2 struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (t *T2) f(s string) { println(s, t.name) }
|
||||
|
||||
func main() {
|
||||
t2 := &T2{"foo"}
|
||||
run(t2.f)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test foo
|
||||
37
_test/issue-1333.go
Normal file
37
_test/issue-1333.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
func mock(name string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprint(rw, "Hello ", name)
|
||||
}
|
||||
}
|
||||
|
||||
func client(uri string) {
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
mux.Handle("/", mock("foo"))
|
||||
client(server.URL)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello foo
|
||||
26
_test/issue-1337.go
Normal file
26
_test/issue-1337.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func f(i interface{}) {
|
||||
switch at := i.(type) {
|
||||
case int, int8:
|
||||
println("integer", at)
|
||||
case io.Reader:
|
||||
println("reader")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
func main() {
|
||||
var fd *os.File
|
||||
var r io.Reader = fd
|
||||
f(r)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// reader
|
||||
// bye
|
||||
12
_test/issue-1342.go
Normal file
12
_test/issue-1342.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
a = "a"
|
||||
fmt.Println(a, a == "a")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// a true
|
||||
13
_test/issue-1344.go
Normal file
13
_test/issue-1344.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var m = map[string]interface{}{"a": "a"}
|
||||
a, _ := m["a"]
|
||||
b, ok := a.(string)
|
||||
fmt.Println("a:", a, ", b:", b, ", ok:", ok)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// a: a , b: a , ok: true
|
||||
21
_test/issue-1354.go
Normal file
21
_test/issue-1354.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println(test()) // Go prints true, Yaegi false
|
||||
}
|
||||
|
||||
func test() bool {
|
||||
if true {
|
||||
goto label
|
||||
}
|
||||
goto label
|
||||
label:
|
||||
println("Go continues here")
|
||||
return true
|
||||
println("Yaegi goes straight to this return (this line is never printed)")
|
||||
return false
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Go continues here
|
||||
// true
|
||||
22
_test/issue-1355.go
Normal file
22
_test/issue-1355.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "github.com/traefik/yaegi/_test/p2"
|
||||
|
||||
func f(i interface{}) {
|
||||
_, ok := i.(p2.I)
|
||||
println("ok:", ok)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var v *p2.T
|
||||
var i interface{}
|
||||
|
||||
i = v
|
||||
_, ok := i.(p2.I)
|
||||
println("ok:", ok)
|
||||
f(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok: true
|
||||
// ok: true
|
||||
14
_test/issue-1360.go
Normal file
14
_test/issue-1360.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "net"
|
||||
)
|
||||
|
||||
func main() {
|
||||
v := IP{}
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
27
_test/issue-1361.go
Normal file
27
_test/issue-1361.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type obj struct {
|
||||
num float64
|
||||
}
|
||||
|
||||
type Fun func(o *obj) (r *obj, err error)
|
||||
|
||||
func numFun(fn func(f float64) float64) Fun {
|
||||
return func(o *obj) (*obj, error) {
|
||||
return &obj{fn(o.num)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := numFun(math.Cos)
|
||||
r, err := f(&obj{})
|
||||
fmt.Println(r, err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{1} <nil>
|
||||
16
_test/issue-1364.go
Normal file
16
_test/issue-1364.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var value interface{}
|
||||
var err error
|
||||
value, err = strconv.ParseFloat("123", 64)
|
||||
fmt.Println(value, err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 123 <nil>
|
||||
18
_test/issue-1365.go
Normal file
18
_test/issue-1365.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
func genInt() (int, error) { return 3, nil }
|
||||
|
||||
func getInt() (value int) {
|
||||
value, err := genInt()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(getInt())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
16
_test/issue-1368.go
Normal file
16
_test/issue-1368.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
const dollar byte = 36
|
||||
|
||||
func main() {
|
||||
var c byte = 36
|
||||
switch true {
|
||||
case c == dollar:
|
||||
println("ok")
|
||||
default:
|
||||
println("not ok")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
18
_test/issue-1371.go
Normal file
18
_test/issue-1371.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type node struct {
|
||||
parent *node
|
||||
child []*node
|
||||
key string
|
||||
}
|
||||
|
||||
func main() {
|
||||
root := &node{key: "root"}
|
||||
root.child = nil
|
||||
fmt.Println("root:", root)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// root: &{<nil> [] root}
|
||||
17
_test/issue-1373.go
Normal file
17
_test/issue-1373.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
func NewBadExpr() ast.Expr {
|
||||
return &ast.BadExpr{}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%T\n", NewBadExpr().(*ast.BadExpr))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// *ast.BadExpr
|
||||
38
_test/issue-1375.go
Normal file
38
_test/issue-1375.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Option func(*Struct)
|
||||
|
||||
func WithOption(opt string) Option {
|
||||
return func(s *Struct) {
|
||||
s.opt = opt
|
||||
}
|
||||
}
|
||||
|
||||
type Struct struct {
|
||||
opt string
|
||||
}
|
||||
|
||||
func New(opts ...Option) *Struct {
|
||||
s := new(Struct)
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Struct) ShowOption() {
|
||||
fmt.Println(s.opt)
|
||||
}
|
||||
|
||||
func main() {
|
||||
opts := []Option{
|
||||
WithOption("test"),
|
||||
}
|
||||
s := New(opts...)
|
||||
s.ShowOption()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
21
_test/issue-1378.go
Normal file
21
_test/issue-1378.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fn := func() error {
|
||||
_, err := t.GobEncode()
|
||||
return err
|
||||
}
|
||||
fmt.Println(fn())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
58
_test/issue-1381.go
Normal file
58
_test/issue-1381.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var bufPtrOne *bytes.Buffer
|
||||
var bufPtrTwo *bytes.Buffer
|
||||
var bufPtrThree *bytes.Buffer
|
||||
var bufPtrFour *bytes.Buffer
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
bufOne := bytes.Buffer{}
|
||||
bufTwo := &bytes.Buffer{}
|
||||
var bufThree bytes.Buffer
|
||||
bufFour := new(bytes.Buffer)
|
||||
|
||||
if bufPtrOne == nil {
|
||||
bufPtrOne = &bufOne
|
||||
} else if bufPtrOne == &bufOne {
|
||||
fmt.Println("bufOne was not properly redeclared")
|
||||
} else {
|
||||
fmt.Println("bufOne is properly redeclared")
|
||||
}
|
||||
|
||||
if bufPtrTwo == nil {
|
||||
bufPtrTwo = bufTwo
|
||||
} else if bufPtrTwo == bufTwo {
|
||||
fmt.Println("bufTwo was not properly redeclared")
|
||||
} else {
|
||||
fmt.Println("bufTwo is properly redeclared")
|
||||
}
|
||||
|
||||
if bufPtrThree == nil {
|
||||
bufPtrThree = &bufThree
|
||||
} else if bufPtrThree == &bufThree {
|
||||
fmt.Println("bufThree was not properly redeclared")
|
||||
} else {
|
||||
fmt.Println("bufThree is properly redeclared")
|
||||
}
|
||||
|
||||
if bufPtrFour == nil {
|
||||
bufPtrFour = bufFour
|
||||
} else if bufPtrFour == bufFour {
|
||||
fmt.Println("bufFour was not properly redeclared")
|
||||
} else {
|
||||
fmt.Println("bufFour is properly redeclared")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bufOne is properly redeclared
|
||||
// bufTwo is properly redeclared
|
||||
// bufThree is properly redeclared
|
||||
// bufFour is properly redeclared
|
||||
22
_test/issue-1404.go
Normal file
22
_test/issue-1404.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type I interface {
|
||||
inI()
|
||||
}
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (t *T) inI() {}
|
||||
|
||||
func main() {
|
||||
var i I = &T{name: "foo"}
|
||||
|
||||
if i, ok := i.(*T); ok {
|
||||
println(i.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foo
|
||||
16
_test/issue-1408.go
Normal file
16
_test/issue-1408.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type (
|
||||
Number = int32
|
||||
Number2 = Number
|
||||
)
|
||||
|
||||
func f(n Number2) { println(n) }
|
||||
|
||||
func main() {
|
||||
var n Number = 5
|
||||
f(n)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
16
_test/issue-1411.go
Normal file
16
_test/issue-1411.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type Number int32
|
||||
|
||||
func (n Number) IsValid() bool { return true }
|
||||
|
||||
type Number1 = Number
|
||||
|
||||
type Number2 = Number1
|
||||
|
||||
func main() {
|
||||
a := Number2(5)
|
||||
println(a.IsValid())
|
||||
}
|
||||
|
||||
// Output: true
|
||||
9
_test/issue-1421.go
Normal file
9
_test/issue-1421.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
type Number = int
|
||||
|
||||
func main() {
|
||||
println(Number(1) < int(2))
|
||||
}
|
||||
|
||||
// Output: true
|
||||
25
_test/issue-435.go
Normal file
25
_test/issue-435.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Foo int
|
||||
|
||||
func (f Foo) String() string {
|
||||
return "foo-" + strconv.Itoa(int(f))
|
||||
}
|
||||
|
||||
func print1(arg interface{}) {
|
||||
fmt.Println(arg)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var arg Foo = 3
|
||||
var f = print1
|
||||
f(arg)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foo-3
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
@@ -36,7 +35,7 @@ type pipe struct {
|
||||
|
||||
func newReadAutoCloser(r io.Reader) readAutoCloser {
|
||||
if _, ok := r.(io.Closer); !ok {
|
||||
return readAutoCloser{ioutil.NopCloser(r)}
|
||||
return readAutoCloser{io.NopCloser(r)}
|
||||
}
|
||||
return readAutoCloser{r.(io.ReadCloser)}
|
||||
}
|
||||
@@ -44,7 +43,7 @@ func newReadAutoCloser(r io.Reader) readAutoCloser {
|
||||
func main() {
|
||||
p := &pipe{}
|
||||
p.Reader = newReadAutoCloser(strings.NewReader("test"))
|
||||
b, err := ioutil.ReadAll(p.Reader)
|
||||
b, err := io.ReadAll(p.Reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
14
_test/method38.go
Normal file
14
_test/method38.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/traefik/yaegi/_test/method38"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(method38.Get())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{[] {<nil>}}
|
||||
19
_test/method38/a.go
Normal file
19
_test/method38/a.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package method38
|
||||
|
||||
import "sync"
|
||||
|
||||
func NewPool() Pool { return Pool{} }
|
||||
|
||||
type Buffer struct {
|
||||
bs []byte
|
||||
pool Pool
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
var (
|
||||
_pool = NewPool()
|
||||
Get = _pool.Get
|
||||
)
|
||||
3
_test/method38/b.go
Normal file
3
_test/method38/b.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package method38
|
||||
|
||||
func (p Pool) Get() *Buffer { return &Buffer{} }
|
||||
32
_test/method39.go
Normal file
32
_test/method39.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/traefik/yaegi/_test/method38"
|
||||
)
|
||||
|
||||
func NewPool() Pool { return Pool{} }
|
||||
|
||||
type Buffer struct {
|
||||
bs []byte
|
||||
pool Pool
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
var (
|
||||
_pool = NewPool()
|
||||
Get = _pool.Get
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
fmt.Println(Get())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 17:11: undefined selector Get
|
||||
@@ -2,11 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
_, err := ioutil.ReadFile("__NotExisting__")
|
||||
_, err := os.ReadFile("__NotExisting__")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
9
_test/p2/p2.go
Normal file
9
_test/p2/p2.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package p2
|
||||
|
||||
type I interface {
|
||||
isI()
|
||||
}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (t *T) isI() {}
|
||||
0
_test/p3/empty
Normal file
0
_test/p3/empty
Normal file
@@ -8,6 +8,7 @@ type A struct {
|
||||
}
|
||||
|
||||
type D struct {
|
||||
F *A
|
||||
E *A
|
||||
}
|
||||
|
||||
|
||||
22
_test/struct61.go
Normal file
22
_test/struct61.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type A struct {
|
||||
B string
|
||||
D
|
||||
}
|
||||
|
||||
type D struct {
|
||||
*A
|
||||
E *A
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := &A{B: "b"}
|
||||
a.D = D{E: a}
|
||||
fmt.Println(a.D.E.B)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// b
|
||||
11
_test/struct62.go
Normal file
11
_test/struct62.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
type A struct{ *A }
|
||||
v := &A{}
|
||||
v.A = v
|
||||
println("v.A.A = v", v.A.A == v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// v.A.A = v true
|
||||
17
_test/type27.go
Normal file
17
_test/type27.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Foo = int
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return f * f
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 7:1: cannot define new methods on non-local type int
|
||||
17
_test/type28.go
Normal file
17
_test/type28.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Foo = int
|
||||
|
||||
func (f *Foo) Bar() int {
|
||||
return *f * *f
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 7:1: cannot define new methods on non-local type int
|
||||
12
_test/type29.go
Normal file
12
_test/type29.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var Foo int
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
12
_test/type30.go
Normal file
12
_test/type30.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var Foo *int
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
17
_test/type31.go
Normal file
17
_test/type31.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return f * f
|
||||
}
|
||||
|
||||
type Foo = int
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
17
_test/type32.go
Normal file
17
_test/type32.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (f *Foo) Bar() int {
|
||||
return *f * *f
|
||||
}
|
||||
|
||||
type Foo = int
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
11
_test/type33.go
Normal file
11
_test/type33.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func (f *Foo) Bar() int {
|
||||
return *f * *f
|
||||
}
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 3:1: undefined: Foo
|
||||
19
_test/var16.go
Normal file
19
_test/var16.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
func getArray() ([]int, error) { println("getArray"); return []int{1, 2}, nil }
|
||||
|
||||
func getNum() (int, error) { println("getNum"); return 3, nil }
|
||||
|
||||
func main() {
|
||||
if a, err := getNum(); err != nil {
|
||||
println("#1", a)
|
||||
} else if a, err := getArray(); err != nil {
|
||||
println("#2", a)
|
||||
}
|
||||
println("#3")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// getNum
|
||||
// getArray
|
||||
// #3
|
||||
@@ -18,12 +18,14 @@ func extractCmd(arg []string) error {
|
||||
var name string
|
||||
var exclude string
|
||||
var include string
|
||||
var tag string
|
||||
|
||||
eflag := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||
eflag.StringVar(&licensePath, "license", "", "path to a LICENSE file")
|
||||
eflag.StringVar(&name, "name", "", "the namespace for the extracted symbols")
|
||||
eflag.StringVar(&exclude, "exclude", "", "comma separated list of regexp matching symbols to exclude")
|
||||
eflag.StringVar(&include, "include", "", "comma separated list of regexp matching symbols to include")
|
||||
eflag.StringVar(&tag, "tag", "", "comma separated list of build tags to be added to the created package")
|
||||
eflag.Usage = func() {
|
||||
fmt.Println("Usage: yaegi extract [options] packages...")
|
||||
fmt.Println("Options:")
|
||||
@@ -56,6 +58,9 @@ func extractCmd(arg []string) error {
|
||||
Dest: name,
|
||||
License: license,
|
||||
}
|
||||
if tag != "" {
|
||||
ext.Tag = strings.Split(tag, ",")
|
||||
}
|
||||
|
||||
if exclude != "" {
|
||||
ext.Exclude = strings.Split(exclude, ",")
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -47,7 +46,12 @@ func run(arg []string) error {
|
||||
}
|
||||
args := rflag.Args()
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i := interp.New(interp.Options{
|
||||
GoPath: build.Default.GOPATH,
|
||||
BuildTags: strings.Split(tags, ","),
|
||||
Env: os.Environ(),
|
||||
Unrestricted: useUnrestricted,
|
||||
})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -130,7 +134,7 @@ func isFile(path string) bool {
|
||||
}
|
||||
|
||||
func runFile(i *interp.Interpreter, path string, noAutoImport bool) error {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -116,7 +116,12 @@ func test(arg []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i := interp.New(interp.Options{
|
||||
GoPath: build.Default.GOPATH,
|
||||
BuildTags: strings.Split(tags, ","),
|
||||
Env: os.Environ(),
|
||||
Unrestricted: useUnrestricted,
|
||||
})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -38,7 +37,7 @@ func applyCIMultiplier(timeout time.Duration) time.Duration {
|
||||
}
|
||||
|
||||
func TestYaegiCmdCancel(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "yaegi-")
|
||||
tmp, err := os.MkdirTemp("", "yaegi-")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tmp directory: %v", err)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ package fs1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
// only available from 1.16.
|
||||
"testing/fstest"
|
||||
"testing/fstest" // only available from 1.16.
|
||||
|
||||
"github.com/traefik/yaegi/interp"
|
||||
"github.com/traefik/yaegi/stdlib"
|
||||
|
||||
@@ -80,7 +80,14 @@ func init() {
|
||||
{{end}}
|
||||
}
|
||||
{{range $m := $value.Method -}}
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} { {{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}} }
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} {
|
||||
{{- if eq $m.Name "String"}}
|
||||
if W.WString == nil {
|
||||
return ""
|
||||
}
|
||||
{{end -}}
|
||||
{{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}}
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
@@ -187,6 +194,11 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
|
||||
case *types.Var:
|
||||
val[name] = Val{pname, true}
|
||||
case *types.TypeName:
|
||||
// Skip type if it is generic.
|
||||
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 {
|
||||
var methods []Method
|
||||
@@ -270,6 +282,15 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range e.Tag {
|
||||
if len(t) != 0 {
|
||||
buildTags += "," + t
|
||||
}
|
||||
}
|
||||
if len(buildTags) != 0 && buildTags[0] == ',' {
|
||||
buildTags = buildTags[1:]
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
data := map[string]interface{}{
|
||||
"Dest": e.Dest,
|
||||
@@ -336,6 +357,7 @@ type Extractor struct {
|
||||
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
|
||||
@@ -446,7 +468,7 @@ func GetMinor(part string) string {
|
||||
return minor
|
||||
}
|
||||
|
||||
const defaultMinorVersion = 17
|
||||
const defaultMinorVersion = 19
|
||||
|
||||
func genBuildTags() (string, error) {
|
||||
version := runtime.Version()
|
||||
|
||||
@@ -3,8 +3,8 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
@@ -535,6 +535,7 @@ func {{$name}}(n *node) {
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
{{- if or (eq $op.Name "==") (eq $op.Name "!=") }}
|
||||
|
||||
@@ -557,7 +558,7 @@ func {{$name}}(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -579,7 +580,7 @@ func {{$name}}(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -602,7 +603,7 @@ func {{$name}}(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -621,9 +622,39 @@ func {{$name}}(n *node) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Do not attempt to optimize '==' or '!=' if an operand is an interface.
|
||||
// This will preserve proper dynamic type checking at runtime. For static types,
|
||||
// type checks are already performed, so bypass them if possible.
|
||||
if t0.Kind() == reflect.Interface || t1.Kind() == reflect.Interface {
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -1183,7 +1214,7 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile("op.go", source, 0666); err != nil {
|
||||
if err = os.WriteFile("op.go", source, 0o666); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,17 @@ 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/reflect/type.go.
|
||||
|
||||
type rtype struct {
|
||||
_ [48]byte
|
||||
_ uintptr
|
||||
_ uintptr
|
||||
_ uint32
|
||||
_ uint32
|
||||
_ uintptr
|
||||
_ uintptr
|
||||
_ uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type emptyInterface struct {
|
||||
@@ -20,21 +29,21 @@ type emptyInterface struct {
|
||||
}
|
||||
|
||||
type structField struct {
|
||||
_ int64
|
||||
_ uintptr
|
||||
typ *rtype
|
||||
_ uintptr
|
||||
}
|
||||
|
||||
type structType struct {
|
||||
rtype
|
||||
_ int64
|
||||
_ uintptr
|
||||
fields []structField
|
||||
}
|
||||
|
||||
// SwapFieldType swaps the type of the struct field with the given type.
|
||||
// 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 SwapFieldType(s reflect.Type, idx int, t reflect.Type) {
|
||||
func SetFieldType(s reflect.Type, idx int, t reflect.Type) {
|
||||
if s.Kind() != reflect.Struct || idx >= s.NumField() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestSwapFieldType(t *testing.T) {
|
||||
typ := reflect.StructOf(f)
|
||||
ntyp := reflect.PtrTo(typ)
|
||||
|
||||
unsafe2.SwapFieldType(typ, 1, ntyp)
|
||||
unsafe2.SetFieldType(typ, 1, ntyp)
|
||||
|
||||
if typ.Field(1).Type != ntyp {
|
||||
t.Fatalf("unexpected field type: want %s; got %s", ntyp, typ.Field(1).Type)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
@@ -73,6 +72,7 @@ const (
|
||||
importSpec
|
||||
incDecStmt
|
||||
indexExpr
|
||||
indexListExpr
|
||||
interfaceType
|
||||
keyValueExpr
|
||||
labeledStmt
|
||||
@@ -93,7 +93,8 @@ const (
|
||||
switchIfStmt
|
||||
typeAssertExpr
|
||||
typeDecl
|
||||
typeSpec
|
||||
typeSpec // type A int
|
||||
typeSpecAssign // type A = int
|
||||
typeSwitch
|
||||
unaryExpr
|
||||
valueSpec
|
||||
@@ -155,6 +156,7 @@ var kinds = [...]string{
|
||||
importSpec: "importSpec",
|
||||
incDecStmt: "incDecStmt",
|
||||
indexExpr: "indexExpr",
|
||||
indexListExpr: "indexListExpr",
|
||||
interfaceType: "interfaceType",
|
||||
keyValueExpr: "keyValueExpr",
|
||||
labeledStmt: "labeledStmt",
|
||||
@@ -176,6 +178,7 @@ var kinds = [...]string{
|
||||
typeAssertExpr: "typeAssertExpr",
|
||||
typeDecl: "typeDecl",
|
||||
typeSpec: "typeSpec",
|
||||
typeSpecAssign: "typeSpecAssign",
|
||||
typeSwitch: "typeSwitch",
|
||||
unaryExpr: "unaryExpr",
|
||||
valueSpec: "valueSpec",
|
||||
@@ -281,6 +284,7 @@ var actions = [...]string{
|
||||
aDec: "--",
|
||||
aEqual: "==",
|
||||
aGreater: ">",
|
||||
aGreaterEqual: ">=",
|
||||
aGetFunc: "getFunc",
|
||||
aGetIndex: "getIndex",
|
||||
aGetMethod: "getMethod",
|
||||
@@ -289,6 +293,7 @@ var actions = [...]string{
|
||||
aLand: "&&",
|
||||
aLor: "||",
|
||||
aLower: "<",
|
||||
aLowerEqual: "<=",
|
||||
aMethod: "Method",
|
||||
aMul: "*",
|
||||
aMulAssign: "*=",
|
||||
@@ -360,21 +365,14 @@ func wrapInMain(src string) string {
|
||||
return fmt.Sprintf("package main; func main() {%s\n}", src)
|
||||
}
|
||||
|
||||
// Note: no type analysis is performed at this stage, it is done in pre-order
|
||||
// processing of CFG, in order to accommodate forward type declarations.
|
||||
|
||||
// ast parses src string containing Go code and generates the corresponding AST.
|
||||
// The package name and the AST root node are returned.
|
||||
// The given name is used to set the filename of the relevant source file in the
|
||||
// interpreter's FileSet.
|
||||
func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error) {
|
||||
var inFunc bool
|
||||
func (interp *Interpreter) parse(src, name string, inc bool) (node ast.Node, err error) {
|
||||
mode := parser.DeclarationErrors
|
||||
|
||||
// Allow incremental parsing of declarations or statements, by inserting
|
||||
// them in a pseudo file package or function. Those statements or
|
||||
// declarations will be always evaluated in the global scope.
|
||||
var tok token.Token
|
||||
var inFunc bool
|
||||
if inc {
|
||||
tok = interp.firstToken(src)
|
||||
switch tok {
|
||||
@@ -391,18 +389,18 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
}
|
||||
|
||||
if ok, err := interp.buildOk(&interp.context, name, src); !ok || err != nil {
|
||||
return "", nil, err // skip source not matching build constraints
|
||||
return nil, err // skip source not matching build constraints
|
||||
}
|
||||
|
||||
f, err := parser.ParseFile(interp.fset, name, src, mode)
|
||||
if err != nil {
|
||||
// only retry if we're on an expression/statement about a func
|
||||
if !inc || tok != token.FUNC {
|
||||
return "", nil, err
|
||||
return nil, err
|
||||
}
|
||||
// do not bother retrying if we know it's an error we're going to ignore later on.
|
||||
if ignoreError(err, src) {
|
||||
return "", nil, err
|
||||
return nil, err
|
||||
}
|
||||
// do not lose initial error, in case retrying fails.
|
||||
initialError := err
|
||||
@@ -410,16 +408,32 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
src := wrapInMain(strings.TrimPrefix(src, "package main;"))
|
||||
f, err = parser.ParseFile(interp.fset, name, src, mode)
|
||||
if err != nil {
|
||||
return "", nil, initialError
|
||||
return nil, initialError
|
||||
}
|
||||
}
|
||||
|
||||
setYaegiTags(&interp.context, f.Comments)
|
||||
if inFunc {
|
||||
// return the body of the wrapper main function
|
||||
return f.Decls[0].(*ast.FuncDecl).Body, nil
|
||||
}
|
||||
|
||||
setYaegiTags(&interp.context, f.Comments)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Note: no type analysis is performed at this stage, it is done in pre-order
|
||||
// processing of CFG, in order to accommodate forward type declarations.
|
||||
|
||||
// ast parses src string containing Go code and generates the corresponding AST.
|
||||
// The package name and the AST root node are returned.
|
||||
// The given name is used to set the filename of the relevant source file in the
|
||||
// interpreter's FileSet.
|
||||
func (interp *Interpreter) ast(f ast.Node) (string, *node, error) {
|
||||
var err error
|
||||
var root *node
|
||||
var anc astNode
|
||||
var st nodestack
|
||||
var pkgName string
|
||||
pkgName := "main"
|
||||
|
||||
addChild := func(root **node, anc astNode, pos token.Pos, kind nkind, act action) *node {
|
||||
var i interface{}
|
||||
@@ -627,7 +641,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
}
|
||||
st.push(addChild(&root, anc, pos, kind, aNop), nod)
|
||||
|
||||
case *ast.CommentGroup:
|
||||
case *ast.CommentGroup, *ast.EmptyStmt:
|
||||
return false
|
||||
|
||||
case *ast.CompositeLit:
|
||||
@@ -682,7 +696,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
n := addChild(&root, anc, pos, funcDecl, aNop)
|
||||
n.val = n
|
||||
if a.Recv == nil {
|
||||
// function is not a method, create an empty receiver list
|
||||
// Function is not a method, create an empty receiver list.
|
||||
addChild(&root, astNode{n, nod}, pos, fieldList, aNop)
|
||||
}
|
||||
st.push(n, nod)
|
||||
@@ -694,7 +708,13 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
st.push(n, nod)
|
||||
|
||||
case *ast.FuncType:
|
||||
st.push(addChild(&root, anc, pos, funcType, aNop), nod)
|
||||
n := addChild(&root, anc, pos, funcType, aNop)
|
||||
n.val = n
|
||||
if a.TypeParams == nil {
|
||||
// Function has no type parameters, create an empty fied list.
|
||||
addChild(&root, astNode{n, nod}, pos, fieldList, aNop)
|
||||
}
|
||||
st.push(n, nod)
|
||||
|
||||
case *ast.GenDecl:
|
||||
var kind nkind
|
||||
@@ -764,6 +784,9 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
case *ast.IndexExpr:
|
||||
st.push(addChild(&root, anc, pos, indexExpr, aGetIndex), nod)
|
||||
|
||||
case *ast.IndexListExpr:
|
||||
st.push(addChild(&root, anc, pos, indexListExpr, aNop), nod)
|
||||
|
||||
case *ast.InterfaceType:
|
||||
st.push(addChild(&root, anc, pos, interfaceType, aNop), nod)
|
||||
|
||||
@@ -826,6 +849,10 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
st.push(addChild(&root, anc, pos, typeAssertExpr, aTypeAssert), nod)
|
||||
|
||||
case *ast.TypeSpec:
|
||||
if a.Assign.IsValid() {
|
||||
st.push(addChild(&root, anc, pos, typeSpecAssign, aNop), nod)
|
||||
break
|
||||
}
|
||||
st.push(addChild(&root, anc, pos, typeSpec, aNop), nod)
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
@@ -892,15 +919,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
}
|
||||
return true
|
||||
})
|
||||
if inFunc {
|
||||
// Incremental parsing: statements were inserted in a pseudo function.
|
||||
// Set root to function body so its statements are evaluated in global scope.
|
||||
root = root.child[1].child[3]
|
||||
root.anc = nil
|
||||
}
|
||||
if pkgName == "" {
|
||||
return "", root, errors.New("no package name found")
|
||||
}
|
||||
|
||||
interp.roots = append(interp.roots, root)
|
||||
return pkgName, root, err
|
||||
}
|
||||
|
||||
546
interp/cfg.go
546
interp/cfg.go
@@ -49,8 +49,10 @@ const nilIdent = "nil"
|
||||
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
||||
// variables. A list of nodes of init functions is returned.
|
||||
// Following this pass, the CFG is ready to run.
|
||||
func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(importPath, pkgName)
|
||||
func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string) ([]*node, error) {
|
||||
if sc == nil {
|
||||
sc = interp.initScopePkg(importPath, pkgName)
|
||||
}
|
||||
check := typecheck{scope: sc}
|
||||
var initNodes []*node
|
||||
var err error
|
||||
@@ -62,6 +64,9 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if n.scope == nil {
|
||||
n.scope = sc
|
||||
}
|
||||
switch n.kind {
|
||||
case binaryExpr, unaryExpr, parenExpr:
|
||||
if isBoolAction(n) {
|
||||
@@ -140,8 +145,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
|
||||
switch o.typ.cat {
|
||||
case valueT:
|
||||
case valueT, aliasT:
|
||||
typ := o.typ.rtype
|
||||
if o.typ.cat == aliasT {
|
||||
typ = o.typ.val.TypeOf()
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Map:
|
||||
n.anc.gen = rangeMap
|
||||
@@ -197,38 +205,42 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n.findex = -1
|
||||
n.val = nil
|
||||
sc = sc.pushBloc()
|
||||
// Pre-define symbols for labels defined in this block, so we are sure that
|
||||
// they are already defined when met.
|
||||
// TODO(marc): labels must be stored outside of symbols to avoid collisions.
|
||||
for _, c := range n.child {
|
||||
if c.kind != labeledStmt {
|
||||
continue
|
||||
}
|
||||
label := c.child[0].ident
|
||||
sym := &symbol{kind: labelSym, node: c, index: -1}
|
||||
sc.sym[label] = sym
|
||||
c.sym = sym
|
||||
}
|
||||
|
||||
case breakStmt, continueStmt, gotoStmt:
|
||||
if len(n.child) > 0 {
|
||||
// Handle labeled statements.
|
||||
label := n.child[0].ident
|
||||
if sym, _, ok := sc.lookup(label); ok {
|
||||
if sym.kind != labelSym {
|
||||
err = n.child[0].cfgErrorf("label %s not defined", label)
|
||||
break
|
||||
}
|
||||
sym.from = append(sym.from, n)
|
||||
n.sym = sym
|
||||
} else {
|
||||
n.sym = &symbol{kind: labelSym, from: []*node{n}, index: -1}
|
||||
sc.sym[label] = n.sym
|
||||
}
|
||||
if len(n.child) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
case labeledStmt:
|
||||
// Handle labeled statements.
|
||||
label := n.child[0].ident
|
||||
// TODO(marc): labels must be stored outside of symbols to avoid collisions
|
||||
// Used labels are searched in current and sub scopes, not upper ones.
|
||||
if sym, ok := sc.lookdown(label); ok {
|
||||
sym.node = n
|
||||
if sym, _, ok := sc.lookup(label); ok {
|
||||
if sym.kind != labelSym {
|
||||
err = n.child[0].cfgErrorf("label %s not defined", label)
|
||||
break
|
||||
}
|
||||
n.sym = sym
|
||||
} else {
|
||||
n.sym = &symbol{kind: labelSym, node: n, index: -1}
|
||||
n.sym = &symbol{kind: labelSym, index: -1}
|
||||
sc.sym[label] = n.sym
|
||||
}
|
||||
if n.kind == gotoStmt {
|
||||
n.sym.from = append(n.sym.from, n) // To allow forward goto statements.
|
||||
}
|
||||
sc.sym[label] = n.sym
|
||||
|
||||
case caseClause:
|
||||
sc = sc.pushBloc()
|
||||
@@ -284,14 +296,14 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
|
||||
case compositeLitExpr:
|
||||
if len(n.child) > 0 && n.child[0].isType(sc) {
|
||||
// Get type from 1st child
|
||||
// Get type from 1st child.
|
||||
if n.typ, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return false
|
||||
}
|
||||
// Indicate that the first child is the type
|
||||
// Indicate that the first child is the type.
|
||||
n.nleft = 1
|
||||
} else {
|
||||
// Get type from ancestor (implicit type)
|
||||
// Get type from ancestor (implicit type).
|
||||
if n.anc.kind == keyValueExpr && n == n.anc.child[0] {
|
||||
n.typ = n.anc.typ.key
|
||||
} else if atyp := n.anc.typ; atyp != nil {
|
||||
@@ -314,6 +326,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
// Propagate type to children, to handle implicit types
|
||||
for _, c := range child {
|
||||
if isBlank(c) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
return false
|
||||
}
|
||||
switch c.kind {
|
||||
case binaryExpr, unaryExpr, compositeLitExpr:
|
||||
// Do not attempt to propagate composite type to operator expressions,
|
||||
@@ -344,16 +360,55 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
fallthrough
|
||||
|
||||
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).")
|
||||
return false
|
||||
}
|
||||
n.val = n
|
||||
|
||||
// Skip substree in case of a generic function.
|
||||
if len(n.child[2].child[0].child) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Skip subtree if the function is a method with a generic receiver.
|
||||
if len(n.child[0].child) > 0 {
|
||||
recvTypeNode := n.child[0].child[0].lastChild()
|
||||
typ, err := nodeType(interp, sc, recvTypeNode)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if typ.cat == genericT || (typ.val != nil && typ.val.cat == genericT) {
|
||||
return false
|
||||
}
|
||||
if typ.cat == ptrT {
|
||||
rc0 := recvTypeNode.child[0]
|
||||
rt0, err := nodeType(interp, sc, rc0)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if rc0.kind == indexExpr && rt0.cat == structT {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute function type before entering local scope to avoid
|
||||
// possible collisions with function argument names.
|
||||
n.child[2].typ, err = nodeType(interp, sc, n.child[2])
|
||||
// Add a frame indirection level as we enter in a func
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
n.typ = n.child[2].typ
|
||||
|
||||
// Add a frame indirection level as we enter in a func.
|
||||
sc = sc.pushFunc()
|
||||
sc.def = n
|
||||
if len(n.child[2].child) == 2 {
|
||||
// Allocate frame space for return values, define output symbols
|
||||
for _, c := range n.child[2].child[1].child {
|
||||
|
||||
// Allocate frame space for return values, define output symbols.
|
||||
if len(n.child[2].child) == 3 {
|
||||
for _, c := range n.child[2].child[2].child {
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, c.lastChild()); err != nil {
|
||||
return false
|
||||
@@ -367,8 +422,9 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define receiver symbol.
|
||||
if len(n.child[0].child) > 0 {
|
||||
// define receiver symbol
|
||||
var typ *itype
|
||||
fr := n.child[0].child[0]
|
||||
recvTypeNode := fr.lastChild()
|
||||
@@ -383,8 +439,9 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
sc.sym[fr.child[0].ident] = &symbol{index: index, kind: varSym, typ: typ}
|
||||
}
|
||||
}
|
||||
for _, c := range n.child[2].child[0].child {
|
||||
// define input parameter symbols
|
||||
|
||||
// Define input parameter symbols.
|
||||
for _, c := range n.child[2].child[1].child {
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, c.lastChild()); err != nil {
|
||||
return false
|
||||
@@ -393,6 +450,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
sc.sym[cc.ident] = &symbol{index: sc.add(typ), kind: varSym, typ: typ}
|
||||
}
|
||||
}
|
||||
|
||||
if n.child[1].ident == "init" && len(n.child[0].child) == 0 {
|
||||
initNodes = append(initNodes, n)
|
||||
}
|
||||
@@ -410,11 +468,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
sc.loop = n
|
||||
|
||||
case importSpec:
|
||||
// already all done in gta
|
||||
// Already all done in GTA.
|
||||
return false
|
||||
|
||||
case typeSpec:
|
||||
// processing already done in GTA pass for global types, only parses inlined types
|
||||
// Processing already done in GTA pass for global types, only parses inlined types.
|
||||
if sc.def == nil {
|
||||
return false
|
||||
}
|
||||
@@ -424,8 +482,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
return false
|
||||
}
|
||||
if typ.incomplete {
|
||||
err = n.cfgErrorf("invalid type declaration")
|
||||
return false
|
||||
// Type may still be incomplete in case of a local recursive struct declaration.
|
||||
if typ, err = typ.finalize(); err != nil {
|
||||
err = n.cfgErrorf("invalid type declaration")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
switch n.child[1].kind {
|
||||
@@ -443,7 +504,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
// values which may be used in further declarations.
|
||||
if !sc.global {
|
||||
for _, c := range n.child {
|
||||
if _, err = interp.cfg(c, importPath, pkgName); err != nil {
|
||||
if _, err = interp.cfg(c, sc, importPath, pkgName); err != nil {
|
||||
// No error processing here, to allow recovery in subtree nodes.
|
||||
err = nil
|
||||
}
|
||||
@@ -470,6 +531,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
|
||||
switch n.kind {
|
||||
case addressExpr:
|
||||
if isBlank(n.child[0]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
err = check.addressExpr(n)
|
||||
@@ -505,6 +570,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
updateSym := false
|
||||
var sym *symbol
|
||||
var level int
|
||||
|
||||
if isBlank(src) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
if n.kind == defineStmt || (n.kind == assignStmt && dest.ident == "_") {
|
||||
if atyp != nil {
|
||||
dest.typ = atyp
|
||||
@@ -594,8 +664,8 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
// Skip optimisation for assigned interface.
|
||||
break
|
||||
}
|
||||
if dest.action == aGetIndex {
|
||||
// Skip optimization, as it does not work when assigning to a struct field.
|
||||
if dest.action == aGetIndex || dest.action == aStar {
|
||||
// Skip optimization, as it does not work when assigning to a struct field or a dereferenced pointer.
|
||||
break
|
||||
}
|
||||
n.gen = nop
|
||||
@@ -637,6 +707,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
|
||||
case incDecStmt:
|
||||
err = check.unaryExpr(n)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
n.findex = n.child[0].findex
|
||||
n.level = n.child[0].level
|
||||
@@ -657,7 +731,12 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
if r := lc.child[0].typ.numOut(); r != l {
|
||||
err = n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, lc.child[0].name(), r)
|
||||
}
|
||||
n.gen = nop
|
||||
if isBinCall(lc, sc) {
|
||||
n.gen = nop
|
||||
} else {
|
||||
// TODO (marc): skip if no conversion or wrapping is needed.
|
||||
n.gen = assignFromCall
|
||||
}
|
||||
case indexExpr:
|
||||
lc.gen = getIndexMap2
|
||||
n.gen = nop
|
||||
@@ -750,15 +829,16 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
|
||||
case indexExpr:
|
||||
if isBlank(n.child[0]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
t := n.child[0].typ
|
||||
for t.cat == aliasT {
|
||||
t = t.val
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
if isString(t.val.TypeOf()) {
|
||||
n.typ = sc.getType("byte")
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case ptrT:
|
||||
n.typ = t.val
|
||||
if t.val.cat == valueT {
|
||||
@@ -774,6 +854,49 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
} else {
|
||||
n.typ = valueTOf(t.rtype.Elem())
|
||||
}
|
||||
case funcT:
|
||||
// A function indexed by a type means an instantiated generic function.
|
||||
c1 := n.child[1]
|
||||
if !c1.isType(sc) {
|
||||
n.typ = t
|
||||
return
|
||||
}
|
||||
g, err := genAST(sc, t.node.anc, []*node{c1})
|
||||
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
|
||||
}
|
||||
// Replace generic func node by instantiated one.
|
||||
n.anc.child[childPos(n)] = g
|
||||
n.typ = g.typ
|
||||
return
|
||||
case genericT:
|
||||
name := t.id() + "[" + n.child[1].typ.id() + "]"
|
||||
sym, _, ok := sc.lookup(name)
|
||||
if !ok {
|
||||
err = n.cfgErrorf("type not found: %s", name)
|
||||
return
|
||||
}
|
||||
n.gen = nop
|
||||
n.typ = sym.typ
|
||||
return
|
||||
case structT:
|
||||
// A struct indexed by a Type means an instantiated generic struct.
|
||||
name := t.name + "[" + n.child[1].ident + "]"
|
||||
sym, _, ok := sc.lookup(name)
|
||||
if ok {
|
||||
n.typ = sym.typ
|
||||
n.findex = sc.add(n.typ)
|
||||
n.gen = nop
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
n.typ = t.val
|
||||
}
|
||||
@@ -840,32 +963,88 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
n.rval = l.rval
|
||||
|
||||
case breakStmt:
|
||||
if len(n.child) > 0 {
|
||||
gotoLabel(n.sym)
|
||||
} else {
|
||||
if len(n.child) == 0 {
|
||||
n.tnext = sc.loop
|
||||
break
|
||||
}
|
||||
if !n.hasAnc(n.sym.node) {
|
||||
err = n.cfgErrorf("invalid break label %s", n.child[0].ident)
|
||||
break
|
||||
}
|
||||
n.tnext = n.sym.node
|
||||
|
||||
case continueStmt:
|
||||
if len(n.child) > 0 {
|
||||
gotoLabel(n.sym)
|
||||
} else {
|
||||
if len(n.child) == 0 {
|
||||
n.tnext = sc.loopRestart
|
||||
break
|
||||
}
|
||||
if !n.hasAnc(n.sym.node) {
|
||||
err = n.cfgErrorf("invalid continue label %s", n.child[0].ident)
|
||||
break
|
||||
}
|
||||
n.tnext = n.sym.node.child[1].lastChild().start
|
||||
|
||||
case gotoStmt:
|
||||
gotoLabel(n.sym)
|
||||
if n.sym.node == nil {
|
||||
// It can be only due to a forward goto, to be resolved at labeledStmt.
|
||||
// Invalid goto labels are catched at AST parsing.
|
||||
break
|
||||
}
|
||||
n.tnext = n.sym.node.start
|
||||
|
||||
case labeledStmt:
|
||||
wireChild(n)
|
||||
n.start = n.child[1].start
|
||||
gotoLabel(n.sym)
|
||||
if len(n.child) > 1 {
|
||||
n.start = n.child[1].start
|
||||
}
|
||||
for _, c := range n.sym.from {
|
||||
c.tnext = n.start // Resolve forward goto.
|
||||
}
|
||||
|
||||
case callExpr:
|
||||
for _, c := range n.child {
|
||||
if isBlank(c) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
return
|
||||
}
|
||||
}
|
||||
wireChild(n)
|
||||
switch {
|
||||
switch c0 := n.child[0]; {
|
||||
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:])
|
||||
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
|
||||
}
|
||||
n.child[0] = g
|
||||
c0 = n.child[0]
|
||||
wireChild(n)
|
||||
if typ := c0.typ; len(typ.ret) > 0 {
|
||||
n.typ = typ.ret[0]
|
||||
if n.anc.kind == returnStmt && n.typ.id() == sc.def.typ.ret[0].id() {
|
||||
// Store the result directly to the return value area of frame.
|
||||
// It can be done only if no type conversion at return is involved.
|
||||
n.findex = childPos(n)
|
||||
} else {
|
||||
n.findex = sc.add(n.typ)
|
||||
for _, t := range typ.ret[1:] {
|
||||
sc.add(t)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n.findex = notInFrame
|
||||
}
|
||||
|
||||
case isBuiltinCall(n, sc):
|
||||
c0 := n.child[0]
|
||||
bname := c0.ident
|
||||
err = check.builtin(bname, n, n.child[1:], n.action == aCallSlice)
|
||||
if err != nil {
|
||||
@@ -885,7 +1064,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
// Store result directly to frame output location, to avoid a frame copy.
|
||||
n.findex = 0
|
||||
case bname == "cap" && isInConstOrTypeDecl(n):
|
||||
switch n.child[1].typ.TypeOf().Kind() {
|
||||
t := n.child[1].typ.TypeOf()
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan:
|
||||
capConst(n)
|
||||
default:
|
||||
@@ -894,7 +1077,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
n.findex = notInFrame
|
||||
n.gen = nop
|
||||
case bname == "len" && isInConstOrTypeDecl(n):
|
||||
switch n.child[1].typ.TypeOf().Kind() {
|
||||
t := n.child[1].typ.TypeOf()
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.String:
|
||||
lenConst(n)
|
||||
default:
|
||||
@@ -908,9 +1095,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
if op, ok := constBltn[bname]; ok && n.anc.action != aAssign {
|
||||
op(n) // pre-compute non-assigned constant :
|
||||
}
|
||||
case n.child[0].isType(sc):
|
||||
|
||||
case c0.isType(sc):
|
||||
// Type conversion expression
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
c1 := n.child[1]
|
||||
switch len(n.child) {
|
||||
case 1:
|
||||
err = n.cfgErrorf("missing argument in conversion to %s", c0.typ.id())
|
||||
@@ -955,16 +1143,17 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
n.typ = c0.typ
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
case isBinCall(n):
|
||||
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
|
||||
|
||||
case isBinCall(n, sc):
|
||||
err = check.arguments(n, n.child[1:], c0, n.action == aCallSlice)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
n.gen = callBin
|
||||
typ := n.child[0].typ.rtype
|
||||
typ := c0.typ.rtype
|
||||
if typ.NumOut() > 0 {
|
||||
if funcType := n.child[0].typ.val; funcType != nil {
|
||||
if funcType := c0.typ.val; funcType != nil {
|
||||
// Use the original unwrapped function type, to allow future field and
|
||||
// methods resolutions, otherwise impossible on the opaque bin type.
|
||||
n.typ = funcType.ret[0]
|
||||
@@ -984,7 +1173,8 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
}
|
||||
}
|
||||
case isOffsetof(n):
|
||||
|
||||
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
|
||||
@@ -998,17 +1188,45 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
n.typ = valueTOf(reflect.TypeOf(field.Offset))
|
||||
n.rval = reflect.ValueOf(field.Offset)
|
||||
n.gen = nop
|
||||
|
||||
default:
|
||||
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
|
||||
// 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
|
||||
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
n.child[0] = g
|
||||
c0 = n.child[0]
|
||||
}
|
||||
|
||||
err = check.arguments(n, n.child[1:], c0, n.action == aCallSlice)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if n.child[0].action == aGetFunc {
|
||||
if c0.action == aGetFunc {
|
||||
// Allocate a frame entry to store the anonymous function definition.
|
||||
sc.add(n.child[0].typ)
|
||||
sc.add(c0.typ)
|
||||
}
|
||||
if typ := n.child[0].typ; len(typ.ret) > 0 {
|
||||
if typ := c0.typ; len(typ.ret) > 0 {
|
||||
n.typ = typ.ret[0]
|
||||
if n.anc.kind == returnStmt && n.typ.id() == sc.def.typ.ret[0].id() {
|
||||
// Store the result directly to the return value area of frame.
|
||||
@@ -1226,7 +1444,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
n.types, n.scope = sc.types, sc
|
||||
sc = sc.pop()
|
||||
funcName := n.child[1].ident
|
||||
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil {
|
||||
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil && !isGeneric(sym.typ) {
|
||||
sym.index = -1 // to force value to n.val
|
||||
sym.typ = n.typ
|
||||
sym.kind = funcSym
|
||||
@@ -1369,9 +1587,17 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
sc = sc.pop()
|
||||
|
||||
case keyValueExpr:
|
||||
if isBlank(n.child[1]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
case landExpr:
|
||||
if isBlank(n.child[0]) || isBlank(n.child[1]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = n.child[1].start
|
||||
setFNext(n.child[0], n)
|
||||
@@ -1383,6 +1609,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
|
||||
case lorExpr:
|
||||
if isBlank(n.child[0]) || isBlank(n.child[1]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = n
|
||||
setFNext(n.child[0], n.child[1].start)
|
||||
@@ -1428,6 +1658,12 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
err = n.cfgErrorf("too many arguments to return")
|
||||
break
|
||||
}
|
||||
for _, c := range n.child {
|
||||
if isBlank(c) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
return
|
||||
}
|
||||
}
|
||||
returnSig := sc.def.child[2]
|
||||
if mustReturnValue(returnSig) {
|
||||
nret := len(n.child)
|
||||
@@ -1444,7 +1680,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
n.val = sc.def
|
||||
for i, c := range n.child {
|
||||
var typ *itype
|
||||
typ, err = nodeType(interp, sc.upperLevel(), returnSig.child[1].fieldType(i))
|
||||
typ, err = nodeType(interp, sc.upperLevel(), returnSig.child[2].fieldType(i))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -1575,7 +1811,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
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.rtype.Kind() != reflect.Interface
|
||||
hasRecvType := n.typ.TypeOf().Kind() != reflect.Interface
|
||||
n.val = method.Index
|
||||
n.gen = getIndexBinMethod
|
||||
n.action = aGetMethod
|
||||
@@ -1584,7 +1820,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
if hasRecvType {
|
||||
n.typ.recv = n.typ
|
||||
}
|
||||
case n.typ.rtype.Kind() == reflect.Ptr:
|
||||
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
|
||||
@@ -1592,7 +1828,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
break
|
||||
}
|
||||
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
|
||||
case n.typ.rtype.Kind() == reflect.Struct:
|
||||
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
|
||||
@@ -1637,7 +1873,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
} 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
|
||||
// Handle method as a function with receiver in 1st argument.
|
||||
n.val = m
|
||||
n.findex = notInFrame
|
||||
n.gen = nop
|
||||
@@ -1645,7 +1881,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
*n.typ = *m.typ
|
||||
n.typ.arg = append([]*itype{n.child[0].typ}, m.typ.arg...)
|
||||
} else {
|
||||
// Handle method with receiver
|
||||
// Handle method with receiver.
|
||||
n.gen = getMethod
|
||||
n.val = m
|
||||
n.typ = m.typ
|
||||
@@ -1719,6 +1955,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
|
||||
case starExpr:
|
||||
if isBlank(n.child[0]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
switch {
|
||||
case n.anc.kind == defineStmt && len(n.anc.child) == 3 && n.anc.child[1] == n:
|
||||
// pointer type expression in a var definition
|
||||
@@ -1809,6 +2049,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
setFNext(c, clauses[i+1])
|
||||
}
|
||||
}
|
||||
sbn.start = clauses[0].start
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
|
||||
@@ -1863,6 +2104,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
|
||||
wireChild(n)
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
if isBlank(c0) || isBlank(c1) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
if c1.typ == nil {
|
||||
if c1.typ, err = nodeType(interp, sc, c1); err != nil {
|
||||
return
|
||||
@@ -1998,13 +2243,18 @@ func compDefineX(sc *scope, n *node) error {
|
||||
} else {
|
||||
types = funtype.ret
|
||||
}
|
||||
if n.child[l-1].isType(sc) {
|
||||
if n.anc.kind == varDecl && n.child[l-1].isType(sc) {
|
||||
l--
|
||||
}
|
||||
if len(types) != l {
|
||||
return n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, src.child[0].name(), len(types))
|
||||
}
|
||||
n.gen = nop
|
||||
if isBinCall(src, sc) {
|
||||
n.gen = nop
|
||||
} else {
|
||||
// TODO (marc): skip if no conversion or wrapping is needed.
|
||||
n.gen = assignFromCall
|
||||
}
|
||||
|
||||
case indexExpr:
|
||||
types = append(types, src.typ, sc.getType("bool"))
|
||||
@@ -2032,12 +2282,18 @@ func compDefineX(sc *scope, n *node) error {
|
||||
}
|
||||
|
||||
for i, t := range types {
|
||||
index := sc.add(t)
|
||||
sc.sym[n.child[i].ident] = &symbol{index: index, kind: varSym, typ: t}
|
||||
var index int
|
||||
id := n.child[i].ident
|
||||
if sym, level, ok := sc.lookup(id); ok && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() {
|
||||
// Reuse symbol in case of a variable redeclaration with the same type.
|
||||
index = sym.index
|
||||
} else {
|
||||
index = sc.add(t)
|
||||
sc.sym[id] = &symbol{index: index, kind: varSym, typ: t}
|
||||
}
|
||||
n.child[i].typ = t
|
||||
n.child[i].findex = index
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2241,6 +2497,10 @@ func (n *node) isType(sc *scope) bool {
|
||||
}
|
||||
case identExpr:
|
||||
return sc.getType(n.ident) != nil
|
||||
case indexExpr:
|
||||
// Maybe a generic type.
|
||||
sym, _, ok := sc.lookup(n.child[0].ident)
|
||||
return ok && sym.kind == typeSym
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -2391,6 +2651,15 @@ func (n *node) fieldType(m int) *node {
|
||||
// lastChild returns the last child of a node.
|
||||
func (n *node) lastChild() *node { return n.child[len(n.child)-1] }
|
||||
|
||||
func (n *node) hasAnc(nod *node) bool {
|
||||
for a := n.anc; a != nil; a = a.anc {
|
||||
if a == nod {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isKey(n *node) bool {
|
||||
return n.anc.kind == fileStmt ||
|
||||
(n.anc.kind == selectorExpr && n.anc.child[0] != n) ||
|
||||
@@ -2403,11 +2672,22 @@ func isField(n *node) bool {
|
||||
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
|
||||
}
|
||||
|
||||
func isInInterfaceType(n *node) bool {
|
||||
anc := n.anc
|
||||
for anc != nil {
|
||||
if anc.kind == interfaceType {
|
||||
return true
|
||||
}
|
||||
anc = anc.anc
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isInConstOrTypeDecl(n *node) bool {
|
||||
anc := n.anc
|
||||
for anc != nil {
|
||||
switch anc.kind {
|
||||
case constDecl, typeDecl:
|
||||
case constDecl, typeDecl, arrayType, chanType:
|
||||
return true
|
||||
case varDecl, funcDecl:
|
||||
return false
|
||||
@@ -2453,19 +2733,30 @@ func isCall(n *node) bool {
|
||||
return n.action == aCall || n.action == aCallSlice
|
||||
}
|
||||
|
||||
func isBinCall(n *node) bool {
|
||||
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
|
||||
func isBinCall(n *node, sc *scope) bool {
|
||||
if !isCall(n) || len(n.child) == 0 {
|
||||
return false
|
||||
}
|
||||
c0 := n.child[0]
|
||||
if c0.typ == nil {
|
||||
// If called early in parsing, child type may not be known yet.
|
||||
c0.typ, _ = nodeType(n.interp, sc, c0)
|
||||
if c0.typ == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return c0.typ.cat == valueT && c0.typ.rtype.Kind() == reflect.Func
|
||||
}
|
||||
|
||||
func isOffsetof(n *node) bool {
|
||||
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].rval.String() == "Offsetof"
|
||||
return n.typ != nil && n.typ.cat == valueT && n.rval.String() == "Offsetof"
|
||||
}
|
||||
|
||||
func mustReturnValue(n *node) bool {
|
||||
if len(n.child) < 2 {
|
||||
if len(n.child) < 3 {
|
||||
return false
|
||||
}
|
||||
for _, f := range n.child[1].child {
|
||||
for _, f := range n.child[2].child {
|
||||
if len(f.child) > 1 {
|
||||
return false
|
||||
}
|
||||
@@ -2546,21 +2837,10 @@ func typeSwichAssign(n *node) bool {
|
||||
return ts.kind == typeSwitch && ts.child[1].action == aAssign
|
||||
}
|
||||
|
||||
func gotoLabel(s *symbol) {
|
||||
if s.node == nil {
|
||||
return
|
||||
}
|
||||
for _, c := range s.from {
|
||||
if c.tnext == nil {
|
||||
c.tnext = s.node.start
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerator) {
|
||||
switch typ.cat {
|
||||
case aliasT, ptrT:
|
||||
gen = compositeGenerator(n, n.typ.val, rtyp)
|
||||
gen = compositeGenerator(n, typ.val, rtyp)
|
||||
case arrayT, sliceT:
|
||||
gen = arrayLit
|
||||
case mapT:
|
||||
@@ -2584,11 +2864,7 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
|
||||
}
|
||||
case valueT:
|
||||
if rtyp == nil {
|
||||
rtyp = n.typ.rtype
|
||||
}
|
||||
// TODO(mpl): I do not understand where this side-effect is coming from, and why it happens. quickfix for now.
|
||||
if rtyp == nil {
|
||||
rtyp = n.typ.val.rtype
|
||||
rtyp = n.typ.TypeOf()
|
||||
}
|
||||
switch k := rtyp.Kind(); k {
|
||||
case reflect.Struct:
|
||||
@@ -2614,23 +2890,54 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
|
||||
// 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.
|
||||
func arrayTypeLen(n *node) int {
|
||||
func arrayTypeLen(n *node, sc *scope) (int, error) {
|
||||
if n.typ != nil && n.typ.cat == arrayT {
|
||||
return n.typ.length
|
||||
return n.typ.length, nil
|
||||
}
|
||||
max := -1
|
||||
for i, c := range n.child[1:] {
|
||||
r := i
|
||||
if c.kind == keyValueExpr {
|
||||
if v := c.child[0].rval; v.IsValid() {
|
||||
r = int(c.child[0].rval.Int())
|
||||
for _, c := range n.child[1:] {
|
||||
var r int
|
||||
|
||||
if c.kind != keyValueExpr {
|
||||
r = max + 1
|
||||
max = r
|
||||
continue
|
||||
}
|
||||
|
||||
c0 := c.child[0]
|
||||
v := c0.rval
|
||||
if v.IsValid() {
|
||||
r = int(v.Int())
|
||||
} else {
|
||||
// Resolve array key value as a constant.
|
||||
if c0.kind == identExpr {
|
||||
// Key is defined by a symbol which must be a constant integer.
|
||||
sym, _, ok := sc.lookup(c0.ident)
|
||||
if !ok {
|
||||
return 0, c0.cfgErrorf("undefined: %s", c0.ident)
|
||||
}
|
||||
if sym.kind != constSym {
|
||||
return 0, c0.cfgErrorf("non-constant array bound %q", c0.ident)
|
||||
}
|
||||
r = int(vInt(sym.rval))
|
||||
} else {
|
||||
// Key is defined by a numeric constant expression.
|
||||
if _, err := c0.interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cv, ok := c0.rval.Interface().(constant.Value)
|
||||
if !ok {
|
||||
return 0, c0.cfgErrorf("non-constant expression")
|
||||
}
|
||||
r = constToInt(cv)
|
||||
}
|
||||
}
|
||||
|
||||
if r > max {
|
||||
max = r
|
||||
}
|
||||
}
|
||||
return max + 1
|
||||
return max + 1, nil
|
||||
}
|
||||
|
||||
// isValueUntyped returns true if value is untyped.
|
||||
@@ -2658,3 +2965,10 @@ func isBoolAction(n *node) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isBlank(n *node) bool {
|
||||
if n.kind == parenExpr && len(n.child) > 0 {
|
||||
return isBlank(n.child[0])
|
||||
}
|
||||
return n.ident == "_"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user