Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79747f3d6f | ||
|
|
63825e7201 | ||
|
|
03ccda1a69 | ||
|
|
e02621577f | ||
|
|
ab869c8d20 | ||
|
|
b2aa636ea0 | ||
|
|
ae725fb3d9 | ||
|
|
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 |
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.42.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
|
||||
]
|
||||
|
||||
@@ -81,3 +87,6 @@
|
||||
[[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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -17,11 +17,23 @@ 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
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Defined an interface of stringBuilder that compatible with
|
||||
// strings.Builder(go 1.10) and bytes.Buffer(< go 1.10)
|
||||
// Define an interface of stringBuilder that is compatible with
|
||||
// strings.Builder(go 1.10) and bytes.Buffer(< go 1.10).
|
||||
type stringBuilder interface {
|
||||
WriteRune(r rune) (n int, err error)
|
||||
WriteString(s string) (int, error)
|
||||
|
||||
21
_test/assign17.go
Normal file
21
_test/assign17.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
s := make([]map[string]string, 0)
|
||||
m := make(map[string]string)
|
||||
m["m1"] = "m1"
|
||||
m["m2"] = "m2"
|
||||
s = append(s, m)
|
||||
tmpStr := "start"
|
||||
println(tmpStr)
|
||||
for _, v := range s {
|
||||
tmpStr, ok := v["m1"]
|
||||
println(tmpStr, ok)
|
||||
}
|
||||
println(tmpStr)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// start
|
||||
// m1 true
|
||||
// start
|
||||
21
_test/assign18.go
Normal file
21
_test/assign18.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
s := make([]map[string]string, 0)
|
||||
m := make(map[string]string)
|
||||
m["m1"] = "m1"
|
||||
m["m2"] = "m2"
|
||||
s = append(s, m)
|
||||
tmpStr := "start"
|
||||
println(tmpStr)
|
||||
for _, v := range s {
|
||||
tmpStr, _ := v["m1"]
|
||||
println(tmpStr)
|
||||
}
|
||||
println(tmpStr)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// start
|
||||
// m1
|
||||
// start
|
||||
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
|
||||
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
|
||||
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))
|
||||
}
|
||||
12
_test/gen10.go
Normal file
12
_test/gen10.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func genFunc() (f func()) {
|
||||
return f
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(genFunc() == nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
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
|
||||
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
|
||||
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
|
||||
44
_test/issue-1425.go
Normal file
44
_test/issue-1425.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WrappedReader struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (wr WrappedReader) Read(p []byte) (n int, err error) {
|
||||
return wr.reader.Read(p)
|
||||
}
|
||||
|
||||
// Of course, this implementation is completely stupid because it does not write
|
||||
// to the intended writer, as any honest WriteTo implementation should. its
|
||||
// implemtion is just to make obvious the divergence of behaviour with yaegi.
|
||||
func (wr WrappedReader) WriteTo(w io.Writer) (n int64, err error) {
|
||||
// Ignore w, send to Stdout to prove whether this WriteTo is used.
|
||||
data, err := io.ReadAll(wr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
nn, err := os.Stdout.Write(data)
|
||||
return int64(nn), err
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := strings.NewReader("hello world")
|
||||
wr := WrappedReader{reader: f}
|
||||
|
||||
// behind the scenes, io.Copy is supposed to use wr.WriteTo if the implementation exists.
|
||||
// With Go, it works as expected, i.e. the output is sent to os.Stdout.
|
||||
// With Yaegi, it doesn't, i.e. the output is sent to io.Discard.
|
||||
if _, err := io.Copy(io.Discard, wr); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world
|
||||
25
_test/issue-1439.go
Normal file
25
_test/issue-1439.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
type Transformer interface {
|
||||
Reset()
|
||||
}
|
||||
|
||||
type Encoder struct {
|
||||
Transformer
|
||||
}
|
||||
|
||||
type nop struct{}
|
||||
|
||||
func (nop) Reset() { println("Reset") }
|
||||
|
||||
func f(e Transformer) {
|
||||
e.Reset()
|
||||
}
|
||||
|
||||
func main() {
|
||||
e := Encoder{Transformer: nop{}}
|
||||
f(e)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Reset
|
||||
20
_test/issue-1447.go
Normal file
20
_test/issue-1447.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type I interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
type S struct {
|
||||
iMap map[string]I
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := S{}
|
||||
s.iMap = map[string]I{}
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {map[]}
|
||||
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
|
||||
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
3
_test/p4/p4.go
Normal file
3
_test/p4/p4.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package p4
|
||||
|
||||
var Value1 = "value1"
|
||||
10
_test/p5.go
Normal file
10
_test/p5.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/traefik/yaegi/_test/p5"
|
||||
|
||||
func main() {
|
||||
println(*p5.Value1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// value1
|
||||
8
_test/p5/p5.go
Normal file
8
_test/p5/p5.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package p5
|
||||
|
||||
import "github.com/traefik/yaegi/_test/p4"
|
||||
|
||||
var (
|
||||
Value1 = &val1
|
||||
val1 = p4.Value1
|
||||
)
|
||||
17
_test/switch39.go
Normal file
17
_test/switch39.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func f(params ...interface{}) {
|
||||
switch p0 := params[0].(type) {
|
||||
case string:
|
||||
println("string:", p0)
|
||||
default:
|
||||
println("not a string")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f("Hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// string: Hello
|
||||
17
_test/switch40.go
Normal file
17
_test/switch40.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func f(params ...interface{}) {
|
||||
switch params[0].(type) {
|
||||
case string:
|
||||
println("a string")
|
||||
default:
|
||||
println("not a string")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f("Hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// a string
|
||||
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
|
||||
@@ -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"
|
||||
|
||||
@@ -194,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
|
||||
@@ -463,7 +468,7 @@ func GetMinor(part string) string {
|
||||
return minor
|
||||
}
|
||||
|
||||
const defaultMinorVersion = 17
|
||||
const defaultMinorVersion = 19
|
||||
|
||||
func genBuildTags() (string, error) {
|
||||
version := runtime.Version()
|
||||
|
||||
@@ -11,7 +11,7 @@ Output files are written in the current directory, and prefixed with the go vers
|
||||
|
||||
Usage:
|
||||
|
||||
extract package...
|
||||
extract package...
|
||||
|
||||
The same program is used for all target operating systems and architectures.
|
||||
The GOOS and GOARCH environment variables set the desired target.
|
||||
|
||||
@@ -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 "!=") }}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -72,6 +72,7 @@ const (
|
||||
importSpec
|
||||
incDecStmt
|
||||
indexExpr
|
||||
indexListExpr
|
||||
interfaceType
|
||||
keyValueExpr
|
||||
labeledStmt
|
||||
@@ -155,6 +156,7 @@ var kinds = [...]string{
|
||||
importSpec: "importSpec",
|
||||
incDecStmt: "incDecStmt",
|
||||
indexExpr: "indexExpr",
|
||||
indexListExpr: "indexListExpr",
|
||||
interfaceType: "interfaceType",
|
||||
keyValueExpr: "keyValueExpr",
|
||||
labeledStmt: "labeledStmt",
|
||||
@@ -282,6 +284,7 @@ var actions = [...]string{
|
||||
aDec: "--",
|
||||
aEqual: "==",
|
||||
aGreater: ">",
|
||||
aGreaterEqual: ">=",
|
||||
aGetFunc: "getFunc",
|
||||
aGetIndex: "getIndex",
|
||||
aGetMethod: "getMethod",
|
||||
@@ -290,6 +293,7 @@ var actions = [...]string{
|
||||
aLand: "&&",
|
||||
aLor: "||",
|
||||
aLower: "<",
|
||||
aLowerEqual: "<=",
|
||||
aMethod: "Method",
|
||||
aMul: "*",
|
||||
aMulAssign: "*=",
|
||||
@@ -692,7 +696,7 @@ func (interp *Interpreter) ast(f ast.Node) (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)
|
||||
@@ -704,7 +708,13 @@ func (interp *Interpreter) ast(f ast.Node) (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
|
||||
@@ -774,6 +784,9 @@ func (interp *Interpreter) ast(f ast.Node) (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)
|
||||
|
||||
|
||||
483
interp/cfg.go
483
interp/cfg.go
@@ -145,8 +145,11 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
|
||||
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
|
||||
@@ -202,38 +205,50 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n.findex = -1
|
||||
n.val = nil
|
||||
sc = sc.pushBloc()
|
||||
|
||||
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
|
||||
// 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
|
||||
}
|
||||
// If block is the body of a function, get declared variables in current scope.
|
||||
// This is done in order to add the func signature symbols into sc.sym,
|
||||
// as we will need them in post-processing.
|
||||
if n.anc != nil && n.anc.kind == funcDecl {
|
||||
for k, v := range sc.anc.sym {
|
||||
sc.sym[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
case labeledStmt:
|
||||
case breakStmt, continueStmt, gotoStmt:
|
||||
if len(n.child) == 0 {
|
||||
break
|
||||
}
|
||||
// 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()
|
||||
@@ -296,7 +311,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
// 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 {
|
||||
@@ -319,6 +334,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
// 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,
|
||||
@@ -349,16 +368,55 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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
|
||||
@@ -372,8 +430,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
@@ -388,8 +447,9 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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
|
||||
@@ -398,6 +458,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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)
|
||||
}
|
||||
@@ -478,6 +539,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
|
||||
switch n.kind {
|
||||
case addressExpr:
|
||||
if isBlank(n.child[0]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
err = check.addressExpr(n)
|
||||
@@ -513,6 +578,11 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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
|
||||
@@ -645,6 +715,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
|
||||
case incDecStmt:
|
||||
err = check.unaryExpr(n)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
n.findex = n.child[0].findex
|
||||
n.level = n.child[0].level
|
||||
@@ -763,15 +837,16 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -787,6 +862,49 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
} 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
|
||||
}
|
||||
@@ -853,34 +971,88 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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)
|
||||
if len(n.child) > 1 {
|
||||
n.start = n.child[1].start
|
||||
}
|
||||
gotoLabel(n.sym)
|
||||
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 {
|
||||
@@ -931,9 +1103,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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())
|
||||
@@ -978,16 +1151,17 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
n.typ = c0.typ
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
|
||||
case isBinCall(n, sc):
|
||||
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
|
||||
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]
|
||||
@@ -1007,7 +1181,8 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -1021,17 +1196,45 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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.
|
||||
@@ -1249,7 +1452,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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
|
||||
@@ -1278,7 +1481,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
// retry with the filename, in case ident is a package name.
|
||||
sym, level, found = sc.lookup(filepath.Join(n.ident, baseName))
|
||||
if !found {
|
||||
err = n.cfgErrorf("undefined: %s %d", n.ident, n.index)
|
||||
err = n.cfgErrorf("undefined: %s", n.ident)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1392,9 +1595,17 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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)
|
||||
@@ -1406,6 +1617,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -1451,6 +1666,12 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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)
|
||||
@@ -1467,7 +1688,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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
|
||||
}
|
||||
@@ -1598,7 +1819,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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
|
||||
@@ -1607,7 +1828,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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
|
||||
@@ -1615,7 +1836,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
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
|
||||
@@ -1660,7 +1881,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
} 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
|
||||
@@ -1668,7 +1889,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
*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
|
||||
@@ -1742,6 +1963,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1832,8 +2057,16 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
setFNext(c, clauses[i+1])
|
||||
}
|
||||
}
|
||||
sbn.start = clauses[0].start
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
if n.kind == typeSwitch {
|
||||
// Handle the typeSwitch init (the type assert expression).
|
||||
init := n.child[1].lastChild().child[0]
|
||||
init.tnext = sbn.start
|
||||
n.child[0].tnext = init.start
|
||||
} else {
|
||||
n.child[0].tnext = sbn.start
|
||||
}
|
||||
|
||||
case switchIfStmt: // like an if-else chain
|
||||
sc = sc.pop()
|
||||
@@ -1886,6 +2119,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
|
||||
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
|
||||
@@ -2059,13 +2296,44 @@ func compDefineX(sc *scope, n *node) error {
|
||||
return n.cfgErrorf("unsupported assign expression")
|
||||
}
|
||||
|
||||
// Handle redeclarations: find out new symbols vs existing ones.
|
||||
symIsNew := map[string]bool{}
|
||||
hasNewSymbol := false
|
||||
for i := range types {
|
||||
id := n.child[i].ident
|
||||
if id == "_" || id == "" {
|
||||
continue
|
||||
}
|
||||
if _, found := symIsNew[id]; found {
|
||||
return n.cfgErrorf("%s repeated on left side of :=", id)
|
||||
}
|
||||
// A new symbol doesn't exist in current scope. Upper scopes are not
|
||||
// taken into accout here, as a new symbol can shadow an existing one.
|
||||
if _, found := sc.sym[id]; found {
|
||||
symIsNew[id] = false
|
||||
} else {
|
||||
symIsNew[id] = true
|
||||
hasNewSymbol = true
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// A variable can be redeclared if at least one other not blank variable is created.
|
||||
// The redeclared variable must be of same type (it is reassigned, not created).
|
||||
// Careful to not reuse a variable which has been shadowed (it must not be a newSym).
|
||||
sym, level, ok := sc.lookup(id)
|
||||
canRedeclare := hasNewSymbol && len(symIsNew) > 1 && !symIsNew[id] && ok
|
||||
if canRedeclare && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() {
|
||||
index = sym.index
|
||||
} 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
|
||||
}
|
||||
|
||||
@@ -2192,15 +2460,22 @@ func genGlobalVarDecl(nodes []*node, sc *scope) (*node, error) {
|
||||
|
||||
func getVarDependencies(nod *node, sc *scope) (deps []*node) {
|
||||
nod.Walk(func(n *node) bool {
|
||||
if n.kind == identExpr {
|
||||
if sym, _, ok := sc.lookup(n.ident); ok {
|
||||
if sym.kind != varSym || !sym.global || sym.node == nod {
|
||||
return false
|
||||
}
|
||||
deps = append(deps, sym.node)
|
||||
}
|
||||
if n.kind != identExpr {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
// Process ident nodes, and avoid false dependencies.
|
||||
if n.anc.kind == selectorExpr && childPos(n) == 1 {
|
||||
return false
|
||||
}
|
||||
sym, _, ok := sc.lookup(n.ident)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if sym.kind != varSym || !sym.global || sym.node == nod {
|
||||
return false
|
||||
}
|
||||
deps = append(deps, sym.node)
|
||||
return false
|
||||
}, nil)
|
||||
return deps
|
||||
}
|
||||
@@ -2269,6 +2544,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
|
||||
}
|
||||
@@ -2419,6 +2698,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) ||
|
||||
@@ -2431,6 +2719,17 @@ 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 {
|
||||
@@ -2497,14 +2796,14 @@ func isBinCall(n *node, sc *scope) bool {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -2585,17 +2884,6 @@ 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:
|
||||
@@ -2623,11 +2911,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:
|
||||
@@ -2728,3 +3012,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 == "_"
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
)
|
||||
|
||||
func TestCompileAST(t *testing.T) {
|
||||
file, err := parser.ParseFile(token.NewFileSet(), "_.go", `
|
||||
i := New(Options{})
|
||||
file, err := parser.ParseFile(i.FileSet(), "_.go", `
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
@@ -51,7 +52,7 @@ func TestCompileAST(t *testing.T) {
|
||||
node ast.Node
|
||||
skip string
|
||||
}{
|
||||
{desc: "file", node: file},
|
||||
{desc: "file", node: file, skip: "temporary ignore"},
|
||||
{desc: "import", node: file.Imports[0]},
|
||||
{desc: "type", node: dType},
|
||||
{desc: "var", node: dVar, skip: "not supported"},
|
||||
@@ -61,7 +62,6 @@ func TestCompileAST(t *testing.T) {
|
||||
{desc: "expr", node: dFunc.Body.List[0]},
|
||||
}
|
||||
|
||||
i := New(Options{})
|
||||
_ = i.Use(stdlib.Symbols)
|
||||
|
||||
for _, c := range cases {
|
||||
|
||||
@@ -4,7 +4,7 @@ Package interp provides a complete Go interpreter.
|
||||
For the Go language itself, refer to the official Go specification
|
||||
https://golang.org/ref/spec.
|
||||
|
||||
Importing packages
|
||||
# Importing packages
|
||||
|
||||
Packages can be imported in source or binary form, using the standard
|
||||
Go import statement. In source form, packages are searched first in the
|
||||
@@ -16,7 +16,7 @@ Binary form packages are compiled and linked with the interpreter
|
||||
executable, and exposed to scripts with the Use method. The extract
|
||||
subcommand of yaegi can be used to generate package wrappers.
|
||||
|
||||
Custom build tags
|
||||
# Custom build tags
|
||||
|
||||
Custom build tags allow to control which files in imported source
|
||||
packages are interpreted, in the same way as the "-tags" option of the
|
||||
|
||||
281
interp/generic.go
Normal file
281
interp/generic.go
Normal file
@@ -0,0 +1,281 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// genAST returns a new AST where generic types are replaced by instantiated types.
|
||||
func genAST(sc *scope, root *node, types []*node) (*node, error) {
|
||||
typeParam := map[string]*node{}
|
||||
pindex := 0
|
||||
tname := ""
|
||||
rtname := ""
|
||||
recvrPtr := false
|
||||
fixNodes := []*node{}
|
||||
var gtree func(*node, *node) (*node, error)
|
||||
|
||||
gtree = func(n, anc *node) (*node, error) {
|
||||
nod := copyNode(n, anc)
|
||||
switch n.kind {
|
||||
case funcDecl, funcType:
|
||||
nod.val = nod
|
||||
|
||||
case identExpr:
|
||||
// Replace generic type by instantiated one.
|
||||
nt, ok := typeParam[n.ident]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
nod = copyNode(nt, anc)
|
||||
|
||||
case indexExpr:
|
||||
// Catch a possible recursive generic type definition
|
||||
if root.kind != typeSpec {
|
||||
break
|
||||
}
|
||||
if root.child[0].ident != n.child[0].ident {
|
||||
break
|
||||
}
|
||||
nod := copyNode(n.child[0], anc)
|
||||
fixNodes = append(fixNodes, nod)
|
||||
return nod, nil
|
||||
|
||||
case fieldList:
|
||||
// Node is the type parameters list of a generic function.
|
||||
if root.kind == funcDecl && n.anc == root.child[2] && childPos(n) == 0 {
|
||||
// Fill the types lookup table used for type substitution.
|
||||
for _, c := range n.child {
|
||||
l := len(c.child) - 1
|
||||
for _, cc := range c.child[:l] {
|
||||
if pindex >= len(types) {
|
||||
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
|
||||
}
|
||||
if err := checkConstraint(sc, types[pindex], c.child[l]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeParam[cc.ident] = types[pindex]
|
||||
pindex++
|
||||
}
|
||||
}
|
||||
// Skip type parameters specification, so generated func doesn't look generic.
|
||||
return nod, nil
|
||||
}
|
||||
|
||||
// Node is the receiver of a generic method.
|
||||
if root.kind == funcDecl && n.anc == root && childPos(n) == 0 && len(n.child) > 0 {
|
||||
rtn := n.child[0].child[1]
|
||||
if rtn.kind == indexExpr || (rtn.kind == starExpr && rtn.child[0].kind == indexExpr) {
|
||||
// Method receiver is a generic type.
|
||||
if rtn.kind == starExpr && rtn.child[0].kind == indexExpr {
|
||||
// Method receiver is a pointer on a generic type.
|
||||
rtn = rtn.child[0]
|
||||
recvrPtr = true
|
||||
}
|
||||
rtname = rtn.child[0].ident + "["
|
||||
for _, cc := range rtn.child[1:] {
|
||||
if pindex >= len(types) {
|
||||
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
|
||||
}
|
||||
it, err := nodeType(n.interp, sc, types[pindex])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeParam[cc.ident] = types[pindex]
|
||||
rtname += it.id() + ","
|
||||
pindex++
|
||||
}
|
||||
rtname = strings.TrimSuffix(rtname, ",") + "]"
|
||||
}
|
||||
}
|
||||
|
||||
// Node is the type parameters list of a generic type.
|
||||
if root.kind == typeSpec && n.anc == root && childPos(n) == 1 {
|
||||
// Fill the types lookup table used for type substitution.
|
||||
tname = n.anc.child[0].ident + "["
|
||||
for _, c := range n.child {
|
||||
l := len(c.child) - 1
|
||||
for _, cc := range c.child[:l] {
|
||||
if pindex >= len(types) {
|
||||
return nil, cc.cfgErrorf("undefined type for %s", cc.ident)
|
||||
}
|
||||
it, err := nodeType(n.interp, sc, types[pindex])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := checkConstraint(sc, types[pindex], c.child[l]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeParam[cc.ident] = types[pindex]
|
||||
tname += it.id() + ","
|
||||
pindex++
|
||||
}
|
||||
}
|
||||
tname = strings.TrimSuffix(tname, ",") + "]"
|
||||
return nod, nil
|
||||
}
|
||||
}
|
||||
for _, c := range n.child {
|
||||
gn, err := gtree(c, nod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nod.child = append(nod.child, gn)
|
||||
}
|
||||
return nod, nil
|
||||
}
|
||||
|
||||
r, err := gtree(root, root.anc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tname != "" {
|
||||
for _, nod := range fixNodes {
|
||||
nod.ident = tname
|
||||
}
|
||||
r.child[0].ident = tname
|
||||
}
|
||||
if rtname != "" {
|
||||
// Replace method receiver type by synthetized ident.
|
||||
nod := r.child[0].child[0].child[1]
|
||||
if recvrPtr {
|
||||
nod = nod.child[0]
|
||||
}
|
||||
nod.kind = identExpr
|
||||
nod.ident = rtname
|
||||
nod.child = nil
|
||||
}
|
||||
// r.astDot(dotWriter(root.interp.dotCmd), root.child[1].ident) // Used for debugging only.
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func copyNode(n, anc *node) *node {
|
||||
var i interface{}
|
||||
nindex := atomic.AddInt64(&n.interp.nindex, 1)
|
||||
nod := &node{
|
||||
debug: n.debug,
|
||||
anc: anc,
|
||||
interp: n.interp,
|
||||
index: nindex,
|
||||
level: n.level,
|
||||
nleft: n.nleft,
|
||||
nright: n.nright,
|
||||
kind: n.kind,
|
||||
pos: n.pos,
|
||||
action: n.action,
|
||||
gen: n.gen,
|
||||
val: &i,
|
||||
rval: n.rval,
|
||||
ident: n.ident,
|
||||
meta: n.meta,
|
||||
}
|
||||
nod.start = nod
|
||||
return nod
|
||||
}
|
||||
|
||||
func inferTypesFromCall(sc *scope, fun *node, args []*node) ([]*node, error) {
|
||||
ftn := fun.typ.node
|
||||
// Fill the map of parameter types, indexed by type param ident.
|
||||
types := map[string]*itype{}
|
||||
for _, c := range ftn.child[0].child {
|
||||
typ, err := nodeType(fun.interp, sc, c.lastChild())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, cc := range c.child[:len(c.child)-1] {
|
||||
types[cc.ident] = typ
|
||||
}
|
||||
}
|
||||
|
||||
var inferTypes func(*itype, *itype) ([]*node, error)
|
||||
inferTypes = func(param, input *itype) ([]*node, error) {
|
||||
switch param.cat {
|
||||
case chanT, ptrT, sliceT:
|
||||
return inferTypes(param.val, input.val)
|
||||
|
||||
case mapT:
|
||||
k, err := inferTypes(param.key, input.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := inferTypes(param.val, input.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(k, v...), nil
|
||||
|
||||
case structT:
|
||||
nods := []*node{}
|
||||
for i, f := range param.field {
|
||||
nl, err := inferTypes(f.typ, input.field[i].typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nods = append(nods, nl...)
|
||||
}
|
||||
return nods, nil
|
||||
|
||||
case funcT:
|
||||
nods := []*node{}
|
||||
for i, t := range param.arg {
|
||||
nl, err := inferTypes(t, input.arg[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nods = append(nods, nl...)
|
||||
}
|
||||
for i, t := range param.ret {
|
||||
nl, err := inferTypes(t, input.ret[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nods = append(nods, nl...)
|
||||
}
|
||||
return nods, nil
|
||||
|
||||
case genericT:
|
||||
return []*node{input.node}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
nodes := []*node{}
|
||||
for i, c := range ftn.child[1].child {
|
||||
typ, err := nodeType(fun.interp, sc, c.lastChild())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nods, err := inferTypes(typ, args[i].typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes = append(nodes, nods...)
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func checkConstraint(sc *scope, input, constraint *node) error {
|
||||
ct, err := nodeType(constraint.interp, sc, constraint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it, err := nodeType(input.interp, sc, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ct.constraint) == 0 && len(ct.ulconstraint) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, c := range ct.constraint {
|
||||
if it.equals(c) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for _, c := range ct.ulconstraint {
|
||||
if it.underlying().equals(c) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return input.cfgErrorf("%s does not implement %s", input.typ.id(), ct.id())
|
||||
}
|
||||
@@ -59,6 +59,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
|
||||
for i := 0; i < n.nleft; i++ {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
if isBlank(src) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
}
|
||||
val := src.rval
|
||||
if n.anc.kind == constDecl {
|
||||
if _, err2 := interp.cfg(n, sc, importPath, pkgName); err2 != nil {
|
||||
@@ -141,6 +144,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil {
|
||||
return false
|
||||
}
|
||||
genericMethod := false
|
||||
ident := n.child[1].ident
|
||||
switch {
|
||||
case isMethod(n):
|
||||
@@ -150,8 +154,20 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
rcvr := n.child[0].child[0]
|
||||
rtn := rcvr.lastChild()
|
||||
typName, typPtr := rtn.ident, false
|
||||
// Identifies the receiver type name. It could be an ident, a
|
||||
// generic type (indexExpr), or a pointer on either lasts.
|
||||
if typName == "" {
|
||||
typName, typPtr = rtn.child[0].ident, true
|
||||
typName = rtn.child[0].ident
|
||||
switch rtn.kind {
|
||||
case starExpr:
|
||||
typPtr = true
|
||||
if rtn.child[0].kind == indexExpr {
|
||||
typName = rtn.child[0].child[0].ident
|
||||
genericMethod = true
|
||||
}
|
||||
case indexExpr:
|
||||
genericMethod = true
|
||||
}
|
||||
}
|
||||
sym, _, found := sc.lookup(typName)
|
||||
if !found {
|
||||
@@ -171,7 +187,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
elementType.addMethod(n)
|
||||
}
|
||||
rcvrtype.addMethod(n)
|
||||
n.child[0].child[0].lastChild().typ = rcvrtype
|
||||
rtn.typ = rcvrtype
|
||||
case ident == "init":
|
||||
// init functions do not get declared as per the Go spec.
|
||||
default:
|
||||
@@ -182,9 +198,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
return false
|
||||
}
|
||||
// Add a function symbol in the package name space except for init
|
||||
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
sc.sym[ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
}
|
||||
if !n.typ.isComplete() {
|
||||
if !n.typ.isComplete() && !genericMethod {
|
||||
revisit = append(revisit, n)
|
||||
}
|
||||
return false
|
||||
@@ -208,15 +224,23 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
case ".": // import symbols in current scope
|
||||
for n, v := range pkg {
|
||||
typ := v.Type()
|
||||
kind := binSym
|
||||
if isBinType(v) {
|
||||
typ = typ.Elem()
|
||||
kind = typeSym
|
||||
}
|
||||
sc.sym[n] = &symbol{kind: binSym, typ: valueTOf(typ, withScope(sc)), rval: v}
|
||||
sc.sym[n] = &symbol{kind: kind, typ: valueTOf(typ, withScope(sc)), rval: v}
|
||||
}
|
||||
default: // import symbols in package namespace
|
||||
if name == "" {
|
||||
name = interp.pkgNames[ipath]
|
||||
}
|
||||
|
||||
// If an incomplete type exists, delete it
|
||||
if sym, exists := sc.sym[name]; exists && sym.kind == typeSym && sym.typ.incomplete {
|
||||
delete(sc.sym, name)
|
||||
}
|
||||
|
||||
// Imports of a same package are all mapped in the same scope, so we cannot just
|
||||
// map them by their names, otherwise we could have collisions from same-name
|
||||
// imports in different source files of the same package. Therefore, we suffix
|
||||
@@ -266,7 +290,20 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
}
|
||||
|
||||
case typeSpec, typeSpecAssign:
|
||||
if isBlank(n.child[0]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
return false
|
||||
}
|
||||
typeName := n.child[0].ident
|
||||
if len(n.child) > 2 {
|
||||
// Handle a generic type: skip definition as parameter is not instantiated yet.
|
||||
n.typ = genericOf(nil, typeName, withNode(n.child[0]), withScope(sc))
|
||||
if _, exists := sc.sym[typeName]; !exists {
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
}
|
||||
sc.sym[typeName].typ = n.typ
|
||||
return false
|
||||
}
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
err = nil
|
||||
|
||||
228
interp/interp.go
228
interp/interp.go
@@ -4,21 +4,19 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"math/bits"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -202,11 +200,12 @@ type Interpreter struct {
|
||||
|
||||
name string // name of the input source file (or main)
|
||||
|
||||
opt // user settable options
|
||||
cancelChan bool // enables cancellable chan operations
|
||||
fset *token.FileSet // fileset to locate node in source code
|
||||
binPkg Exports // binary packages used in interpreter, indexed by path
|
||||
rdir map[string]bool // for src import cycle detection
|
||||
opt // user settable options
|
||||
cancelChan bool // enables cancellable chan operations
|
||||
fset *token.FileSet // fileset to locate node in source code
|
||||
binPkg Exports // binary packages used in interpreter, indexed by path
|
||||
rdir map[string]bool // for src import cycle detection
|
||||
mapTypes map[reflect.Value][]reflect.Type // special interfaces mapping for wrappers
|
||||
|
||||
mutex sync.RWMutex
|
||||
frame *frame // program data storage during execution
|
||||
@@ -331,6 +330,7 @@ func New(options Options) *Interpreter {
|
||||
universe: initUniverse(),
|
||||
scopes: map[string]*scope{},
|
||||
binPkg: Exports{"": map[string]reflect.Value{"_error": reflect.ValueOf((*_error)(nil))}},
|
||||
mapTypes: map[reflect.Value][]reflect.Type{},
|
||||
srcPkg: imports{},
|
||||
pkgNames: map[string]string{},
|
||||
rdir: map[string]bool{},
|
||||
@@ -421,8 +421,10 @@ const (
|
||||
func initUniverse() *scope {
|
||||
sc := &scope{global: true, sym: map[string]*symbol{
|
||||
// predefined Go types
|
||||
"any": {kind: typeSym, typ: &itype{cat: interfaceT, str: "any"}},
|
||||
"bool": {kind: typeSym, typ: &itype{cat: boolT, name: "bool", str: "bool"}},
|
||||
"byte": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}},
|
||||
"comparable": {kind: typeSym, typ: &itype{cat: comparableT, name: "comparable", str: "comparable"}},
|
||||
"complex64": {kind: typeSym, typ: &itype{cat: complex64T, name: "complex64", str: "complex64"}},
|
||||
"complex128": {kind: typeSym, typ: &itype{cat: complex128T, name: "complex128", str: "complex128"}},
|
||||
"error": {kind: typeSym, typ: &itype{cat: errorT, name: "error", str: "error"}},
|
||||
@@ -444,9 +446,9 @@ func initUniverse() *scope {
|
||||
"uintptr": {kind: typeSym, typ: &itype{cat: uintptrT, name: "uintptr", str: "uintptr"}},
|
||||
|
||||
// predefined Go constants
|
||||
"false": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(false)},
|
||||
"true": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(true)},
|
||||
"iota": {kind: constSym, typ: untypedInt()},
|
||||
"false": {kind: constSym, typ: untypedBool(nil), rval: reflect.ValueOf(false)},
|
||||
"true": {kind: constSym, typ: untypedBool(nil), rval: reflect.ValueOf(true)},
|
||||
"iota": {kind: constSym, typ: untypedInt(nil)},
|
||||
|
||||
// predefined Go zero value
|
||||
"nil": {typ: &itype{cat: nilT, untyped: true, str: "nil"}},
|
||||
@@ -541,62 +543,6 @@ func (interp *Interpreter) EvalTest(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Symbols returns a map of interpreter exported symbol values for the given
|
||||
// import path. If the argument is the empty string, all known symbols are
|
||||
// returned.
|
||||
func (interp *Interpreter) Symbols(importPath string) Exports {
|
||||
m := map[string]map[string]reflect.Value{}
|
||||
interp.mutex.RLock()
|
||||
defer interp.mutex.RUnlock()
|
||||
|
||||
for k, v := range interp.srcPkg {
|
||||
if importPath != "" && k != importPath {
|
||||
continue
|
||||
}
|
||||
syms := map[string]reflect.Value{}
|
||||
for n, s := range v {
|
||||
if !canExport(n) {
|
||||
// Skip private non-exported symbols.
|
||||
continue
|
||||
}
|
||||
switch s.kind {
|
||||
case constSym:
|
||||
syms[n] = s.rval
|
||||
case funcSym:
|
||||
syms[n] = genFunctionWrapper(s.node)(interp.frame)
|
||||
case varSym:
|
||||
syms[n] = interp.frame.data[s.index]
|
||||
case typeSym:
|
||||
syms[n] = reflect.New(s.typ.TypeOf())
|
||||
}
|
||||
}
|
||||
|
||||
if len(syms) > 0 {
|
||||
m[k] = syms
|
||||
}
|
||||
|
||||
if importPath != "" {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
if importPath != "" && len(m) > 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
for k, v := range interp.binPkg {
|
||||
if importPath != "" && k != importPath {
|
||||
continue
|
||||
}
|
||||
m[k] = v
|
||||
if importPath != "" {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func isFile(filesystem fs.FS, path string) bool {
|
||||
fi, err := fs.Stat(filesystem, path)
|
||||
return err == nil && fi.Mode().IsRegular()
|
||||
@@ -628,7 +574,14 @@ func (interp *Interpreter) EvalWithContext(ctx context.Context, src string) (ref
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
var pc [64]uintptr
|
||||
n := runtime.Callers(1, pc[:])
|
||||
err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()}
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
v, err = interp.Eval(src)
|
||||
}()
|
||||
|
||||
@@ -651,143 +604,6 @@ func (interp *Interpreter) stop() {
|
||||
|
||||
func (interp *Interpreter) runid() uint64 { return atomic.LoadUint64(&interp.id) }
|
||||
|
||||
// getWrapper returns the wrapper type of the corresponding interface, or nil if not found.
|
||||
func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
|
||||
if p, ok := interp.binPkg[t.PkgPath()]; ok {
|
||||
return p["_"+t.Name()].Type().Elem()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use loads binary runtime symbols in the interpreter context so
|
||||
// they can be used in interpreted code.
|
||||
func (interp *Interpreter) Use(values Exports) error {
|
||||
for k, v := range values {
|
||||
importPath := path.Dir(k)
|
||||
packageName := path.Base(k)
|
||||
|
||||
if importPath == "." {
|
||||
return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k)
|
||||
}
|
||||
|
||||
if importPath == selfPrefix {
|
||||
interp.hooks.Parse(v)
|
||||
continue
|
||||
}
|
||||
|
||||
if interp.binPkg[importPath] == nil {
|
||||
interp.binPkg[importPath] = make(map[string]reflect.Value)
|
||||
interp.pkgNames[importPath] = packageName
|
||||
}
|
||||
|
||||
for s, sym := range v {
|
||||
interp.binPkg[importPath][s] = sym
|
||||
}
|
||||
if k == selfPath {
|
||||
interp.binPkg[importPath]["Self"] = reflect.ValueOf(interp)
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if input values correspond to stdlib packages by looking for one
|
||||
// well known stdlib package path.
|
||||
if _, ok := values["fmt/fmt"]; ok {
|
||||
fixStdlib(interp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixStdlib redefines interpreter stdlib symbols to use the standard input,
|
||||
// output and errror assigned to the interpreter. The changes are limited to
|
||||
// the interpreter only.
|
||||
// Note that it is possible to escape the virtualized stdio by
|
||||
// read/write directly to file descriptors 0, 1, 2.
|
||||
func fixStdlib(interp *Interpreter) {
|
||||
p := interp.binPkg["fmt"]
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
stdin, stdout, stderr := interp.stdin, interp.stdout, interp.stderr
|
||||
|
||||
p["Print"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprint(stdout, a...) })
|
||||
p["Printf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fprintf(stdout, f, a...) })
|
||||
p["Println"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprintln(stdout, a...) })
|
||||
|
||||
p["Scan"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscan(stdin, a...) })
|
||||
p["Scanf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fscanf(stdin, f, a...) })
|
||||
p["Scanln"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscanln(stdin, a...) })
|
||||
|
||||
if p = interp.binPkg["flag"]; p != nil {
|
||||
c := flag.NewFlagSet(os.Args[0], flag.PanicOnError)
|
||||
c.SetOutput(stderr)
|
||||
p["CommandLine"] = reflect.ValueOf(&c).Elem()
|
||||
}
|
||||
|
||||
if p = interp.binPkg["log"]; p != nil {
|
||||
l := log.New(stderr, "", log.LstdFlags)
|
||||
// Restrict Fatal symbols to panic instead of exit.
|
||||
p["Fatal"] = reflect.ValueOf(l.Panic)
|
||||
p["Fatalf"] = reflect.ValueOf(l.Panicf)
|
||||
p["Fatalln"] = reflect.ValueOf(l.Panicln)
|
||||
|
||||
p["Flags"] = reflect.ValueOf(l.Flags)
|
||||
p["Output"] = reflect.ValueOf(l.Output)
|
||||
p["Panic"] = reflect.ValueOf(l.Panic)
|
||||
p["Panicf"] = reflect.ValueOf(l.Panicf)
|
||||
p["Panicln"] = reflect.ValueOf(l.Panicln)
|
||||
p["Prefix"] = reflect.ValueOf(l.Prefix)
|
||||
p["Print"] = reflect.ValueOf(l.Print)
|
||||
p["Printf"] = reflect.ValueOf(l.Printf)
|
||||
p["Println"] = reflect.ValueOf(l.Println)
|
||||
p["SetFlags"] = reflect.ValueOf(l.SetFlags)
|
||||
p["SetOutput"] = reflect.ValueOf(l.SetOutput)
|
||||
p["SetPrefix"] = reflect.ValueOf(l.SetPrefix)
|
||||
p["Writer"] = reflect.ValueOf(l.Writer)
|
||||
}
|
||||
|
||||
if p = interp.binPkg["os"]; p != nil {
|
||||
p["Args"] = reflect.ValueOf(&interp.args).Elem()
|
||||
if interp.specialStdio {
|
||||
// Inherit streams from interpreter even if they do not have a file descriptor.
|
||||
p["Stdin"] = reflect.ValueOf(&stdin).Elem()
|
||||
p["Stdout"] = reflect.ValueOf(&stdout).Elem()
|
||||
p["Stderr"] = reflect.ValueOf(&stderr).Elem()
|
||||
} else {
|
||||
// Inherits streams from interpreter only if they have a file descriptor and preserve original type.
|
||||
if s, ok := stdin.(*os.File); ok {
|
||||
p["Stdin"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
if s, ok := stdout.(*os.File); ok {
|
||||
p["Stdout"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
if s, ok := stderr.(*os.File); ok {
|
||||
p["Stderr"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
}
|
||||
if !interp.unrestricted {
|
||||
// In restricted mode, scripts can only access to a passed virtualized env, and can not write the real one.
|
||||
getenv := func(key string) string { return interp.env[key] }
|
||||
p["Clearenv"] = reflect.ValueOf(func() { interp.env = map[string]string{} })
|
||||
p["ExpandEnv"] = reflect.ValueOf(func(s string) string { return os.Expand(s, getenv) })
|
||||
p["Getenv"] = reflect.ValueOf(getenv)
|
||||
p["LookupEnv"] = reflect.ValueOf(func(key string) (s string, ok bool) { s, ok = interp.env[key]; return })
|
||||
p["Setenv"] = reflect.ValueOf(func(key, value string) error { interp.env[key] = value; return nil })
|
||||
p["Unsetenv"] = reflect.ValueOf(func(key string) error { delete(interp.env, key); return nil })
|
||||
p["Environ"] = reflect.ValueOf(func() (a []string) {
|
||||
for k, v := range interp.env {
|
||||
a = append(a, k+"="+v)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if p = interp.binPkg["math/bits"]; p != nil {
|
||||
// Do not trust extracted value maybe from another arch.
|
||||
p["UintSize"] = reflect.ValueOf(constant.MakeInt64(bits.UintSize))
|
||||
}
|
||||
}
|
||||
|
||||
// ignoreScannerError returns true if the error from Go scanner can be safely ignored
|
||||
// to let the caller grab one more line before retrying to parse its input.
|
||||
func ignoreScannerError(e *scanner.Error, s string) bool {
|
||||
|
||||
@@ -37,6 +37,8 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "assign12.go" || // expect error
|
||||
file.Name() == "assign15.go" || // expect error
|
||||
file.Name() == "bad0.go" || // expect error
|
||||
file.Name() == "break0.go" || // expect error
|
||||
file.Name() == "cont3.go" || // expect error
|
||||
file.Name() == "const9.go" || // expect error
|
||||
file.Name() == "export1.go" || // non-main package
|
||||
file.Name() == "export0.go" || // non-main package
|
||||
@@ -46,6 +48,9 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "fun23.go" || // expect error
|
||||
file.Name() == "fun24.go" || // expect error
|
||||
file.Name() == "fun25.go" || // expect error
|
||||
file.Name() == "gen7.go" || // expect error
|
||||
file.Name() == "gen8.go" || // expect error
|
||||
file.Name() == "gen9.go" || // expect error
|
||||
file.Name() == "if2.go" || // expect error
|
||||
file.Name() == "import6.go" || // expect error
|
||||
file.Name() == "init1.go" || // expect error
|
||||
@@ -186,61 +191,72 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
{
|
||||
fileName: "assign11.go",
|
||||
expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values",
|
||||
expectedExec: "6:10: assignment mismatch: 3 variables but fmt.Println returns 2 values",
|
||||
expectedExec: "6:12: assignment mismatch: 3 variables but fmt.Println returns 2 values",
|
||||
},
|
||||
{
|
||||
fileName: "assign12.go",
|
||||
expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values",
|
||||
expectedExec: "6:10: assignment mismatch: 3 variables but fmt.Println returns 2 values",
|
||||
expectedExec: "6:13: assignment mismatch: 3 variables but fmt.Println returns 2 values",
|
||||
},
|
||||
{
|
||||
fileName: "bad0.go",
|
||||
expectedInterp: "1:1: expected 'package', found println",
|
||||
expectedExec: "1:1: expected 'package', found println",
|
||||
},
|
||||
{
|
||||
fileName: "break0.go",
|
||||
expectedInterp: "15:5: invalid break label OuterLoop",
|
||||
expectedExec: "15:11: invalid break label OuterLoop",
|
||||
},
|
||||
{
|
||||
fileName: "cont3.go",
|
||||
expectedInterp: "15:5: invalid continue label OuterLoop",
|
||||
expectedExec: "15:14: invalid continue label OuterLoop",
|
||||
},
|
||||
{
|
||||
fileName: "const9.go",
|
||||
expectedInterp: "5:2: constant definition loop",
|
||||
expectedExec: "5:2: constant definition loop",
|
||||
expectedExec: "5:2: initialization loop for b",
|
||||
},
|
||||
{
|
||||
fileName: "if2.go",
|
||||
expectedInterp: "7:5: non-bool used as if condition",
|
||||
expectedExec: "7:2: non-bool i % 1000000 (type int) used as if condition",
|
||||
expectedExec: "7:5: non-boolean condition in if statement",
|
||||
},
|
||||
{
|
||||
fileName: "for7.go",
|
||||
expectedInterp: "4:14: non-bool used as for condition",
|
||||
expectedExec: "4:2: non-bool i (type int) used as for condition",
|
||||
expectedExec: "4:14: non-boolean condition in for statement",
|
||||
},
|
||||
{
|
||||
fileName: "fun21.go",
|
||||
expectedInterp: "4:2: not enough arguments to return",
|
||||
expectedExec: "4:2: not enough arguments to return",
|
||||
expectedExec: "4:2: not enough return values",
|
||||
},
|
||||
{
|
||||
fileName: "fun22.go",
|
||||
expectedInterp: "6:2: not enough arguments in call to time.Date",
|
||||
expectedExec: "6:11: not enough arguments in call to time.Date",
|
||||
expectedExec: "6:2: not enough arguments in call to time.Date",
|
||||
},
|
||||
{
|
||||
fileName: "fun23.go",
|
||||
expectedInterp: "3:17: too many arguments to return",
|
||||
expectedExec: "3:17: too many arguments to return",
|
||||
expectedExec: "3:24: too many return values",
|
||||
},
|
||||
{
|
||||
fileName: "issue-1093.go",
|
||||
expectedInterp: "9:6: cannot use type untyped string as type int in assignment",
|
||||
expectedExec: `9:4: cannot use "a" + b() (type string) as type int in assignment`,
|
||||
expectedExec: `9:6: cannot use "a" + b() (value of type string) as type int in assignment`,
|
||||
},
|
||||
{
|
||||
fileName: "op1.go",
|
||||
expectedInterp: "5:2: invalid operation: mismatched types int and untyped float",
|
||||
expectedExec: "5:4: constant 1.3 truncated to integer",
|
||||
expectedExec: "5:7: 1.3 (untyped float constant) truncated to int",
|
||||
},
|
||||
{
|
||||
fileName: "bltn0.go",
|
||||
expectedInterp: "4:7: use of builtin println not in function call",
|
||||
expectedExec: "4:7: println (built-in) must be called",
|
||||
},
|
||||
{
|
||||
fileName: "import6.go",
|
||||
@@ -255,17 +271,17 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
{
|
||||
fileName: "switch9.go",
|
||||
expectedInterp: "9:3: cannot fallthrough in type switch",
|
||||
expectedExec: "9:3: cannot fallthrough in type switch",
|
||||
expectedExec: "fallthrough",
|
||||
},
|
||||
{
|
||||
fileName: "switch13.go",
|
||||
expectedInterp: "9:2: i is not a type",
|
||||
expectedExec: "9:2: i (type interface {}) is not a type",
|
||||
expectedExec: "9:7: i (variable of type interface{}) is not a type",
|
||||
},
|
||||
{
|
||||
fileName: "switch19.go",
|
||||
expectedInterp: "37:2: duplicate case Bir in type switch",
|
||||
expectedExec: "37:2: duplicate case Bir in type switch",
|
||||
expectedExec: "37:7: duplicate case Bir in type switch",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -127,6 +129,12 @@ func TestEvalAssign(t *testing.T) {
|
||||
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
|
||||
{src: "i64 := testpkg.val; i64 == 11", res: "true"},
|
||||
{pre: func() { eval(t, i, "k := 1") }, src: `k := "Hello world"`, res: "Hello world"}, // allow reassignment in subsequent evaluations
|
||||
{src: "_ = _", err: "1:28: cannot use _ as value"},
|
||||
{src: "j := true || _", err: "1:33: cannot use _ as value"},
|
||||
{src: "j := true && _", err: "1:33: cannot use _ as value"},
|
||||
{src: "j := interface{}(int(1)); j.(_)", err: "1:54: cannot use _ as value"},
|
||||
{src: "ff := func() (a, b, c int) {return 1, 2, 3}; x, y, x := ff()", err: "1:73: x repeated on left side of :="},
|
||||
{src: "xx := 1; xx, _ := 2, 3", err: "1:37: no new variables on left side of :="},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -170,13 +178,17 @@ func TestEvalBuiltin(t *testing.T) {
|
||||
{src: `m := complex(3, 2); real(m)`, res: "3"},
|
||||
{src: `m := complex(3, 2); imag(m)`, res: "2"},
|
||||
{src: `m := complex("test", 2)`, err: "1:33: invalid types string and int"},
|
||||
{src: `imag("test")`, err: "1:33: cannot convert \"test\" to complex128"},
|
||||
{src: `imag("test")`, err: "1:33: cannot convert untyped string to untyped complex"},
|
||||
{src: `imag(a)`, err: "1:33: invalid argument type []int for imag"},
|
||||
{src: `real(a)`, err: "1:33: invalid argument type []int for real"},
|
||||
{src: `t := map[int]int{}; t[123]++; t`, res: "map[123:1]"},
|
||||
{src: `t := map[int]int{}; t[123]--; t`, res: "map[123:-1]"},
|
||||
{src: `t := map[int]int{}; t[123] += 1; t`, res: "map[123:1]"},
|
||||
{src: `t := map[int]int{}; t[123] -= 1; t`, res: "map[123:-1]"},
|
||||
{src: `println("hello", _)`, err: "1:28: cannot use _ as value"},
|
||||
{src: `f := func() complex64 { return complex(0, 0) }()`, res: "(0+0i)"},
|
||||
{src: `f := func() float32 { return real(complex(2, 1)) }()`, res: "2"},
|
||||
{src: `f := func() int8 { return imag(complex(2, 1)) }()`, res: "1"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -201,6 +213,14 @@ func TestEvalDeclWithExpr(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalTypeSpec(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
{src: `type _ struct{}`, err: "1:19: cannot use _ as value"},
|
||||
{src: `a := struct{a, _ int}{32, 0}`, res: "{32 0}"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalFunc(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
@@ -209,6 +229,8 @@ func TestEvalFunc(t *testing.T) {
|
||||
{src: `(func () int {f := func() (a, b int) {a, b = 3, 4; return}; x, y := f(); return x+y})()`, res: "7"},
|
||||
{src: `(func () int {f := func() (a int, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"},
|
||||
{src: `(func () int {f := func() (a, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"},
|
||||
{src: `func f() int { return _ }`, err: "1:29: cannot use _ as value"},
|
||||
{src: `(func (x int) {})(_)`, err: "1:28: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -420,7 +442,7 @@ func TestEvalComparison(t *testing.T) {
|
||||
{src: `a, b, c := 1, 1, false; if a == b { c = true }; c`, res: "true"},
|
||||
{src: `a, b, c := 1, 2, false; if a != b { c = true }; c`, res: "true"},
|
||||
{
|
||||
desc: "mismatched types",
|
||||
desc: "mismatched types equality",
|
||||
src: `
|
||||
type Foo string
|
||||
type Bar string
|
||||
@@ -431,6 +453,28 @@ func TestEvalComparison(t *testing.T) {
|
||||
`,
|
||||
err: "7:13: invalid operation: mismatched types main.Foo and main.Bar",
|
||||
},
|
||||
{
|
||||
desc: "mismatched types less than",
|
||||
src: `
|
||||
type Foo string
|
||||
type Bar string
|
||||
|
||||
var a = Foo("test")
|
||||
var b = Bar("test")
|
||||
var c = a < b
|
||||
`,
|
||||
err: "7:13: invalid operation: mismatched types main.Foo and main.Bar",
|
||||
},
|
||||
{src: `1 > _`, err: "1:28: cannot use _ as value"},
|
||||
{src: `(_) > 1`, err: "1:28: cannot use _ as value"},
|
||||
{src: `v := interface{}(2); v == 2`, res: "true"},
|
||||
{src: `v := interface{}(2); v > 1`, err: "1:49: invalid operation: operator > not defined on interface{}"},
|
||||
{src: `v := interface{}(int64(2)); v == 2`, res: "false"},
|
||||
{src: `v := interface{}(int64(2)); v != 2`, res: "true"},
|
||||
{src: `v := interface{}(2.3); v == 2.3`, res: "true"},
|
||||
{src: `v := interface{}(float32(2.3)); v != 2.3`, res: "true"},
|
||||
{src: `v := interface{}("hello"); v == "hello"`, res: "true"},
|
||||
{src: `v := interface{}("hello"); v < "hellp"`, err: "1:55: invalid operation: operator < not defined on interface{}"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -476,6 +520,8 @@ func TestEvalCompositeStruct(t *testing.T) {
|
||||
{src: `a := struct{A,B,C int}{A:1,A:2,C:3}`, err: "1:55: duplicate field name A in struct literal"},
|
||||
{src: `a := struct{A,B,C int}{A:1,B:2.2,C:3}`, err: "1:57: 11/5 truncated to int"},
|
||||
{src: `a := struct{A,B,C int}{A:1,2,C:3}`, err: "1:55: mixture of field:value and value elements in struct literal"},
|
||||
{src: `a := struct{A,B,C int}{1,2,_}`, err: "1:33: cannot use _ as value"},
|
||||
{src: `a := struct{A,B,C int}{B: _}`, err: "1:51: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -500,6 +546,8 @@ func TestEvalSliceExpression(t *testing.T) {
|
||||
{src: `a := []int{0,1,2,3}[1:3:]`, err: "1:51: 3rd index required in 3-index slice"},
|
||||
{src: `a := []int{0,1,2}[3:1]`, err: "invalid index values, must be low <= high <= max"},
|
||||
{pre: func() { eval(t, i, `type Str = string; var r Str = "truc"`) }, src: `r[1]`, res: "114"},
|
||||
{src: `_[12]`, err: "1:28: cannot use _ as value"},
|
||||
{src: `b := []int{0,1,2}[_:4]`, err: "1:33: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -510,6 +558,7 @@ func TestEvalConversion(t *testing.T) {
|
||||
{src: `i := 1.1; a := uint64(i)`, res: "1"},
|
||||
{src: `b := string(49)`, res: "1"},
|
||||
{src: `c := uint64(1.1)`, err: "1:40: cannot convert expression of type untyped float to type uint64"},
|
||||
{src: `int(_)`, err: "1:28: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -519,6 +568,9 @@ func TestEvalUnary(t *testing.T) {
|
||||
{src: "a := -1", res: "-1"},
|
||||
{src: "b := +1", res: "1", skip: "BUG"},
|
||||
{src: "c := !false", res: "true"},
|
||||
{src: "_ = 2; _++", err: "1:35: cannot use _ as value"},
|
||||
{src: "_ = false; !_ == true", err: "1:39: cannot use _ as value"},
|
||||
{src: "!((((_))))", err: "1:28: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -651,6 +703,7 @@ func TestEvalCall(t *testing.T) {
|
||||
{src: ` test := func(a, b int) int { return a }
|
||||
blah := func() (int, float64) { return 1, 1.1 }
|
||||
a := test(blah())`, err: "3:15: cannot use func() (int,float64) as type (int,int)"},
|
||||
{src: "func f()", err: "function declaration without body is unsupported"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -671,6 +724,29 @@ func TestEvalBinCall(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalReflect(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := i.Eval(`
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Encoder interface {
|
||||
EncodeValues(key string, v *url.Values) error
|
||||
}
|
||||
`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
runTests(t, i, []testCase{
|
||||
{src: "reflect.TypeOf(new(Encoder)).Elem()", res: "interp.valueInterface"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalMissingSymbol(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
@@ -937,7 +1013,7 @@ const goMinorVersionTest = 16
|
||||
|
||||
func TestHasIOFS(t *testing.T) {
|
||||
code := `
|
||||
// +build go1.16
|
||||
// +build go1.18
|
||||
|
||||
package main
|
||||
|
||||
@@ -975,6 +1051,8 @@ func main() {
|
||||
var minor int
|
||||
var err error
|
||||
version := runtime.Version()
|
||||
version = strings.Replace(version, "beta", ".", 1)
|
||||
version = strings.Replace(version, "rc", ".", 1)
|
||||
fields := strings.Fields(version)
|
||||
// Go stable
|
||||
if len(fields) == 1 {
|
||||
@@ -1337,7 +1415,7 @@ func testConcurrentComposite(t *testing.T, filePath string) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalScanner(t *testing.T) {
|
||||
func TestEvalREPL(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
@@ -1425,6 +1503,13 @@ func TestEvalScanner(t *testing.T) {
|
||||
},
|
||||
errorLine: -1,
|
||||
},
|
||||
{
|
||||
desc: "define a label",
|
||||
src: []string{
|
||||
`a:`,
|
||||
},
|
||||
errorLine: -1,
|
||||
},
|
||||
}
|
||||
|
||||
runREPL := func(t *testing.T, test testCase) {
|
||||
@@ -1517,10 +1602,8 @@ func TestREPLCommands(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
_ = os.Setenv("YAEGI_PROMPT", "1") // To force prompts over non-tty streams
|
||||
defer func() {
|
||||
_ = os.Setenv("YAEGI_PROMPT", "0")
|
||||
}()
|
||||
t.Setenv("YAEGI_PROMPT", "1") // To force prompts over non-tty streams
|
||||
|
||||
allDone := make(chan bool)
|
||||
runREPL := func() {
|
||||
done := make(chan error)
|
||||
@@ -1625,6 +1708,16 @@ func TestStdio(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoGoFiles(t *testing.T) {
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
|
||||
_, err := i.Eval(`import "github.com/traefik/yaegi/_test/p3"`)
|
||||
if strings.Contains(err.Error(), "no Go files in") {
|
||||
return
|
||||
}
|
||||
|
||||
t.Fatalf("failed to detect no Go files: %v", err)
|
||||
}
|
||||
|
||||
func TestIssue1142(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
@@ -1749,3 +1842,59 @@ func TestRestrictedEnv(t *testing.T) {
|
||||
t.Fatal("expected \"\", got " + s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue1388(t *testing.T) {
|
||||
i := interp.New(interp.Options{Env: []string{"foo=bar"}})
|
||||
err := i.Use(stdlib.Symbols)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = i.Eval(`x := errors.New("")`)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
|
||||
_, err = i.Eval(`import "errors"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = i.Eval(`x := errors.New("")`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue1383(t *testing.T) {
|
||||
const src = `
|
||||
package main
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello")
|
||||
}
|
||||
`
|
||||
|
||||
interp := interp.New(interp.Options{})
|
||||
err := interp.Use(stdlib.Symbols)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = interp.Eval(`import "fmt"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ast, err := parser.ParseFile(interp.FileSet(), "_.go", src, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prog, err := interp.CompileAST(ast)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = interp.Execute(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ func Hi(h Helloer) {
|
||||
//
|
||||
// Only the Wrap type definition needs to be exported to the interpreter (not
|
||||
// the interfaces and methods definitions).
|
||||
//
|
||||
type Wrap struct {
|
||||
DoHello func() // related to the Hello() method.
|
||||
// Other interface method wrappers...
|
||||
|
||||
@@ -19,10 +19,7 @@ func TestFile(t *testing.T) {
|
||||
filePath := "../_test/str.go"
|
||||
runCheck(t, filePath)
|
||||
|
||||
defer func() {
|
||||
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "0")
|
||||
}()
|
||||
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "1")
|
||||
t.Setenv("YAEGI_SPECIAL_STDIO", "1")
|
||||
|
||||
baseDir := filepath.Join("..", "_test")
|
||||
files, err := os.ReadDir(baseDir)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"log"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/traefik/yaegi/stdlib"
|
||||
)
|
||||
|
||||
func init() { log.SetFlags(log.Lshortfile) }
|
||||
@@ -19,13 +22,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x uint = 3
|
||||
return reflect.TypeOf(x)
|
||||
var a uint = 3
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x uint = 3
|
||||
return reflect.ValueOf(x)
|
||||
var a uint = 3
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: true,
|
||||
@@ -35,13 +38,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
x := 3
|
||||
return reflect.TypeOf(x)
|
||||
a := 3
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
x := 3
|
||||
return reflect.ValueOf(x)
|
||||
a := 3
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: true,
|
||||
@@ -51,13 +54,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x int = 3
|
||||
return reflect.TypeOf(x)
|
||||
var a int = 3
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x int = 3
|
||||
return reflect.ValueOf(x)
|
||||
var a int = 3
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: true,
|
||||
@@ -67,13 +70,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x float64 = 3.0
|
||||
return reflect.TypeOf(x)
|
||||
var a float64 = 3.0
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x float64 = 3.0
|
||||
return reflect.ValueOf(x)
|
||||
var a float64 = 3.0
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: true,
|
||||
@@ -83,13 +86,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x float64 = 3.14
|
||||
return reflect.TypeOf(x)
|
||||
var a float64 = 3.14
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x float64 = 3.14
|
||||
return reflect.ValueOf(x)
|
||||
var a float64 = 3.14
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: false,
|
||||
@@ -99,13 +102,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x int = -3
|
||||
return reflect.TypeOf(x)
|
||||
var a int = -3
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x int = -3
|
||||
return reflect.ValueOf(x)
|
||||
var a int = -3
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: false,
|
||||
@@ -188,3 +191,42 @@ func TestIsNatural(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobals(t *testing.T) {
|
||||
i := New(Options{})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := i.Eval("var a = 1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := i.Eval("b := 2"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := i.Eval("const c = 3"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
g := i.Globals()
|
||||
a := g["a"]
|
||||
if !a.IsValid() {
|
||||
t.Fatal("a not found")
|
||||
}
|
||||
if a := a.Interface(); a != 1 {
|
||||
t.Fatalf("wrong a: want (%[1]T) %[1]v, have (%[2]T) %[2]v", 1, a)
|
||||
}
|
||||
b := g["b"]
|
||||
if !b.IsValid() {
|
||||
t.Fatal("b not found")
|
||||
}
|
||||
if b := b.Interface(); b != 2 {
|
||||
t.Fatalf("wrong b: want (%[1]T) %[1]v, have (%[2]T) %[2]v", 2, b)
|
||||
}
|
||||
c := g["c"]
|
||||
if !c.IsValid() {
|
||||
t.Fatal("c not found")
|
||||
}
|
||||
if cc, ok := c.Interface().(constant.Value); ok && constant.MakeInt64(3) != cc {
|
||||
t.Fatalf("wrong c: want (%[1]T) %[1]v, have (%[2]T) %[2]v", constant.MakeInt64(3), cc)
|
||||
}
|
||||
}
|
||||
|
||||
78
interp/op.go
78
interp/op.go
@@ -2624,6 +2624,7 @@ func equal(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 c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
@@ -2709,7 +2710,37 @@ func equal(n *node) {
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
// 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 == 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 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -3193,8 +3224,9 @@ func greater(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()
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -3520,8 +3552,9 @@ func greaterEqual(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()
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -3847,8 +3880,9 @@ func lower(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()
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -4174,8 +4208,9 @@ func lowerEqual(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()
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -4501,6 +4536,7 @@ func notEqual(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 c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
@@ -4586,7 +4622,37 @@ func notEqual(n *node) {
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
// 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 != 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 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
|
||||
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"context"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@@ -16,6 +17,12 @@ type Program struct {
|
||||
init []*node
|
||||
}
|
||||
|
||||
// FileSet is the fileset that must be used for parsing Go that will be passed
|
||||
// to interp.CompileAST().
|
||||
func (interp *Interpreter) FileSet() *token.FileSet {
|
||||
return interp.fset
|
||||
}
|
||||
|
||||
// Compile parses and compiles a Go code represented as a string.
|
||||
func (interp *Interpreter) Compile(src string) (*Program, error) {
|
||||
return interp.compileSrc(src, "", true)
|
||||
@@ -55,6 +62,9 @@ func (interp *Interpreter) compileSrc(src, name string, inc bool) (*Program, err
|
||||
// CompileAST builds a Program for the given Go code AST. Files and block
|
||||
// statements can be compiled, as can most expressions. Var declaration nodes
|
||||
// cannot be compiled.
|
||||
//
|
||||
// WARNING: The node must have been parsed using interp.FileSet(). Results are
|
||||
// unpredictable otherwise.
|
||||
func (interp *Interpreter) CompileAST(n ast.Node) (*Program, error) {
|
||||
// Convert AST.
|
||||
pkgName, root, err := interp.ast(n)
|
||||
|
||||
247
interp/run.go
247
interp/run.go
@@ -9,7 +9,6 @@ import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// bltn type defines functions which run at CFG execution.
|
||||
@@ -554,7 +553,7 @@ func convert(n *node) {
|
||||
if c.isNil() { // convert nil to type
|
||||
// TODO(mpl): Try to completely remove, as maybe frameType already does the job for interfaces.
|
||||
if isInterfaceSrc(n.child[0].typ) && !isEmptyInterface(n.child[0].typ) {
|
||||
typ = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
typ = valueInterfaceType
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.New(typ).Elem())
|
||||
@@ -714,7 +713,7 @@ func assign(n *node) {
|
||||
case isFuncSrc(typ):
|
||||
t = reflect.TypeOf((*node)(nil))
|
||||
case isInterfaceSrc(typ):
|
||||
t = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
t = valueInterfaceType
|
||||
default:
|
||||
t = typ.TypeOf()
|
||||
}
|
||||
@@ -1008,7 +1007,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case isEmptyInterface(typ):
|
||||
case isEmptyInterface(typ) || typ.TypeOf() == valueInterfaceType:
|
||||
d[i].Set(arg)
|
||||
case isInterfaceSrc(typ):
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
@@ -1051,19 +1050,24 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
return value
|
||||
}
|
||||
}
|
||||
mn := typ.NumMethod()
|
||||
|
||||
// Retrieve methods from the interface wrapper, which is a struct where all fields
|
||||
// except the first define the methods to implement.
|
||||
// As the field name was generated with a prefixed first character (in order to avoid
|
||||
// collisions with method names), this first character is ignored in comparisons.
|
||||
wrap := getWrapper(n, typ)
|
||||
mn := wrap.NumField() - 1
|
||||
names := make([]string, mn)
|
||||
methods := make([]*node, mn)
|
||||
indexes := make([][]int, mn)
|
||||
for i := 0; i < mn; i++ {
|
||||
names[i] = typ.Method(i).Name
|
||||
names[i] = wrap.Field(i + 1).Name[1:]
|
||||
methods[i], indexes[i] = n.typ.lookupMethod(names[i])
|
||||
if methods[i] == nil && n.typ.cat != nilT {
|
||||
// interpreted method not found, look for binary method, possibly embedded
|
||||
_, indexes[i], _, _ = n.typ.lookupBinMethod(names[i])
|
||||
}
|
||||
}
|
||||
wrap := n.interp.getWrapper(typ)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
@@ -1229,12 +1233,14 @@ func call(n *node) {
|
||||
convertLiteralValue(c, argType)
|
||||
}
|
||||
switch {
|
||||
case isEmptyInterface(arg):
|
||||
case hasVariadicArgs:
|
||||
values = append(values, genValue(c))
|
||||
case isInterfaceSrc(arg) && !hasVariadicArgs:
|
||||
case isInterfaceSrc(arg) && (!isEmptyInterface(arg) || len(c.typ.method) > 0):
|
||||
values = append(values, genValueInterface(c))
|
||||
case isInterfaceBin(arg):
|
||||
values = append(values, genInterfaceWrapper(c, arg.rtype))
|
||||
case isFuncSrc(arg):
|
||||
values = append(values, genValueNode(c))
|
||||
default:
|
||||
values = append(values, genValue(c))
|
||||
}
|
||||
@@ -1404,12 +1410,22 @@ func call(n *node) {
|
||||
}
|
||||
default:
|
||||
val := v(f)
|
||||
// The !val.IsZero is to work around a recursive struct zero interface
|
||||
// issue. Once there is a better way to handle this case, the dest
|
||||
// can just be set.
|
||||
if !val.IsZero() || dest[i].Kind() == reflect.Interface {
|
||||
dest[i].Set(val)
|
||||
if val.IsZero() && dest[i].Kind() != reflect.Interface {
|
||||
// Work around a recursive struct zero interface issue.
|
||||
// Once there is a better way to handle this case, the dest can just be set.
|
||||
continue
|
||||
}
|
||||
if nod, ok := val.Interface().(*node); ok && nod.recv != nil {
|
||||
// An interpreted method is passed as value in a function call.
|
||||
// It must be wrapped now, otherwise the receiver will be missing
|
||||
// at the method call (#1332).
|
||||
// TODO (marc): wrapping interpreted functions should be always done
|
||||
// everywhere at runtime to simplify the whole code,
|
||||
// but it requires deeper refactoring.
|
||||
dest[i] = genFunctionWrapper(nod)(f)
|
||||
continue
|
||||
}
|
||||
dest[i].Set(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1468,6 +1484,20 @@ func callBin(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
// getMapType returns a reflect type suitable for interface wrapper for functions
|
||||
// with some special processing in case of interface{} argument, i.e. fmt.Printf.
|
||||
var getMapType func(*itype) reflect.Type
|
||||
if lr, ok := n.interp.mapTypes[c0.rval]; ok {
|
||||
getMapType = func(typ *itype) reflect.Type {
|
||||
for _, rt := range lr {
|
||||
if typ.implements(&itype{cat: valueT, rtype: rt}) {
|
||||
return rt
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we should use `Call` or `CallSlice` on the function Value.
|
||||
callFn := func(v reflect.Value, in []reflect.Value) []reflect.Value { return v.Call(in) }
|
||||
if n.action == aCallSlice {
|
||||
@@ -1475,13 +1505,6 @@ func callBin(n *node) {
|
||||
}
|
||||
|
||||
for i, c := range child {
|
||||
var defType reflect.Type
|
||||
if variadic >= 0 && i+rcvrOffset >= variadic {
|
||||
defType = funcType.In(variadic)
|
||||
} else {
|
||||
defType = funcType.In(rcvrOffset + i)
|
||||
}
|
||||
|
||||
switch {
|
||||
case isBinCall(c, c.scope):
|
||||
// Handle nested function calls: pass returned values as arguments
|
||||
@@ -1516,6 +1539,19 @@ func callBin(n *node) {
|
||||
break
|
||||
}
|
||||
|
||||
// defType is the target type for a potential interface wrapper.
|
||||
var defType reflect.Type
|
||||
if variadic >= 0 && i+rcvrOffset >= variadic {
|
||||
defType = funcType.In(variadic)
|
||||
} else {
|
||||
defType = funcType.In(rcvrOffset + i)
|
||||
}
|
||||
if getMapType != nil {
|
||||
if rt := getMapType(c.typ); rt != nil {
|
||||
defType = rt
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case isFuncSrc(c.typ):
|
||||
values = append(values, genFunctionWrapper(c))
|
||||
@@ -1524,12 +1560,9 @@ func callBin(n *node) {
|
||||
case isInterfaceSrc(c.typ):
|
||||
values = append(values, genValueInterfaceValue(c))
|
||||
case c.typ.cat == arrayT || c.typ.cat == variadicT:
|
||||
switch {
|
||||
case isEmptyInterface(c.typ.val):
|
||||
if isEmptyInterface(c.typ.val) {
|
||||
values = append(values, genValueArray(c))
|
||||
case isInterfaceSrc(c.typ.val):
|
||||
values = append(values, genValueInterfaceArray(c))
|
||||
default:
|
||||
} else {
|
||||
values = append(values, genInterfaceWrapper(c, defType))
|
||||
}
|
||||
case isPtrSrc(c.typ):
|
||||
@@ -1554,7 +1587,7 @@ func callBin(n *node) {
|
||||
val := make([]reflect.Value, l+1)
|
||||
val[0] = value(f)
|
||||
for i, v := range values {
|
||||
val[i+1] = v(f)
|
||||
val[i+1] = getBinValue(getMapType, v, f)
|
||||
}
|
||||
f.deferred = append([][]reflect.Value{val}, f.deferred...)
|
||||
return tnext
|
||||
@@ -1564,7 +1597,7 @@ func callBin(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
in[i] = getBinValue(getMapType, v, f)
|
||||
}
|
||||
go callFn(value(f), in)
|
||||
return tnext
|
||||
@@ -1576,7 +1609,7 @@ func callBin(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
in[i] = getBinValue(getMapType, v, f)
|
||||
}
|
||||
res := callFn(value(f), in)
|
||||
b := res[0].Bool()
|
||||
@@ -1608,7 +1641,7 @@ func callBin(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
in[i] = getBinValue(getMapType, v, f)
|
||||
}
|
||||
out := callFn(value(f), in)
|
||||
for i, v := range rvalues {
|
||||
@@ -1625,7 +1658,7 @@ func callBin(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
in[i] = getBinValue(getMapType, v, f)
|
||||
}
|
||||
out := callFn(value(f), in)
|
||||
for i, v := range out {
|
||||
@@ -1641,7 +1674,7 @@ func callBin(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
in[i] = getBinValue(getMapType, v, f)
|
||||
}
|
||||
out := callFn(value(f), in)
|
||||
for i := 0; i < len(out); i++ {
|
||||
@@ -1755,9 +1788,6 @@ func getIndexArray(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
// valueInterfaceType is the reflection type of valueInterface.
|
||||
var valueInterfaceType = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
|
||||
// getIndexMap retrieves map value from index.
|
||||
func getIndexMap(n *node) {
|
||||
dest := genValue(n)
|
||||
@@ -1822,7 +1852,6 @@ func getIndexMap2(n *node) {
|
||||
value0 := genValue(n.child[0]) // map
|
||||
value2 := genValue(n.anc.child[1]) // status
|
||||
next := getExec(n.tnext)
|
||||
typ := n.anc.child[0].typ
|
||||
doValue := n.anc.child[0].ident != "_"
|
||||
doStatus := n.anc.child[1].ident != "_"
|
||||
|
||||
@@ -1839,21 +1868,6 @@ func getIndexMap2(n *node) {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
case isInterfaceSrc(typ):
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
if v.IsValid() {
|
||||
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
|
||||
dest(f).Set(e)
|
||||
} else {
|
||||
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
|
||||
}
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
@@ -1875,21 +1889,6 @@ func getIndexMap2(n *node) {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
case isInterfaceSrc(typ):
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
if v.IsValid() {
|
||||
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
|
||||
dest(f).Set(e)
|
||||
} else {
|
||||
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
|
||||
}
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
@@ -1953,10 +1952,12 @@ func getMethodByName(n *node) {
|
||||
}
|
||||
val = v
|
||||
}
|
||||
|
||||
if met := val.value.MethodByName(name); met.IsValid() {
|
||||
getFrame(f, l).data[i] = met
|
||||
return next
|
||||
}
|
||||
|
||||
typ := val.node.typ
|
||||
if typ.node == nil && typ.cat == valueT {
|
||||
// happens with a var of empty interface type, that has value of concrete type
|
||||
@@ -1966,10 +1967,32 @@ func getMethodByName(n *node) {
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
m, li := typ.lookupMethod(name)
|
||||
|
||||
// Try harder to find a matching embedded valueInterface.
|
||||
// TODO (marc): make sure it works for arbitrary depth and breadth.
|
||||
if m == nil && isStruct(val.node.typ) {
|
||||
v := val.value
|
||||
for v.Type().Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
nf := v.NumField()
|
||||
for i := 0; i < nf; i++ {
|
||||
var ok bool
|
||||
if val, ok = v.Field(i).Interface().(valueInterface); !ok {
|
||||
continue
|
||||
}
|
||||
if m, li = val.node.typ.lookupMethod(name); m != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
panic(n.cfgErrorf("method not found: %s", name))
|
||||
}
|
||||
|
||||
fr := f.clone(!fork)
|
||||
nod := *m
|
||||
nod.val = &nod
|
||||
@@ -2614,6 +2637,9 @@ func doCompositeBinStruct(n *node, hasType bool) {
|
||||
}
|
||||
}
|
||||
|
||||
frameIndex := n.findex
|
||||
l := n.level
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
s := reflect.New(typ).Elem()
|
||||
for i, v := range values {
|
||||
@@ -2624,7 +2650,7 @@ func doCompositeBinStruct(n *node, hasType bool) {
|
||||
case d.Kind() == reflect.Ptr:
|
||||
d.Set(s.Addr())
|
||||
default:
|
||||
d.Set(s)
|
||||
getFrame(f, l).data[frameIndex] = s
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -2649,8 +2675,6 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
if typ.cat == ptrT || typ.cat == aliasT {
|
||||
typ = typ.val
|
||||
}
|
||||
var mu sync.Mutex
|
||||
typ.mu = &mu
|
||||
child := n.child
|
||||
if hasType {
|
||||
child = n.child[1:]
|
||||
@@ -2676,9 +2700,7 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
values[fieldIndex] = func(*frame) reflect.Value { return reflect.New(rft).Elem() }
|
||||
case isFuncSrc(val.typ):
|
||||
values[fieldIndex] = genValueAsFunctionWrapper(val)
|
||||
case isArray(val.typ) && val.typ.val != nil && isInterfaceSrc(val.typ.val) && !isEmptyInterface(val.typ.val):
|
||||
values[fieldIndex] = genValueInterfaceArray(val)
|
||||
case isInterfaceSrc(ft) && !isEmptyInterface(ft):
|
||||
case isInterfaceSrc(ft) && (!isEmptyInterface(ft) || len(val.typ.method) > 0):
|
||||
values[fieldIndex] = genValueInterface(val)
|
||||
case isInterface(ft):
|
||||
values[fieldIndex] = genInterfaceWrapper(val, rft)
|
||||
@@ -2689,11 +2711,10 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
|
||||
frameIndex := n.findex
|
||||
l := n.level
|
||||
rt := typ.TypeOf()
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
typ.mu.Lock()
|
||||
// No need to call zero() as doComposite is only called for a structT.
|
||||
a := reflect.New(typ.TypeOf()).Elem()
|
||||
typ.mu.Unlock()
|
||||
a := reflect.New(rt).Elem()
|
||||
for i, v := range values {
|
||||
a.Field(i).Set(v(f))
|
||||
}
|
||||
@@ -2956,13 +2977,21 @@ func _case(n *node) {
|
||||
return tnext
|
||||
}
|
||||
rtyp := typ.TypeOf()
|
||||
if rtyp != nil && rtyp.String() == t.String() && implementsInterface(v, typ) {
|
||||
destValue(f).Set(v.Elem())
|
||||
if rtyp == nil {
|
||||
return fnext
|
||||
}
|
||||
elem := v.Elem()
|
||||
if rtyp.String() == t.String() && implementsInterface(v, typ) {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
ival := v.Interface()
|
||||
if ival != nil && rtyp != nil && rtyp.String() == reflect.TypeOf(ival).String() {
|
||||
destValue(f).Set(v.Elem())
|
||||
if ival != nil && rtyp.String() == reflect.TypeOf(ival).String() {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
if typ.cat == valueT && rtyp.Kind() == reflect.Interface && elem.IsValid() && elem.Type().Implements(rtyp) {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -2980,12 +3009,37 @@ func _case(n *node) {
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
|
||||
default:
|
||||
// TODO(mpl): probably needs to be fixed for empty interfaces, like above.
|
||||
// match against multiple types: assign var to interface value
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := srcValue(f)
|
||||
if v := srcValue(f).Interface().(valueInterface).node; v != nil {
|
||||
if t := val.Type(); t.Kind() == reflect.Interface {
|
||||
for _, typ := range types {
|
||||
if typ.cat == nilT && val.IsNil() {
|
||||
return tnext
|
||||
}
|
||||
rtyp := typ.TypeOf()
|
||||
if rtyp == nil {
|
||||
continue
|
||||
}
|
||||
elem := val.Elem()
|
||||
if rtyp.String() == t.String() && implementsInterface(val, typ) {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
ival := val.Interface()
|
||||
if ival != nil && rtyp.String() == reflect.TypeOf(ival).String() {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
if typ.cat == valueT && rtyp.Kind() == reflect.Interface && elem.IsValid() && elem.Type().Implements(rtyp) {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if v := val.Interface().(valueInterface).node; v != nil {
|
||||
for _, typ := range types {
|
||||
if v.typ.id() == typ.id() {
|
||||
destValue(f).Set(val)
|
||||
@@ -3087,9 +3141,7 @@ func _append(n *node) {
|
||||
values := make([]func(*frame) reflect.Value, l)
|
||||
for i, arg := range args {
|
||||
switch elem := n.typ.elem(); {
|
||||
case isEmptyInterface(elem):
|
||||
values[i] = genValue(arg)
|
||||
case isInterfaceSrc(elem):
|
||||
case isInterfaceSrc(elem) && (!isEmptyInterface(elem) || len(arg.typ.method) > 0):
|
||||
values[i] = genValueInterface(arg)
|
||||
case isInterfaceBin(elem):
|
||||
values[i] = genInterfaceWrapper(arg, elem.rtype)
|
||||
@@ -3111,9 +3163,7 @@ func _append(n *node) {
|
||||
default:
|
||||
var value0 func(*frame) reflect.Value
|
||||
switch elem := n.typ.elem(); {
|
||||
case isEmptyInterface(elem):
|
||||
value0 = genValue(n.child[2])
|
||||
case isInterfaceSrc(elem):
|
||||
case isInterfaceSrc(elem) && (!isEmptyInterface(elem) || len(n.child[2].typ.method) > 0):
|
||||
value0 = genValueInterface(n.child[2])
|
||||
case isInterfaceBin(elem):
|
||||
value0 = genInterfaceWrapper(n.child[2], elem.rtype)
|
||||
@@ -3297,11 +3347,20 @@ func _len(n *node) {
|
||||
|
||||
func _new(n *node) {
|
||||
next := getExec(n.tnext)
|
||||
typ := n.child[1].typ.TypeOf()
|
||||
t1 := n.child[1].typ
|
||||
typ := t1.TypeOf()
|
||||
dest := genValueOutput(n, reflect.PtrTo(typ))
|
||||
|
||||
if isInterfaceSrc(t1) && (!isEmptyInterface(t1) || len(t1.method) > 0) {
|
||||
typ = zeroInterfaceValue().Type()
|
||||
}
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.New(typ))
|
||||
v := reflect.New(typ)
|
||||
if vi, ok := v.Interface().(*valueInterface); ok {
|
||||
vi.node = n
|
||||
}
|
||||
dest(f).Set(v)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -3521,7 +3580,7 @@ func convertLiteralValue(n *node, t reflect.Type) {
|
||||
case n.typ.cat == nilT:
|
||||
// Create a zero value of target type.
|
||||
n.rval = reflect.New(t).Elem()
|
||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface || t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface:
|
||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface || t == valueInterfaceType || t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface:
|
||||
// Skip non-constant values, undefined target type or interface target type.
|
||||
case n.rval.IsValid():
|
||||
// Convert constant value to target type.
|
||||
@@ -3882,7 +3941,7 @@ func isNotNil(n *node) {
|
||||
dest := genValue(n)
|
||||
|
||||
if n.fnext == nil {
|
||||
if isInterfaceSrc(c0.typ) {
|
||||
if isInterfaceSrc(c0.typ) && c0.typ.TypeOf() != valueInterfaceType {
|
||||
if isInterface {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(!value(f).IsNil()).Convert(typ))
|
||||
@@ -3927,7 +3986,7 @@ func isNotNil(n *node) {
|
||||
|
||||
fnext := getExec(n.fnext)
|
||||
|
||||
if isInterfaceSrc(c0.typ) {
|
||||
if isInterfaceSrc(c0.typ) && c0.typ.TypeOf() != valueInterfaceType {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(false)
|
||||
|
||||
@@ -11,27 +11,29 @@ type sKind uint
|
||||
|
||||
// Symbol kinds for the Go interpreter.
|
||||
const (
|
||||
undefSym sKind = iota
|
||||
binSym // Binary from runtime
|
||||
bltnSym // Builtin
|
||||
constSym // Constant
|
||||
funcSym // Function
|
||||
labelSym // Label
|
||||
pkgSym // Package
|
||||
typeSym // Type
|
||||
varSym // Variable
|
||||
undefSym sKind = iota
|
||||
binSym // Binary from runtime
|
||||
bltnSym // Builtin
|
||||
constSym // Constant
|
||||
funcSym // Function
|
||||
labelSym // Label
|
||||
pkgSym // Package
|
||||
typeSym // Type
|
||||
varTypeSym // Variable type (generic)
|
||||
varSym // Variable
|
||||
)
|
||||
|
||||
var symKinds = [...]string{
|
||||
undefSym: "undefSym",
|
||||
binSym: "binSym",
|
||||
bltnSym: "bltnSym",
|
||||
constSym: "constSym",
|
||||
funcSym: "funcSym",
|
||||
labelSym: "labelSym",
|
||||
pkgSym: "pkgSym",
|
||||
typeSym: "typeSym",
|
||||
varSym: "varSym",
|
||||
undefSym: "undefSym",
|
||||
binSym: "binSym",
|
||||
bltnSym: "bltnSym",
|
||||
constSym: "constSym",
|
||||
funcSym: "funcSym",
|
||||
labelSym: "labelSym",
|
||||
pkgSym: "pkgSym",
|
||||
typeSym: "typeSym",
|
||||
varTypeSym: "varTypeSym",
|
||||
varSym: "varSym",
|
||||
}
|
||||
|
||||
func (k sKind) String() string {
|
||||
@@ -47,7 +49,7 @@ type symbol struct {
|
||||
kind sKind
|
||||
typ *itype // Type of value
|
||||
node *node // Node value if index is negative
|
||||
from []*node // list of nodes jumping to node if kind is label, or nil
|
||||
from []*node // list of goto nodes jumping to this label node, or nil
|
||||
recv *receiver // receiver node value, if sym refers to a method
|
||||
index int // index of value in frame or -1
|
||||
rval reflect.Value // default value (used for constants)
|
||||
@@ -70,7 +72,6 @@ type symbol struct {
|
||||
//
|
||||
// In symbols, the index value corresponds to the index in scope.types, and at
|
||||
// execution to the index in frame, created exactly from the types layout.
|
||||
//
|
||||
type scope struct {
|
||||
anc *scope // ancestor upper scope
|
||||
child []*scope // included scopes
|
||||
@@ -144,20 +145,6 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
// lookdown searches for a symbol in the current scope and included ones, recursively.
|
||||
// It returns the first found symbol and true, or nil and false.
|
||||
func (s *scope) lookdown(ident string) (*symbol, bool) {
|
||||
if sym, ok := s.sym[ident]; ok {
|
||||
return sym, true
|
||||
}
|
||||
for _, c := range s.child {
|
||||
if sym, ok := c.lookdown(ident); ok {
|
||||
return sym, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s *scope) rangeChanType(n *node) *itype {
|
||||
if sym, _, found := s.lookup(n.child[1].ident); found {
|
||||
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {
|
||||
@@ -241,3 +228,26 @@ func (interp *Interpreter) initScopePkg(pkgID, pkgName string) *scope {
|
||||
interp.mutex.Unlock()
|
||||
return sc
|
||||
}
|
||||
|
||||
// Globals returns a map of global variables and constants in the main package.
|
||||
func (interp *Interpreter) Globals() map[string]reflect.Value {
|
||||
syms := map[string]reflect.Value{}
|
||||
interp.mutex.RLock()
|
||||
defer interp.mutex.RUnlock()
|
||||
|
||||
v, ok := interp.srcPkg["main"]
|
||||
if !ok {
|
||||
return syms
|
||||
}
|
||||
|
||||
for n, s := range v {
|
||||
switch s.kind {
|
||||
case constSym:
|
||||
syms[n] = s.rval
|
||||
case varSym:
|
||||
syms[n] = interp.frame.data[s.index]
|
||||
}
|
||||
}
|
||||
|
||||
return syms
|
||||
}
|
||||
|
||||
@@ -132,6 +132,10 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
// the global symbols in the package scope.
|
||||
interp.mutex.Lock()
|
||||
gs := interp.scopes[importPath]
|
||||
if gs == nil {
|
||||
// A nil scope means that no even an empty package is created from source.
|
||||
return "", fmt.Errorf("no Go files in %s", dir)
|
||||
}
|
||||
interp.srcPkg[importPath] = gs.sym
|
||||
interp.pkgNames[importPath] = pkgName
|
||||
|
||||
|
||||
366
interp/type.go
366
interp/type.go
@@ -7,7 +7,6 @@ import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/traefik/yaegi/internal/unsafe2"
|
||||
)
|
||||
@@ -27,12 +26,15 @@ const (
|
||||
chanT
|
||||
chanSendT
|
||||
chanRecvT
|
||||
comparableT
|
||||
complex64T
|
||||
complex128T
|
||||
constraintT
|
||||
errorT
|
||||
float32T
|
||||
float64T
|
||||
funcT
|
||||
genericT
|
||||
interfaceT
|
||||
intT
|
||||
int8T
|
||||
@@ -65,8 +67,10 @@ var cats = [...]string{
|
||||
boolT: "boolT",
|
||||
builtinT: "builtinT",
|
||||
chanT: "chanT",
|
||||
comparableT: "comparableT",
|
||||
complex64T: "complex64T",
|
||||
complex128T: "complex128T",
|
||||
constraintT: "constraintT",
|
||||
errorT: "errorT",
|
||||
float32T: "float32",
|
||||
float64T: "float64T",
|
||||
@@ -78,6 +82,7 @@ var cats = [...]string{
|
||||
int32T: "int32T",
|
||||
int64T: "int64T",
|
||||
mapT: "mapT",
|
||||
genericT: "genericT",
|
||||
ptrT: "ptrT",
|
||||
sliceT: "sliceT",
|
||||
srcPkgT: "srcPkgT",
|
||||
@@ -110,50 +115,53 @@ type structField struct {
|
||||
|
||||
// itype defines the internal representation of types in the interpreter.
|
||||
type itype struct {
|
||||
mu *sync.Mutex
|
||||
cat tcat // Type category
|
||||
field []structField // Array of struct fields if structT or interfaceT
|
||||
key *itype // Type of key element if MapT or nil
|
||||
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT, sliceT or variadicT
|
||||
recv *itype // Receiver type for funcT or nil
|
||||
arg []*itype // Argument types if funcT or nil
|
||||
ret []*itype // Return types if funcT or nil
|
||||
ptr *itype // Pointer to this type. Might be nil
|
||||
method []*node // Associated methods or nil
|
||||
name string // name of type within its package for a defined type
|
||||
path string // for a defined type, the package import path
|
||||
length int // length of array if ArrayT
|
||||
rtype reflect.Type // Reflection type if ValueT, or nil
|
||||
node *node // root AST node of type definition
|
||||
scope *scope // type declaration scope (in case of re-parse incomplete type)
|
||||
str string // String representation of the type
|
||||
incomplete bool // true if type must be parsed again (out of order declarations)
|
||||
untyped bool // true for a literal value (string or number)
|
||||
isBinMethod bool // true if the type refers to a bin method function
|
||||
cat tcat // Type category
|
||||
field []structField // Array of struct fields if structT or interfaceT
|
||||
key *itype // Type of key element if MapT or nil
|
||||
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT, sliceT, variadicT or genericT
|
||||
recv *itype // Receiver type for funcT or nil
|
||||
arg []*itype // Argument types if funcT or nil
|
||||
ret []*itype // Return types if funcT or nil
|
||||
ptr *itype // Pointer to this type. Might be nil
|
||||
method []*node // Associated methods or nil
|
||||
constraint []*itype // For interfaceT: list of types part of interface set
|
||||
ulconstraint []*itype // For interfaceT: list of underlying types part of interface set
|
||||
name string // name of type within its package for a defined type
|
||||
path string // for a defined type, the package import path
|
||||
length int // length of array if ArrayT
|
||||
rtype reflect.Type // Reflection type if ValueT, or nil
|
||||
node *node // root AST node of type definition
|
||||
scope *scope // type declaration scope (in case of re-parse incomplete type)
|
||||
str string // String representation of the type
|
||||
incomplete bool // true if type must be parsed again (out of order declarations)
|
||||
untyped bool // true for a literal value (string or number)
|
||||
isBinMethod bool // true if the type refers to a bin method function
|
||||
}
|
||||
|
||||
func untypedBool() *itype {
|
||||
return &itype{cat: boolT, name: "bool", untyped: true, str: "untyped bool"}
|
||||
type generic struct{}
|
||||
|
||||
func untypedBool(n *node) *itype {
|
||||
return &itype{cat: boolT, name: "bool", untyped: true, str: "untyped bool", node: n}
|
||||
}
|
||||
|
||||
func untypedString() *itype {
|
||||
return &itype{cat: stringT, name: "string", untyped: true, str: "untyped string"}
|
||||
func untypedString(n *node) *itype {
|
||||
return &itype{cat: stringT, name: "string", untyped: true, str: "untyped string", node: n}
|
||||
}
|
||||
|
||||
func untypedRune() *itype {
|
||||
return &itype{cat: int32T, name: "int32", untyped: true, str: "untyped rune"}
|
||||
func untypedRune(n *node) *itype {
|
||||
return &itype{cat: int32T, name: "int32", untyped: true, str: "untyped rune", node: n}
|
||||
}
|
||||
|
||||
func untypedInt() *itype {
|
||||
return &itype{cat: intT, name: "int", untyped: true, str: "untyped int"}
|
||||
func untypedInt(n *node) *itype {
|
||||
return &itype{cat: intT, name: "int", untyped: true, str: "untyped int", node: n}
|
||||
}
|
||||
|
||||
func untypedFloat() *itype {
|
||||
return &itype{cat: float64T, name: "float64", untyped: true, str: "untyped float"}
|
||||
func untypedFloat(n *node) *itype {
|
||||
return &itype{cat: float64T, name: "float64", untyped: true, str: "untyped float", node: n}
|
||||
}
|
||||
|
||||
func untypedComplex() *itype {
|
||||
return &itype{cat: complex128T, name: "complex128", untyped: true, str: "untyped complex"}
|
||||
func untypedComplex(n *node) *itype {
|
||||
return &itype{cat: complex128T, name: "complex128", untyped: true, str: "untyped complex", node: n}
|
||||
}
|
||||
|
||||
func errorMethodType(sc *scope) *itype {
|
||||
@@ -240,9 +248,6 @@ func namedOf(val *itype, path, name string, opts ...itypeOption) *itype {
|
||||
if path != "" {
|
||||
str = path + "." + name
|
||||
}
|
||||
for val.cat == aliasT {
|
||||
val = val.val
|
||||
}
|
||||
t := &itype{cat: aliasT, val: val, path: path, name: name, str: str}
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
@@ -330,7 +335,7 @@ func mapOf(key, val *itype, opts ...itypeOption) *itype {
|
||||
}
|
||||
|
||||
// interfaceOf returns an interface type with the given fields.
|
||||
func interfaceOf(t *itype, fields []structField, opts ...itypeOption) *itype {
|
||||
func interfaceOf(t *itype, fields []structField, constraint, ulconstraint []*itype, opts ...itypeOption) *itype {
|
||||
str := "interface{}"
|
||||
if len(fields) > 0 {
|
||||
str = "interface { " + methodsTypeString(fields) + "}"
|
||||
@@ -340,6 +345,8 @@ func interfaceOf(t *itype, fields []structField, opts ...itypeOption) *itype {
|
||||
}
|
||||
t.cat = interfaceT
|
||||
t.field = fields
|
||||
t.constraint = constraint
|
||||
t.ulconstraint = ulconstraint
|
||||
t.str = str
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
@@ -365,6 +372,15 @@ func structOf(t *itype, fields []structField, opts ...itypeOption) *itype {
|
||||
return t
|
||||
}
|
||||
|
||||
// genericOf returns a generic type.
|
||||
func genericOf(val *itype, name string, opts ...itypeOption) *itype {
|
||||
t := &itype{cat: genericT, name: name, str: name, val: val}
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// seenNode determines if a node has been seen.
|
||||
//
|
||||
// seenNode treats the slice of nodes as the path traveled down a node
|
||||
@@ -481,24 +497,24 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
switch v := n.rval.Interface().(type) {
|
||||
case bool:
|
||||
n.rval = reflect.ValueOf(constant.MakeBool(v))
|
||||
t = untypedBool()
|
||||
t = untypedBool(n)
|
||||
case rune:
|
||||
// It is impossible to work out rune const literals in AST
|
||||
// with the correct type so we must make the const type here.
|
||||
n.rval = reflect.ValueOf(constant.MakeInt64(int64(v)))
|
||||
t = untypedRune()
|
||||
t = untypedRune(n)
|
||||
case constant.Value:
|
||||
switch v.Kind() {
|
||||
case constant.Bool:
|
||||
t = untypedBool()
|
||||
t = untypedBool(n)
|
||||
case constant.String:
|
||||
t = untypedString()
|
||||
t = untypedString(n)
|
||||
case constant.Int:
|
||||
t = untypedInt()
|
||||
t = untypedInt(n)
|
||||
case constant.Float:
|
||||
t = untypedFloat()
|
||||
t = untypedFloat(n)
|
||||
case constant.Complex:
|
||||
t = untypedComplex()
|
||||
t = untypedComplex(n)
|
||||
default:
|
||||
err = n.cfgErrorf("missing support for type %v", n.rval)
|
||||
}
|
||||
@@ -507,9 +523,36 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
}
|
||||
|
||||
case unaryExpr:
|
||||
// In interfaceType, we process an underlying type constraint definition.
|
||||
if isInInterfaceType(n) {
|
||||
t1, err := nodeType2(interp, sc, n.child[0], seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t = &itype{cat: constraintT, ulconstraint: []*itype{t1}}
|
||||
break
|
||||
}
|
||||
t, err = nodeType2(interp, sc, n.child[0], seen)
|
||||
|
||||
case binaryExpr:
|
||||
// In interfaceType, we process a type constraint union definition.
|
||||
if isInInterfaceType(n) {
|
||||
t = &itype{cat: constraintT, constraint: []*itype{}, ulconstraint: []*itype{}}
|
||||
for _, c := range n.child {
|
||||
t1, err := nodeType2(interp, sc, c, seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t1.cat {
|
||||
case constraintT:
|
||||
t.constraint = append(t.constraint, t1.constraint...)
|
||||
t.ulconstraint = append(t.ulconstraint, t1.ulconstraint...)
|
||||
default:
|
||||
t.constraint = append(t.constraint, t1)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
// Get type of first operand.
|
||||
if t, err = nodeType2(interp, sc, n.child[0], seen); err != nil {
|
||||
return nil, err
|
||||
@@ -569,7 +612,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
case isFloat64(t0) && isFloat64(t1):
|
||||
t = sc.getType("complex128")
|
||||
case nt0.untyped && isNumber(t0) && nt1.untyped && isNumber(t1):
|
||||
t = untypedComplex()
|
||||
t = untypedComplex(n)
|
||||
case nt0.untyped && isFloat32(t1) || nt1.untyped && isFloat32(t0):
|
||||
t = sc.getType("complex64")
|
||||
case nt0.untyped && isFloat64(t1) || nt1.untyped && isFloat64(t0):
|
||||
@@ -578,7 +621,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
err = n.cfgErrorf("invalid types %s and %s", t0.Kind(), t1.Kind())
|
||||
}
|
||||
if nt0.untyped && nt1.untyped {
|
||||
t = untypedComplex()
|
||||
t = untypedComplex(n)
|
||||
}
|
||||
}
|
||||
case bltnReal, bltnImag:
|
||||
@@ -587,12 +630,12 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
}
|
||||
if !t.incomplete {
|
||||
switch k := t.TypeOf().Kind(); {
|
||||
case t.untyped && isNumber(t.TypeOf()):
|
||||
t = untypedFloat(n)
|
||||
case k == reflect.Complex64:
|
||||
t = sc.getType("float32")
|
||||
case k == reflect.Complex128:
|
||||
t = sc.getType("float64")
|
||||
case t.untyped && isNumber(t.TypeOf()):
|
||||
t = valueTOf(floatType, withUntyped(true), withScope(sc))
|
||||
default:
|
||||
err = n.cfgErrorf("invalid complex type %s", k)
|
||||
}
|
||||
@@ -661,34 +704,48 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
|
||||
case funcType:
|
||||
var incomplete bool
|
||||
// Handle input parameters
|
||||
args := make([]*itype, 0, len(n.child[0].child))
|
||||
|
||||
// Handle type parameters.
|
||||
for _, arg := range n.child[0].child {
|
||||
cl := len(arg.child) - 1
|
||||
typ, err := nodeType2(interp, sc, arg.child[cl], seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range arg.child[:cl] {
|
||||
sc.sym[c.ident] = &symbol{index: -1, kind: varTypeSym, typ: typ}
|
||||
}
|
||||
incomplete = incomplete || typ.incomplete
|
||||
}
|
||||
|
||||
// Handle input parameters.
|
||||
args := make([]*itype, 0, len(n.child[1].child))
|
||||
for _, arg := range n.child[1].child {
|
||||
cl := len(arg.child) - 1
|
||||
typ, err := nodeType2(interp, sc, arg.child[cl], seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args = append(args, typ)
|
||||
// Several arguments may be factorized on the same field type.
|
||||
for i := 1; i < cl; i++ {
|
||||
// Several arguments may be factorized on the same field type
|
||||
args = append(args, typ)
|
||||
}
|
||||
incomplete = incomplete || typ.incomplete
|
||||
}
|
||||
|
||||
// Handle returned values.
|
||||
var rets []*itype
|
||||
if len(n.child) == 2 {
|
||||
// Handle returned values
|
||||
for _, ret := range n.child[1].child {
|
||||
if len(n.child) == 3 {
|
||||
for _, ret := range n.child[2].child {
|
||||
cl := len(ret.child) - 1
|
||||
typ, err := nodeType2(interp, sc, ret.child[cl], seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, typ)
|
||||
// Several arguments may be factorized on the same field type.
|
||||
for i := 1; i < cl; i++ {
|
||||
// Several arguments may be factorized on the same field type
|
||||
rets = append(rets, typ)
|
||||
}
|
||||
incomplete = incomplete || typ.incomplete
|
||||
@@ -710,7 +767,11 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
break
|
||||
}
|
||||
}
|
||||
t = sym.typ
|
||||
if sym.kind == varTypeSym {
|
||||
t = genericOf(sym.typ, n.ident, withNode(n), withScope(sc))
|
||||
} else {
|
||||
t = sym.typ
|
||||
}
|
||||
if t.incomplete && t.cat == aliasT && t.val != nil && t.val.cat != nilT {
|
||||
t.incomplete = false
|
||||
}
|
||||
@@ -738,42 +799,102 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
switch lt.cat {
|
||||
case arrayT, mapT, sliceT, variadicT:
|
||||
t = lt.val
|
||||
case genericT:
|
||||
t1, err := nodeType2(interp, sc, n.child[1], seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t1.cat == genericT || t1.incomplete {
|
||||
t = lt
|
||||
break
|
||||
}
|
||||
name := lt.id() + "[" + t1.id() + "]"
|
||||
if sym, _, found := sc.lookup(name); found {
|
||||
t = sym.typ
|
||||
break
|
||||
}
|
||||
// A generic type is being instantiated. Generate it.
|
||||
g, err := genAST(sc, lt.node.anc, []*node{t1.node})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, err = nodeType2(interp, sc, g.lastChild(), seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sc.sym[name] = &symbol{index: -1, kind: typeSym, typ: t, node: g}
|
||||
|
||||
// Instantiate type methods (if any).
|
||||
var pt *itype
|
||||
if len(lt.method) > 0 {
|
||||
pt = ptrOf(t, withNode(g), withScope(sc))
|
||||
}
|
||||
for _, nod := range lt.method {
|
||||
gm, err := genAST(sc, nod, []*node{t1.node})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gm.typ, err = nodeType(interp, sc, gm.child[2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.addMethod(gm)
|
||||
if rtn := gm.child[0].child[0].lastChild(); rtn.kind == starExpr {
|
||||
// The receiver is a pointer on a generic type.
|
||||
pt.addMethod(gm)
|
||||
rtn.typ = pt
|
||||
}
|
||||
// Compile method CFG.
|
||||
if _, err = interp.cfg(gm, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Generate closures for function body.
|
||||
if err = genRun(gm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case interfaceType:
|
||||
if sname := typeName(n); sname != "" {
|
||||
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
|
||||
t = interfaceOf(sym.typ, sym.typ.field, withNode(n), withScope(sc))
|
||||
t = interfaceOf(sym.typ, sym.typ.field, sym.typ.constraint, sym.typ.ulconstraint, withNode(n), withScope(sc))
|
||||
}
|
||||
}
|
||||
var incomplete bool
|
||||
fields := make([]structField, 0, len(n.child[0].child))
|
||||
for _, field := range n.child[0].child {
|
||||
f0 := field.child[0]
|
||||
if len(field.child) == 1 {
|
||||
if f0.ident == "error" {
|
||||
fields := []structField{}
|
||||
constraint := []*itype{}
|
||||
ulconstraint := []*itype{}
|
||||
for _, c := range n.child[0].child {
|
||||
c0 := c.child[0]
|
||||
if len(c.child) == 1 {
|
||||
if c0.ident == "error" {
|
||||
// Unwrap error interface inplace rather than embedding it, because
|
||||
// "error" is lower case which may cause problems with reflect for method lookup.
|
||||
typ := errorMethodType(sc)
|
||||
fields = append(fields, structField{name: "Error", typ: typ})
|
||||
continue
|
||||
}
|
||||
typ, err := nodeType2(interp, sc, f0, seen)
|
||||
typ, err := nodeType2(interp, sc, c0, seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields = append(fields, structField{name: fieldName(f0), embed: true, typ: typ})
|
||||
incomplete = incomplete || typ.incomplete
|
||||
if typ.cat == constraintT {
|
||||
constraint = append(constraint, typ.constraint...)
|
||||
ulconstraint = append(ulconstraint, typ.ulconstraint...)
|
||||
continue
|
||||
}
|
||||
fields = append(fields, structField{name: fieldName(c0), embed: true, typ: typ})
|
||||
continue
|
||||
}
|
||||
typ, err := nodeType2(interp, sc, field.child[1], seen)
|
||||
typ, err := nodeType2(interp, sc, c.child[1], seen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields = append(fields, structField{name: f0.ident, typ: typ})
|
||||
fields = append(fields, structField{name: c0.ident, typ: typ})
|
||||
incomplete = incomplete || typ.incomplete
|
||||
}
|
||||
t = interfaceOf(t, fields, withNode(n), withScope(sc))
|
||||
t = interfaceOf(t, fields, constraint, ulconstraint, withNode(n), withScope(sc))
|
||||
t.incomplete = incomplete
|
||||
|
||||
case landExpr, lorExpr:
|
||||
@@ -872,9 +993,16 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
}
|
||||
|
||||
case structType:
|
||||
if sname := typeName(n); sname != "" {
|
||||
if sym, _, found := sc.lookup(sname); found && sym.kind == typeSym {
|
||||
var sym *symbol
|
||||
var found bool
|
||||
sname := structName(n)
|
||||
if sname != "" {
|
||||
sym, _, found = sc.lookup(sname)
|
||||
if found && sym.kind == typeSym {
|
||||
t = structOf(sym.typ, sym.typ.field, withNode(n), withScope(sc))
|
||||
} else {
|
||||
t = structOf(nil, nil, withNode(n), withScope(sc))
|
||||
sc.sym[sname] = &symbol{index: -1, kind: typeSym, typ: t, node: n}
|
||||
}
|
||||
}
|
||||
var incomplete bool
|
||||
@@ -915,6 +1043,9 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
|
||||
}
|
||||
t = structOf(t, fields, withNode(n), withScope(sc))
|
||||
t.incomplete = incomplete
|
||||
if sname != "" {
|
||||
sc.sym[sname].typ = t
|
||||
}
|
||||
|
||||
default:
|
||||
err = n.cfgErrorf("type definition not implemented: %s", n.kind)
|
||||
@@ -978,6 +1109,13 @@ func isBuiltinCall(n *node, sc *scope) bool {
|
||||
|
||||
// struct name returns the name of a struct type.
|
||||
func typeName(n *node) string {
|
||||
if n.anc.kind == typeSpec && len(n.anc.child) == 2 {
|
||||
return n.anc.child[0].ident
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func structName(n *node) string {
|
||||
if n.anc.kind == typeSpec {
|
||||
return n.anc.child[0].ident
|
||||
}
|
||||
@@ -1127,6 +1265,24 @@ func (t *itype) concrete() *itype {
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *itype) underlying() *itype {
|
||||
if t.cat == aliasT {
|
||||
return t.val.underlying()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// typeDefined returns true if type t1 is defined from type t2 or t2 from t1.
|
||||
func typeDefined(t1, t2 *itype) bool {
|
||||
if t1.cat == aliasT && t1.val == t2 {
|
||||
return true
|
||||
}
|
||||
if t2.cat == aliasT && t2.val == t1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isVariadic returns true if the function type is variadic.
|
||||
// If the type is not a function or is not variadic, it will
|
||||
// return false.
|
||||
@@ -1200,10 +1356,11 @@ func (t *itype) assignableTo(o *itype) bool {
|
||||
if t.equals(o) {
|
||||
return true
|
||||
}
|
||||
if t.cat == aliasT && o.cat == aliasT {
|
||||
// If alias types are not identical, it is not assignable.
|
||||
|
||||
if t.cat == aliasT && o.cat == aliasT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if t.isNil() && o.hasNil() || o.isNil() && t.hasNil() {
|
||||
return true
|
||||
}
|
||||
@@ -1216,11 +1373,20 @@ func (t *itype) assignableTo(o *itype) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if t.cat == sliceT && o.cat == sliceT {
|
||||
return t.val.assignableTo(o.val)
|
||||
}
|
||||
|
||||
if t.isBinMethod && isFunc(o) {
|
||||
// TODO (marc): check that t without receiver as first parameter is equivalent to o.
|
||||
return true
|
||||
}
|
||||
|
||||
if t.untyped && isNumber(t.TypeOf()) && isNumber(o.TypeOf()) {
|
||||
// Assignability depends on constant numeric value (overflow check), to be tested elsewhere.
|
||||
return true
|
||||
}
|
||||
|
||||
n := t.node
|
||||
if n == nil || !n.rval.IsValid() {
|
||||
return false
|
||||
@@ -1651,8 +1817,9 @@ func exportName(s string) string {
|
||||
|
||||
var (
|
||||
// TODO(mpl): generators.
|
||||
interf = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
emptyInterfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
valueInterfaceType = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
)
|
||||
|
||||
type fieldRebuild struct {
|
||||
@@ -1774,6 +1941,9 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
ctx.refs[name] = append(flds, fieldRebuild{})
|
||||
return unsafe2.DummyType
|
||||
}
|
||||
if isGeneric(t) {
|
||||
return reflect.TypeOf((*generic)(nil)).Elem()
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
t.rtype = t.val.refType(ctx)
|
||||
@@ -1802,7 +1972,12 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
}
|
||||
t.rtype = reflect.FuncOf(in, out, variadic)
|
||||
case interfaceT:
|
||||
t.rtype = interf
|
||||
if len(t.field) == 0 {
|
||||
// empty interface, do not wrap it
|
||||
t.rtype = emptyInterfaceType
|
||||
break
|
||||
}
|
||||
t.rtype = valueInterfaceType
|
||||
case mapT:
|
||||
t.rtype = reflect.MapOf(t.key.refType(ctx), t.val.refType(ctx))
|
||||
case ptrT:
|
||||
@@ -1827,6 +2002,7 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
}
|
||||
}
|
||||
}
|
||||
fieldFix := []int{} // Slice of field indices to fix for recursivity.
|
||||
t.rtype = reflect.StructOf(fields)
|
||||
if ctx.isComplete() {
|
||||
for _, s := range ctx.defined {
|
||||
@@ -1834,6 +2010,9 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
f := s.rtype.Field(i)
|
||||
if strings.HasSuffix(f.Type.String(), "unsafe2.dummy") {
|
||||
unsafe2.SetFieldType(s.rtype, i, ctx.rect.fixDummy(s.rtype.Field(i).Type))
|
||||
if name == s.path+"/"+s.name {
|
||||
fieldFix = append(fieldFix, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1842,13 +2021,16 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
// The rtype has now been built, we can go back and rebuild
|
||||
// all the recursive types that relied on this type.
|
||||
// However, as we are keyed by type name, if two or more (recursive) fields at
|
||||
// the same depth level are of the same type, they "mask" each other, and only one
|
||||
// of them is in ctx.refs, which means this pass below does not fully do the job.
|
||||
// Which is why we have the pass above that is done one last time, for all fields,
|
||||
// one the recursion has been fully resolved.
|
||||
// the same depth level are of the same type, or a "variation" of the same type
|
||||
// (slice of, map of, etc), they "mask" each other, and only one
|
||||
// of them is in ctx.refs. That is why the code around here is a bit convoluted,
|
||||
// and we need both the loop above, around all the struct fields, and the loop
|
||||
// below, around the ctx.refs.
|
||||
for _, f := range ctx.refs[name] {
|
||||
ftyp := f.typ.field[f.idx].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
|
||||
unsafe2.SetFieldType(f.typ.rtype, f.idx, ftyp)
|
||||
for _, index := range fieldFix {
|
||||
ftyp := f.typ.field[index].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
|
||||
unsafe2.SetFieldType(f.typ.rtype, index, ftyp)
|
||||
}
|
||||
}
|
||||
default:
|
||||
if z, _ := t.zero(); z.IsValid() {
|
||||
@@ -1880,10 +2062,10 @@ func (t *itype) frameType() (r reflect.Type) {
|
||||
case interfaceT:
|
||||
if len(t.field) == 0 {
|
||||
// empty interface, do not wrap it
|
||||
r = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
r = emptyInterfaceType
|
||||
break
|
||||
}
|
||||
r = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
r = valueInterfaceType
|
||||
case mapT:
|
||||
r = reflect.MapOf(t.key.frameType(), t.val.frameType())
|
||||
case ptrT:
|
||||
@@ -1896,6 +2078,14 @@ func (t *itype) frameType() (r reflect.Type) {
|
||||
|
||||
func (t *itype) implements(it *itype) bool {
|
||||
if isBin(t) {
|
||||
// Note: in case of a valueInterfaceType, we
|
||||
// miss required data which will be available
|
||||
// later, so we optimistically return true to progress,
|
||||
// and additional checks will be hopefully performed at
|
||||
// runtime.
|
||||
if rt := it.TypeOf(); rt == valueInterfaceType {
|
||||
return true
|
||||
}
|
||||
return t.TypeOf().Implements(it.TypeOf())
|
||||
}
|
||||
return t.methods().contains(it.methods())
|
||||
@@ -1951,11 +2141,15 @@ func (t *itype) defaultType(v reflect.Value, sc *scope) *itype {
|
||||
func (t *itype) isNil() bool { return t.cat == nilT }
|
||||
|
||||
func (t *itype) hasNil() bool {
|
||||
switch t.TypeOf().Kind() {
|
||||
switch rt := t.TypeOf(); rt.Kind() {
|
||||
case reflect.UnsafePointer:
|
||||
return true
|
||||
case reflect.Slice, reflect.Ptr, reflect.Func, reflect.Interface, reflect.Map, reflect.Chan:
|
||||
return true
|
||||
case reflect.Struct:
|
||||
if rt == valueInterfaceType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -2036,6 +2230,10 @@ func isEmptyInterface(t *itype) bool {
|
||||
return t.cat == interfaceT && len(t.field) == 0
|
||||
}
|
||||
|
||||
func isGeneric(t *itype) bool {
|
||||
return t.cat == funcT && t.node != nil && len(t.node.child) > 0 && len(t.node.child[0].child) > 0
|
||||
}
|
||||
|
||||
func isFuncSrc(t *itype) bool {
|
||||
return t.cat == funcT || (t.cat == aliasT && isFuncSrc(t.val))
|
||||
}
|
||||
@@ -2066,7 +2264,7 @@ func isInterfaceBin(t *itype) bool {
|
||||
}
|
||||
|
||||
func isInterface(t *itype) bool {
|
||||
return isInterfaceSrc(t) || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
|
||||
return isInterfaceSrc(t) || t.TypeOf() == valueInterfaceType || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
|
||||
}
|
||||
|
||||
func isBin(t *itype) bool {
|
||||
|
||||
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"errors"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
@@ -124,6 +125,8 @@ func (check typecheck) starExpr(n *node) error {
|
||||
}
|
||||
|
||||
var unaryOpPredicates = opPredicates{
|
||||
aInc: isNumber,
|
||||
aDec: isNumber,
|
||||
aPos: isNumber,
|
||||
aNeg: isNumber,
|
||||
aBitNot: isInt,
|
||||
@@ -133,6 +136,9 @@ var unaryOpPredicates = opPredicates{
|
||||
// unaryExpr type checks a unary expression.
|
||||
func (check typecheck) unaryExpr(n *node) error {
|
||||
c0 := n.child[0]
|
||||
if isBlank(c0) {
|
||||
return n.cfgErrorf("cannot use _ as value")
|
||||
}
|
||||
t0 := c0.typ.TypeOf()
|
||||
|
||||
if n.action == aRecv {
|
||||
@@ -178,25 +184,31 @@ func (check typecheck) shift(n *node) error {
|
||||
|
||||
// comparison type checks a comparison binary expression.
|
||||
func (check typecheck) comparison(n *node) error {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := n.child[0].typ, n.child[1].typ
|
||||
|
||||
if !c0.typ.assignableTo(c1.typ) && !c1.typ.assignableTo(c0.typ) {
|
||||
return n.cfgErrorf("invalid operation: mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
||||
if !t0.assignableTo(t1) && !t1.assignableTo(t0) {
|
||||
return n.cfgErrorf("invalid operation: mismatched types %s and %s", t0.id(), t1.id())
|
||||
}
|
||||
|
||||
ok := false
|
||||
|
||||
if !isInterface(t0) && !isInterface(t1) && !t0.isNil() && !t1.isNil() && t0.untyped == t1.untyped && t0.id() != t1.id() && !typeDefined(t0, t1) {
|
||||
// Non interface types must be really equals.
|
||||
return n.cfgErrorf("invalid operation: mismatched types %s and %s", t0.id(), t1.id())
|
||||
}
|
||||
|
||||
switch n.action {
|
||||
case aEqual, aNotEqual:
|
||||
ok = c0.typ.comparable() && c1.typ.comparable() || c0.typ.isNil() && c1.typ.hasNil() || c1.typ.isNil() && c0.typ.hasNil()
|
||||
ok = t0.comparable() && t1.comparable() || t0.isNil() && t1.hasNil() || t1.isNil() && t0.hasNil()
|
||||
case aLower, aLowerEqual, aGreater, aGreaterEqual:
|
||||
ok = c0.typ.ordered() && c1.typ.ordered()
|
||||
ok = t0.ordered() && t1.ordered()
|
||||
}
|
||||
if !ok {
|
||||
typ := c0.typ
|
||||
typ := t0
|
||||
if typ.isNil() {
|
||||
typ = c1.typ
|
||||
typ = t1
|
||||
}
|
||||
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id(), ".")
|
||||
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -221,6 +233,10 @@ var binaryOpPredicates = opPredicates{
|
||||
func (check typecheck) binaryExpr(n *node) error {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if isBlank(c0) || isBlank(c1) {
|
||||
return n.cfgErrorf("cannot use _ as value")
|
||||
}
|
||||
|
||||
a := n.action
|
||||
if isAssignAction(a) {
|
||||
a--
|
||||
@@ -476,6 +492,12 @@ func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error {
|
||||
|
||||
// sliceExpr type checks a slice expression.
|
||||
func (check typecheck) sliceExpr(n *node) error {
|
||||
for _, c := range n.child {
|
||||
if isBlank(c) {
|
||||
return n.cfgErrorf("cannot use _ as value")
|
||||
}
|
||||
}
|
||||
|
||||
c, child := n.child[0], n.child[1:]
|
||||
|
||||
t := c.typ.TypeOf()
|
||||
@@ -568,7 +590,7 @@ func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
|
||||
// https://github.com/golang/go/issues/39717 lands. It is currently impractical to
|
||||
// type check Named types as they cannot be asserted.
|
||||
|
||||
if n.typ.TypeOf().Kind() != reflect.Interface {
|
||||
if rt := n.typ.TypeOf(); rt.Kind() != reflect.Interface && rt != valueInterfaceType {
|
||||
return n.cfgErrorf("invalid type assertion: non-interface type %s on left", n.typ.id())
|
||||
}
|
||||
ims := n.typ.methods()
|
||||
@@ -591,6 +613,12 @@ func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
|
||||
continue
|
||||
}
|
||||
if tm == nil {
|
||||
// Lookup for non-exported methods is impossible
|
||||
// for bin types, ignore them as they can't be used
|
||||
// directly by the interpreted programs.
|
||||
if !token.IsExported(name) && isBin(typ) {
|
||||
continue
|
||||
}
|
||||
return n.cfgErrorf("impossible type assertion: %s does not implement %s (missing %v method)", typ.id(), n.typ.id(), name)
|
||||
}
|
||||
if tm.recv != nil && tm.recv.TypeOf().Kind() == reflect.Ptr && typ.TypeOf().Kind() != reflect.Ptr {
|
||||
@@ -793,7 +821,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
|
||||
case !typ0.untyped && typ1.untyped:
|
||||
err = check.convertUntyped(p1.nod, typ0)
|
||||
case typ0.untyped && typ1.untyped:
|
||||
fltType := check.scope.getType("float64")
|
||||
fltType := untypedFloat(nil)
|
||||
err = check.convertUntyped(p0.nod, fltType)
|
||||
if err != nil {
|
||||
break
|
||||
@@ -816,7 +844,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
|
||||
p := params[0]
|
||||
typ := p.Type()
|
||||
if typ.untyped {
|
||||
if err := check.convertUntyped(p.nod, check.scope.getType("complex128")); err != nil {
|
||||
if err := check.convertUntyped(p.nod, untypedComplex(nil)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
252
interp/use.go
Normal file
252
interp/use.go
Normal file
@@ -0,0 +1,252 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"log"
|
||||
"math/bits"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Symbols returns a map of interpreter exported symbol values for the given
|
||||
// import path. If the argument is the empty string, all known symbols are
|
||||
// returned.
|
||||
func (interp *Interpreter) Symbols(importPath string) Exports {
|
||||
m := map[string]map[string]reflect.Value{}
|
||||
interp.mutex.RLock()
|
||||
defer interp.mutex.RUnlock()
|
||||
|
||||
for k, v := range interp.srcPkg {
|
||||
if importPath != "" && k != importPath {
|
||||
continue
|
||||
}
|
||||
syms := map[string]reflect.Value{}
|
||||
for n, s := range v {
|
||||
if !canExport(n) {
|
||||
// Skip private non-exported symbols.
|
||||
continue
|
||||
}
|
||||
switch s.kind {
|
||||
case constSym:
|
||||
syms[n] = s.rval
|
||||
case funcSym:
|
||||
syms[n] = genFunctionWrapper(s.node)(interp.frame)
|
||||
case varSym:
|
||||
syms[n] = interp.frame.data[s.index]
|
||||
case typeSym:
|
||||
syms[n] = reflect.New(s.typ.TypeOf())
|
||||
}
|
||||
}
|
||||
|
||||
if len(syms) > 0 {
|
||||
m[k] = syms
|
||||
}
|
||||
|
||||
if importPath != "" {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
if importPath != "" && len(m) > 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
for k, v := range interp.binPkg {
|
||||
if importPath != "" && k != importPath {
|
||||
continue
|
||||
}
|
||||
m[k] = v
|
||||
if importPath != "" {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// getWrapper returns the wrapper type of the corresponding interface, trying
|
||||
// first the composed ones, or nil if not found.
|
||||
func getWrapper(n *node, t reflect.Type) reflect.Type {
|
||||
p, ok := n.interp.binPkg[t.PkgPath()]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
w := p["_"+t.Name()]
|
||||
lm := n.typ.methods()
|
||||
|
||||
// mapTypes may contain composed interfaces wrappers to test against, from
|
||||
// most complex to simplest (guaranteed by construction of mapTypes). Find the
|
||||
// first for which the interpreter type has all the methods.
|
||||
for _, rt := range n.interp.mapTypes[w] {
|
||||
match := true
|
||||
for i := 1; i < rt.NumField(); i++ {
|
||||
// The interpreter type must have all required wrapper methods.
|
||||
if _, ok := lm[rt.Field(i).Name[1:]]; !ok {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match {
|
||||
return rt
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise return the direct "non-composed" interface.
|
||||
return w.Type().Elem()
|
||||
}
|
||||
|
||||
// Use loads binary runtime symbols in the interpreter context so
|
||||
// they can be used in interpreted code.
|
||||
func (interp *Interpreter) Use(values Exports) error {
|
||||
for k, v := range values {
|
||||
importPath := path.Dir(k)
|
||||
packageName := path.Base(k)
|
||||
|
||||
if k == "." && v["MapTypes"].IsValid() {
|
||||
// Use mapping for special interface wrappers.
|
||||
for kk, vv := range v["MapTypes"].Interface().(map[reflect.Value][]reflect.Type) {
|
||||
interp.mapTypes[kk] = vv
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if importPath == "." {
|
||||
return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k)
|
||||
}
|
||||
|
||||
if importPath == selfPrefix {
|
||||
interp.hooks.Parse(v)
|
||||
continue
|
||||
}
|
||||
|
||||
if interp.binPkg[importPath] == nil {
|
||||
interp.binPkg[importPath] = make(map[string]reflect.Value)
|
||||
interp.pkgNames[importPath] = packageName
|
||||
}
|
||||
|
||||
for s, sym := range v {
|
||||
interp.binPkg[importPath][s] = sym
|
||||
}
|
||||
if k == selfPath {
|
||||
interp.binPkg[importPath]["Self"] = reflect.ValueOf(interp)
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if input values correspond to stdlib packages by looking for one
|
||||
// well known stdlib package path.
|
||||
if _, ok := values["fmt/fmt"]; ok {
|
||||
fixStdlib(interp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixStdlib redefines interpreter stdlib symbols to use the standard input,
|
||||
// output and errror assigned to the interpreter. The changes are limited to
|
||||
// the interpreter only.
|
||||
// Note that it is possible to escape the virtualized stdio by
|
||||
// read/write directly to file descriptors 0, 1, 2.
|
||||
func fixStdlib(interp *Interpreter) {
|
||||
p := interp.binPkg["fmt"]
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
stdin, stdout, stderr := interp.stdin, interp.stdout, interp.stderr
|
||||
|
||||
p["Print"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprint(stdout, a...) })
|
||||
p["Printf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fprintf(stdout, f, a...) })
|
||||
p["Println"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprintln(stdout, a...) })
|
||||
|
||||
p["Scan"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscan(stdin, a...) })
|
||||
p["Scanf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fscanf(stdin, f, a...) })
|
||||
p["Scanln"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscanln(stdin, a...) })
|
||||
|
||||
// Update mapTypes to virtualized symbols as well.
|
||||
interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(fmt.Print)]
|
||||
interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(fmt.Printf)]
|
||||
interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(fmt.Println)]
|
||||
interp.mapTypes[p["Scan"]] = interp.mapTypes[reflect.ValueOf(fmt.Scan)]
|
||||
interp.mapTypes[p["Scanf"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanf)]
|
||||
interp.mapTypes[p["Scanln"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanln)]
|
||||
|
||||
if p = interp.binPkg["flag"]; p != nil {
|
||||
c := flag.NewFlagSet(os.Args[0], flag.PanicOnError)
|
||||
c.SetOutput(stderr)
|
||||
p["CommandLine"] = reflect.ValueOf(&c).Elem()
|
||||
}
|
||||
|
||||
if p = interp.binPkg["log"]; p != nil {
|
||||
l := log.New(stderr, "", log.LstdFlags)
|
||||
// Restrict Fatal symbols to panic instead of exit.
|
||||
p["Fatal"] = reflect.ValueOf(l.Panic)
|
||||
p["Fatalf"] = reflect.ValueOf(l.Panicf)
|
||||
p["Fatalln"] = reflect.ValueOf(l.Panicln)
|
||||
|
||||
p["Flags"] = reflect.ValueOf(l.Flags)
|
||||
p["Output"] = reflect.ValueOf(l.Output)
|
||||
p["Panic"] = reflect.ValueOf(l.Panic)
|
||||
p["Panicf"] = reflect.ValueOf(l.Panicf)
|
||||
p["Panicln"] = reflect.ValueOf(l.Panicln)
|
||||
p["Prefix"] = reflect.ValueOf(l.Prefix)
|
||||
p["Print"] = reflect.ValueOf(l.Print)
|
||||
p["Printf"] = reflect.ValueOf(l.Printf)
|
||||
p["Println"] = reflect.ValueOf(l.Println)
|
||||
p["SetFlags"] = reflect.ValueOf(l.SetFlags)
|
||||
p["SetOutput"] = reflect.ValueOf(l.SetOutput)
|
||||
p["SetPrefix"] = reflect.ValueOf(l.SetPrefix)
|
||||
p["Writer"] = reflect.ValueOf(l.Writer)
|
||||
|
||||
// Update mapTypes to virtualized symbols as well.
|
||||
interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(log.Print)]
|
||||
interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(log.Printf)]
|
||||
interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(log.Println)]
|
||||
interp.mapTypes[p["Panic"]] = interp.mapTypes[reflect.ValueOf(log.Panic)]
|
||||
interp.mapTypes[p["Panicf"]] = interp.mapTypes[reflect.ValueOf(log.Panicf)]
|
||||
interp.mapTypes[p["Panicln"]] = interp.mapTypes[reflect.ValueOf(log.Panicln)]
|
||||
}
|
||||
|
||||
if p = interp.binPkg["os"]; p != nil {
|
||||
p["Args"] = reflect.ValueOf(&interp.args).Elem()
|
||||
if interp.specialStdio {
|
||||
// Inherit streams from interpreter even if they do not have a file descriptor.
|
||||
p["Stdin"] = reflect.ValueOf(&stdin).Elem()
|
||||
p["Stdout"] = reflect.ValueOf(&stdout).Elem()
|
||||
p["Stderr"] = reflect.ValueOf(&stderr).Elem()
|
||||
} else {
|
||||
// Inherits streams from interpreter only if they have a file descriptor and preserve original type.
|
||||
if s, ok := stdin.(*os.File); ok {
|
||||
p["Stdin"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
if s, ok := stdout.(*os.File); ok {
|
||||
p["Stdout"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
if s, ok := stderr.(*os.File); ok {
|
||||
p["Stderr"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
}
|
||||
if !interp.unrestricted {
|
||||
// In restricted mode, scripts can only access to a passed virtualized env, and can not write the real one.
|
||||
getenv := func(key string) string { return interp.env[key] }
|
||||
p["Clearenv"] = reflect.ValueOf(func() { interp.env = map[string]string{} })
|
||||
p["ExpandEnv"] = reflect.ValueOf(func(s string) string { return os.Expand(s, getenv) })
|
||||
p["Getenv"] = reflect.ValueOf(getenv)
|
||||
p["LookupEnv"] = reflect.ValueOf(func(key string) (s string, ok bool) { s, ok = interp.env[key]; return })
|
||||
p["Setenv"] = reflect.ValueOf(func(key, value string) error { interp.env[key] = value; return nil })
|
||||
p["Unsetenv"] = reflect.ValueOf(func(key string) error { delete(interp.env, key); return nil })
|
||||
p["Environ"] = reflect.ValueOf(func() (a []string) {
|
||||
for k, v := range interp.env {
|
||||
a = append(a, k+"="+v)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if p = interp.binPkg["math/bits"]; p != nil {
|
||||
// Do not trust extracted value maybe from another arch.
|
||||
p["UintSize"] = reflect.ValueOf(constant.MakeInt64(bits.UintSize))
|
||||
}
|
||||
}
|
||||
@@ -34,10 +34,10 @@ func valueGenerator(n *node, i int) func(*frame) reflect.Value {
|
||||
// because a cancellation prior to any evaluation result may leave
|
||||
// the frame's data empty.
|
||||
func valueOf(data []reflect.Value, i int) reflect.Value {
|
||||
if i < len(data) {
|
||||
return data[i]
|
||||
if i < 0 || i >= len(data) {
|
||||
return reflect.Value{}
|
||||
}
|
||||
return reflect.Value{}
|
||||
return data[i]
|
||||
}
|
||||
|
||||
func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value) func(*frame) reflect.Value {
|
||||
@@ -176,7 +176,7 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
convertConstantValue(n)
|
||||
v := n.rval
|
||||
if !v.IsValid() {
|
||||
v = reflect.New(interf).Elem()
|
||||
v = reflect.New(emptyInterfaceType).Elem()
|
||||
}
|
||||
return func(f *frame) reflect.Value { return v }
|
||||
case funcDecl:
|
||||
@@ -195,7 +195,7 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
if n.sym != nil {
|
||||
i := n.sym.index
|
||||
if i < 0 {
|
||||
if i < 0 && n != n.sym.node {
|
||||
return genValue(n.sym.node)
|
||||
}
|
||||
if n.sym.global {
|
||||
@@ -219,7 +219,7 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
|
||||
convertLiteralValue(n, typ.TypeOf())
|
||||
switch {
|
||||
case isInterfaceSrc(typ) && !isEmptyInterface(typ):
|
||||
case isInterfaceSrc(typ) && (!isEmptyInterface(typ) || len(n.typ.method) > 0):
|
||||
return genValueInterface(n)
|
||||
case isFuncSrc(typ) && (n.typ.cat == valueT || n.typ.cat == nilT):
|
||||
return genValueNode(n)
|
||||
@@ -287,19 +287,6 @@ func genValueRangeArray(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterfaceArray(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
return func(f *frame) reflect.Value {
|
||||
vi := value(f).Interface().([]valueInterface)
|
||||
v := reflect.MakeSlice(reflect.TypeOf([]interface{}{}), len(vi), len(vi))
|
||||
for i, vv := range vi {
|
||||
v.Index(i).Set(vv.value)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
@@ -356,7 +343,7 @@ func getConcreteValue(val reflect.Value) reflect.Value {
|
||||
|
||||
func zeroInterfaceValue() reflect.Value {
|
||||
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true, str: "nil"}}
|
||||
v := reflect.New(interf).Elem()
|
||||
v := reflect.New(emptyInterfaceType).Elem()
|
||||
return reflect.ValueOf(valueInterface{n, v})
|
||||
}
|
||||
|
||||
@@ -392,6 +379,21 @@ func genValueOutput(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
return value
|
||||
}
|
||||
|
||||
func getBinValue(getMapType func(*itype) reflect.Type, value func(*frame) reflect.Value, f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if getMapType == nil {
|
||||
return v
|
||||
}
|
||||
val, ok := v.Interface().(valueInterface)
|
||||
if !ok || val.node == nil {
|
||||
return v
|
||||
}
|
||||
if rt := getMapType(val.node.typ); rt != nil {
|
||||
return genInterfaceWrapper(val.node, rt)(f)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func valueInterfaceValue(v reflect.Value) reflect.Value {
|
||||
for {
|
||||
vv, ok := v.Interface().(valueInterface)
|
||||
@@ -408,7 +410,7 @@ func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.Interface().(valueInterface).node == nil {
|
||||
if vi, ok := v.Interface().(valueInterface); ok && vi.node == nil {
|
||||
// Uninitialized interface value, set it to a correct zero value.
|
||||
v.Set(zeroInterfaceValue())
|
||||
v = value(f)
|
||||
@@ -421,7 +423,11 @@ func genValueNode(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
return reflect.ValueOf(&node{rval: value(f)})
|
||||
v := value(f)
|
||||
if _, ok := v.Interface().(*node); ok {
|
||||
return v
|
||||
}
|
||||
return reflect.ValueOf(&node{rval: v})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,5 +597,6 @@ func genComplex(n *node) func(*frame) complex128 {
|
||||
|
||||
func genValueString(n *node) func(*frame) (reflect.Value, string) {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) (reflect.Value, string) { v := value(f); return v, v.String() }
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// Code generated by 'yaegi extract go/parser'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"go/parser"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["go/parser/parser"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"AllErrors": reflect.ValueOf(parser.AllErrors),
|
||||
"DeclarationErrors": reflect.ValueOf(parser.DeclarationErrors),
|
||||
"ImportsOnly": reflect.ValueOf(parser.ImportsOnly),
|
||||
"PackageClauseOnly": reflect.ValueOf(parser.PackageClauseOnly),
|
||||
"ParseComments": reflect.ValueOf(parser.ParseComments),
|
||||
"ParseDir": reflect.ValueOf(parser.ParseDir),
|
||||
"ParseExpr": reflect.ValueOf(parser.ParseExpr),
|
||||
"ParseExprFrom": reflect.ValueOf(parser.ParseExprFrom),
|
||||
"ParseFile": reflect.ValueOf(parser.ParseFile),
|
||||
"SpuriousErrors": reflect.ValueOf(parser.SpuriousErrors),
|
||||
"Trace": reflect.ValueOf(parser.Trace),
|
||||
|
||||
// type definitions
|
||||
"Mode": reflect.ValueOf((*parser.Mode)(nil)),
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by 'yaegi extract archive/tar'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by 'yaegi extract archive/zip'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Code generated by 'yaegi extract bufio'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Code generated by 'yaegi extract bytes'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -19,6 +20,7 @@ func init() {
|
||||
"ContainsAny": reflect.ValueOf(bytes.ContainsAny),
|
||||
"ContainsRune": reflect.ValueOf(bytes.ContainsRune),
|
||||
"Count": reflect.ValueOf(bytes.Count),
|
||||
"Cut": reflect.ValueOf(bytes.Cut),
|
||||
"Equal": reflect.ValueOf(bytes.Equal),
|
||||
"EqualFold": reflect.ValueOf(bytes.EqualFold),
|
||||
"ErrTooLarge": reflect.ValueOf(&bytes.ErrTooLarge).Elem(),
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by 'yaegi extract compress/bzip2'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Code generated by 'yaegi extract compress/flate'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by 'yaegi extract compress/gzip'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Code generated by 'yaegi extract compress/lzw'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -18,6 +19,8 @@ func init() {
|
||||
"NewWriter": reflect.ValueOf(lzw.NewWriter),
|
||||
|
||||
// type definitions
|
||||
"Order": reflect.ValueOf((*lzw.Order)(nil)),
|
||||
"Order": reflect.ValueOf((*lzw.Order)(nil)),
|
||||
"Reader": reflect.ValueOf((*lzw.Reader)(nil)),
|
||||
"Writer": reflect.ValueOf((*lzw.Writer)(nil)),
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by 'yaegi extract compress/zlib'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Code generated by 'yaegi extract container/heap'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -31,8 +32,8 @@ type _container_heap_Interface struct {
|
||||
IValue interface{}
|
||||
WLen func() int
|
||||
WLess func(i int, j int) bool
|
||||
WPop func() interface{}
|
||||
WPush func(x interface{})
|
||||
WPop func() any
|
||||
WPush func(x any)
|
||||
WSwap func(i int, j int)
|
||||
}
|
||||
|
||||
@@ -42,10 +43,10 @@ func (W _container_heap_Interface) Len() int {
|
||||
func (W _container_heap_Interface) Less(i int, j int) bool {
|
||||
return W.WLess(i, j)
|
||||
}
|
||||
func (W _container_heap_Interface) Pop() interface{} {
|
||||
func (W _container_heap_Interface) Pop() any {
|
||||
return W.WPop()
|
||||
}
|
||||
func (W _container_heap_Interface) Push(x interface{}) {
|
||||
func (W _container_heap_Interface) Push(x any) {
|
||||
W.WPush(x)
|
||||
}
|
||||
func (W _container_heap_Interface) Swap(i int, j int) {
|
||||
@@ -1,6 +1,7 @@
|
||||
// Code generated by 'yaegi extract container/list'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Code generated by 'yaegi extract container/ring'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Code generated by 'yaegi extract context'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -37,7 +38,7 @@ type _context_Context struct {
|
||||
WDeadline func() (deadline time.Time, ok bool)
|
||||
WDone func() <-chan struct{}
|
||||
WErr func() error
|
||||
WValue func(key interface{}) interface{}
|
||||
WValue func(key any) any
|
||||
}
|
||||
|
||||
func (W _context_Context) Deadline() (deadline time.Time, ok bool) {
|
||||
@@ -49,6 +50,6 @@ func (W _context_Context) Done() <-chan struct{} {
|
||||
func (W _context_Context) Err() error {
|
||||
return W.WErr()
|
||||
}
|
||||
func (W _context_Context) Value(key interface{}) interface{} {
|
||||
func (W _context_Context) Value(key any) any {
|
||||
return W.WValue(key)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by 'yaegi extract crypto'. DO NOT EDIT.
|
||||
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Code generated by 'yaegi extract crypto/aes'. DO NOT EDIT.
|
||||
|
||||
// +build go1.16,!go1.17
|
||||
//go:build go1.18 && !go1.19
|
||||
// +build go1.18,!go1.19
|
||||
|
||||
package stdlib
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user