Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbee2baf9d | ||
|
|
2819b4167b | ||
|
|
2af660cb1f | ||
|
|
8323068414 | ||
|
|
5b62f9fdb6 | ||
|
|
4f66e3fe6c | ||
|
|
1335b4c64f | ||
|
|
da03c922ca | ||
|
|
9620116c30 | ||
|
|
dd7197f2a2 | ||
|
|
9214806342 | ||
|
|
348e713a8e | ||
|
|
cb81fe41ab | ||
|
|
a876bb3673 | ||
|
|
afa46daccd | ||
|
|
3c00da291e | ||
|
|
c847481184 | ||
|
|
f46ef67180 | ||
|
|
229ddfdae1 | ||
|
|
aa7f0849e3 | ||
|
|
7617b8a090 | ||
|
|
4e06abe002 | ||
|
|
b1a758dd5a | ||
|
|
5bf4daef2d | ||
|
|
e56db3b82e | ||
|
|
d3bbe01d5c | ||
|
|
286d6c6359 | ||
|
|
84424b52bc | ||
|
|
98c2dcd3e5 | ||
|
|
808f0bde9d | ||
|
|
c5c6012947 | ||
|
|
836060c8ad | ||
|
|
7a54353c7b | ||
|
|
b591ba0e78 | ||
|
|
5af51aefe6 | ||
|
|
e7c0f68bab | ||
|
|
bd9a6a4f8a | ||
|
|
3eb2c79fd8 | ||
|
|
4653d87298 | ||
|
|
45d569c215 | ||
|
|
c33caeb573 | ||
|
|
91a55cc4c5 | ||
|
|
05f08d776a | ||
|
|
d2569a85a6 | ||
|
|
772cd68fea | ||
|
|
4af992bccb | ||
|
|
da922ce90b | ||
|
|
7b77b0fa22 | ||
|
|
b7f9a39eff | ||
|
|
d2b25a7426 | ||
|
|
b5bf4ef31a | ||
|
|
a69b9bc2dc | ||
|
|
b84278dcc6 |
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.17
|
||||
GOLANGCI_LINT_VERSION: v1.41.1
|
||||
GOLANGCI_LINT_VERSION: v1.42.1
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
@@ -63,10 +63,10 @@
|
||||
exclude = []
|
||||
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/.+_test\\.go"
|
||||
path = ".+_test\\.go"
|
||||
linters = ["goconst"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/.+_test\\.go"
|
||||
path = ".+_test\\.go"
|
||||
text = "var-declaration:"
|
||||
|
||||
[[issues.exclude-rules]]
|
||||
@@ -75,6 +75,9 @@
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp.go"
|
||||
text = "`out` can be `io.Writer`"
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp.go"
|
||||
text = "`Panic` should conform to the `XxxError` format"
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp_eval_test.go"
|
||||
linters = ["thelper"]
|
||||
|
||||
10
_test/a29.go
10
_test/a29.go
@@ -6,16 +6,18 @@ const (
|
||||
zero = iota
|
||||
one
|
||||
two
|
||||
three
|
||||
)
|
||||
|
||||
func main() {
|
||||
a := [...]string{
|
||||
zero: "zero",
|
||||
one: "one",
|
||||
two: "two",
|
||||
zero: "zero",
|
||||
one: "one",
|
||||
three: "three",
|
||||
three + 2: "five",
|
||||
}
|
||||
fmt.Printf("%v %T\n", a, a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [zero one two] [3]string
|
||||
// [zero one three five] [6]string
|
||||
|
||||
18
_test/alias2.go
Normal file
18
_test/alias2.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (t MyT) Test() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
type MyT int
|
||||
|
||||
func main() {
|
||||
t := MyT(1)
|
||||
|
||||
fmt.Println(t.Test())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
22
_test/alias3.go
Normal file
22
_test/alias3.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "github.com/traefik/yaegi/_test/alias3"
|
||||
|
||||
var globalT *T
|
||||
|
||||
func init() {
|
||||
globalT = &T{A: "test"}
|
||||
}
|
||||
|
||||
type T alias3.T
|
||||
|
||||
func (t *T) PrintT() {
|
||||
(*alias3.T)(t).Print()
|
||||
}
|
||||
|
||||
func main() {
|
||||
globalT.PrintT()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
9
_test/alias3/alias3.go
Normal file
9
_test/alias3/alias3.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package alias3
|
||||
|
||||
type T struct {
|
||||
A string
|
||||
}
|
||||
|
||||
func (t *T) Print() {
|
||||
println(t.A)
|
||||
}
|
||||
27
_test/alias4.go
Normal file
27
_test/alias4.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type A http.Header
|
||||
|
||||
func (a A) Test1() {
|
||||
fmt.Println("test1")
|
||||
}
|
||||
|
||||
type B A
|
||||
|
||||
func (b B) Test2() {
|
||||
fmt.Println("test2")
|
||||
}
|
||||
|
||||
func main() {
|
||||
b := B{}
|
||||
|
||||
b.Test2()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test2
|
||||
37
_test/assert2.go
Normal file
37
_test/assert2.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Defined an interface of stringBuilder that 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)
|
||||
Reset()
|
||||
Grow(n int)
|
||||
String() string
|
||||
}
|
||||
|
||||
var builderPool = sync.Pool{New: func() interface{} {
|
||||
return newStringBuilder()
|
||||
}}
|
||||
|
||||
func newStringBuilder() stringBuilder {
|
||||
return &strings.Builder{}
|
||||
}
|
||||
|
||||
func main() {
|
||||
i := builderPool.Get()
|
||||
sb := i.(stringBuilder)
|
||||
_, _ = sb.WriteString("hello")
|
||||
|
||||
println(sb.String())
|
||||
|
||||
builderPool.Put(i)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
@@ -8,4 +8,4 @@ func main() {
|
||||
}
|
||||
|
||||
// Error:
|
||||
// _test/assign15.go:5:26: cannot use type chan<- struct{} as type <-chan struct{} in assignment
|
||||
// _test/assign15.go:5:26: cannot use type chan<- struct {} as type <-chan struct {} in assignment
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// A test program
|
||||
|
||||
// +build darwin,linux !arm
|
||||
// +build go1.12 !go1.13
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("hello world")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -13,7 +13,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -21,7 +21,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -13,7 +13,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -40,7 +40,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -40,7 +40,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -41,7 +41,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
32
_test/composite18.go
Normal file
32
_test/composite18.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type fn func(string, string) bool
|
||||
|
||||
var funcs = []fn{
|
||||
cmpLessFn,
|
||||
cmpGreaterFn,
|
||||
nil,
|
||||
}
|
||||
|
||||
func cmpLessFn(a string, b string) bool {
|
||||
return a < b
|
||||
}
|
||||
|
||||
func cmpGreaterFn(a string, b string) bool {
|
||||
return a > b
|
||||
}
|
||||
|
||||
func main() {
|
||||
for _, f := range funcs {
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
fmt.Println(f("a", "b"))
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
33
_test/composite19.go
Normal file
33
_test/composite19.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type fn func(string, string) bool
|
||||
|
||||
var funcs = map[string]fn{
|
||||
"less": cmpLessFn,
|
||||
"greater": cmpGreaterFn,
|
||||
"none": nil,
|
||||
}
|
||||
|
||||
func cmpLessFn(a string, b string) bool {
|
||||
return a < b
|
||||
}
|
||||
|
||||
func cmpGreaterFn(a string, b string) bool {
|
||||
return a > b
|
||||
}
|
||||
|
||||
func main() {
|
||||
for _, n := range []string{"less", "greater", "none"} {
|
||||
f := funcs[n]
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
fmt.Println(f("a", "b"))
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
@@ -9,7 +9,7 @@ func main() {
|
||||
const huge = 1 << 100
|
||||
const large = huge >> 38
|
||||
|
||||
fmt.Println(large)
|
||||
fmt.Println(int64(large))
|
||||
}
|
||||
|
||||
// Output:
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const maxLen = int64(int(^uint(0) >> 1))
|
||||
const maxLen = int64(int64(^uint64(0) >> 1))
|
||||
|
||||
func main() {
|
||||
fmt.Println(maxLen)
|
||||
|
||||
28
_test/const26.go
Normal file
28
_test/const26.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fmt.Println(constString)
|
||||
fmt.Println(const2)
|
||||
fmt.Println(varString)
|
||||
}
|
||||
|
||||
const constString string = "hello"
|
||||
|
||||
const (
|
||||
const1 = iota + 10
|
||||
const2
|
||||
const3
|
||||
)
|
||||
|
||||
var varString string = "test"
|
||||
|
||||
func main() {}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
// 11
|
||||
// test
|
||||
@@ -2,12 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
file, err := ioutil.TempFile("", "yeagibench")
|
||||
file, err := os.CreateTemp("", "yeagibench")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -23,7 +22,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(file.Name())
|
||||
b, err := os.ReadFile(file.Name())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func main() {
|
||||
r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.")
|
||||
|
||||
b, err := ioutil.ReadAll(r)
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -10,4 +10,4 @@ func main() {
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 9:6: cannot use type string as type int in assignment
|
||||
// 9:6: cannot use type untyped string as type int in assignment
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -13,7 +12,7 @@ type sink interface {
|
||||
|
||||
func newSink() sink {
|
||||
// return os.Stdout // Stdout is special in yaegi tests
|
||||
file, err := ioutil.TempFile("", "yaegi-test.*")
|
||||
file, err := os.CreateTemp("", "yaegi-test.*")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
67
_test/issue-1260.go
Normal file
67
_test/issue-1260.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type WriteSyncer interface {
|
||||
io.Writer
|
||||
Sync() error
|
||||
}
|
||||
|
||||
type Sink interface {
|
||||
WriteSyncer
|
||||
io.Closer
|
||||
}
|
||||
|
||||
func newFileSink(path string) (Sink, error) {
|
||||
return os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
||||
}
|
||||
|
||||
type Sink1 struct{ name string }
|
||||
|
||||
func (s Sink1) Write(b []byte) (int, error) { println("in Write"); return 0, nil }
|
||||
func (s Sink1) Sync() error { println("in Sync"); return nil }
|
||||
func (s Sink1) Close() error { println("in Close", s.name); return nil }
|
||||
func newS1(name string) Sink { return Sink1{name} }
|
||||
func newS1p(name string) Sink { return &Sink1{name} }
|
||||
|
||||
type Sink2 struct{ name string }
|
||||
|
||||
func (s *Sink2) Write(b []byte) (int, error) { println("in Write"); return 0, nil }
|
||||
func (s *Sink2) Sync() error { println("in Sync"); return nil }
|
||||
func (s *Sink2) Close() error { println("in Close", s.name); return nil }
|
||||
func newS2(name string) Sink { return Sink1{name} }
|
||||
|
||||
func main() {
|
||||
tmpfile, err := os.CreateTemp("", "xxx")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.Remove(tmpfile.Name())
|
||||
closers := []io.Closer{}
|
||||
sink, err := newFileSink(tmpfile.Name())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
closers = append(closers, sink)
|
||||
|
||||
s1p := newS1p("ptr")
|
||||
s1 := newS1("struct")
|
||||
s2 := newS2("ptr2")
|
||||
closers = append(closers, s1p, s1, s2)
|
||||
for _, closer := range closers {
|
||||
fmt.Println(closer.Close())
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
// in Close ptr
|
||||
// <nil>
|
||||
// in Close struct
|
||||
// <nil>
|
||||
// in Close ptr2
|
||||
// <nil>
|
||||
24
_test/issue-1276.go
Normal file
24
_test/issue-1276.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
type customFlag struct{}
|
||||
|
||||
func (cf customFlag) String() string {
|
||||
return "custom flag"
|
||||
}
|
||||
|
||||
func (cf customFlag) Set(string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Var(customFlag{}, "cf", "custom flag")
|
||||
flag.Parse()
|
||||
println("Hello, playground")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello, playground
|
||||
30
_test/issue-1280.go
Normal file
30
_test/issue-1280.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type DBReader interface {
|
||||
io.ReadCloser
|
||||
io.ReaderAt
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
f DBReader
|
||||
}
|
||||
|
||||
func main() {
|
||||
f, err := os.Open("/dev/null")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
d := &DB{f}
|
||||
data := make([]byte, 1)
|
||||
_, _ = d.f.ReadAt(data, 0)
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
25
_test/issue-1285.go
Normal file
25
_test/issue-1285.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
type (
|
||||
T1 struct{ Path [12]int8 }
|
||||
T2 struct{ Path *[12]int8 }
|
||||
)
|
||||
|
||||
var (
|
||||
t11 = &T1{}
|
||||
t21 = &T2{}
|
||||
)
|
||||
|
||||
func main() {
|
||||
b := [12]byte{}
|
||||
t12 := &T1{}
|
||||
t22 := &T2{}
|
||||
b11 := (*[len(t11.Path)]byte)(&b)
|
||||
b12 := (*[len(t12.Path)]byte)(&b)
|
||||
b21 := (*[len(t21.Path)]byte)(&b)
|
||||
b22 := (*[len(t22.Path)]byte)(&b)
|
||||
println(len(b11), len(b12), len(b21), len(b22))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 12 12 12 12
|
||||
10
_test/issue-1288.go
Normal file
10
_test/issue-1288.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("Hi")
|
||||
goto done
|
||||
done:
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hi
|
||||
20
_test/issue-1300.go
Normal file
20
_test/issue-1300.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
const buflen = 512
|
||||
|
||||
type T struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func f(t *T) { *t = T{buf: make([]byte, 0, buflen)} }
|
||||
|
||||
func main() {
|
||||
s := T{}
|
||||
println(cap(s.buf))
|
||||
f(&s)
|
||||
println(cap(s.buf))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 512
|
||||
16
_test/issue-1304.go
Normal file
16
_test/issue-1304.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Alias *Node
|
||||
Child []*Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
n := &Node{Name: "parent"}
|
||||
n.Child = append(n.Child, &Node{Name: "child"})
|
||||
println(n.Name, n.Child[0].Name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// parent child
|
||||
15
_test/issue-1306.go
Normal file
15
_test/issue-1306.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func check() (result bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
result, error := check()
|
||||
fmt.Println(result, error)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true <nil>
|
||||
27
_test/issue-1308.go
Normal file
27
_test/issue-1308.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type test struct {
|
||||
v interface{}
|
||||
s string
|
||||
}
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := []test{
|
||||
{
|
||||
v: []interface{}{
|
||||
T{"hello"},
|
||||
},
|
||||
s: "world",
|
||||
},
|
||||
}
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [{[{hello}] world}]
|
||||
19
_test/issue-1311.go
Normal file
19
_test/issue-1311.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
v interface{}
|
||||
}
|
||||
|
||||
func f() (ret int64, err error) {
|
||||
ret += 2
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.v, _ = f()
|
||||
println(t.v.(int64))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
32
_test/issue-1315.go
Normal file
32
_test/issue-1315.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
type Intf interface {
|
||||
M()
|
||||
}
|
||||
|
||||
type T struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (t *T) M() { println("in M") }
|
||||
|
||||
func f(i interface{}) {
|
||||
switch j := i.(type) {
|
||||
case Intf:
|
||||
j.M()
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var i Intf
|
||||
var k interface{} = 1
|
||||
i = &T{"hello"}
|
||||
f(i)
|
||||
f(k)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in M
|
||||
// default
|
||||
29
_test/issue-1320.go
Normal file
29
_test/issue-1320.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
type Pooler interface {
|
||||
Get() string
|
||||
}
|
||||
|
||||
type baseClient struct {
|
||||
connPool Pooler
|
||||
}
|
||||
|
||||
type connPool struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (c *connPool) Get() string { return c.name }
|
||||
|
||||
func newBaseClient(i int, p Pooler) *baseClient {
|
||||
return &baseClient{connPool: p}
|
||||
}
|
||||
|
||||
func newConnPool() *connPool { return &connPool{name: "connPool"} }
|
||||
|
||||
func main() {
|
||||
b := newBaseClient(0, newConnPool())
|
||||
println(b.connPool.(*connPool).name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// connPool
|
||||
42
_test/issue-1326.go
Normal file
42
_test/issue-1326.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
type Option interface {
|
||||
apply(*T)
|
||||
}
|
||||
|
||||
type T struct {
|
||||
s string
|
||||
}
|
||||
|
||||
type opt struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (o *opt) apply(t *T) {
|
||||
println(o.name)
|
||||
}
|
||||
|
||||
func BuildOptions() []Option {
|
||||
return []Option{
|
||||
&opt{"opt1"},
|
||||
&opt{"opt2"},
|
||||
}
|
||||
}
|
||||
|
||||
func NewT(name string, options ...Option) *T {
|
||||
t := &T{name}
|
||||
for _, opt := range options {
|
||||
opt.apply(t)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := NewT("hello", BuildOptions()...)
|
||||
println(t.s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// opt1
|
||||
// opt2
|
||||
// hello
|
||||
16
_test/issue-1328.go
Normal file
16
_test/issue-1328.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
func main() {
|
||||
script := "hello"
|
||||
sumRaw := sha1.Sum([]byte(script))
|
||||
sum := hex.EncodeToString(sumRaw[:])
|
||||
println(sum)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
|
||||
42
_test/issue-1330.go
Normal file
42
_test/issue-1330.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type wrappedConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func main() {
|
||||
_, err := net.Listen("tcp", "127.0.0.1:49153")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
LocalAddr: &net.TCPAddr{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := dialer.Dial("tcp", "127.0.0.1:49153")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
t := &wrappedConn{conn}
|
||||
var w io.Writer = t
|
||||
if n, err := w.Write([]byte("hello")); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println(n)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
37
_test/issue-1333.go
Normal file
37
_test/issue-1333.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
func mock(name string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprint(rw, "Hello ", name)
|
||||
}
|
||||
}
|
||||
|
||||
func client(uri string) {
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
mux.Handle("/", mock("foo"))
|
||||
client(server.URL)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello foo
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
@@ -36,7 +35,7 @@ type pipe struct {
|
||||
|
||||
func newReadAutoCloser(r io.Reader) readAutoCloser {
|
||||
if _, ok := r.(io.Closer); !ok {
|
||||
return readAutoCloser{ioutil.NopCloser(r)}
|
||||
return readAutoCloser{io.NopCloser(r)}
|
||||
}
|
||||
return readAutoCloser{r.(io.ReadCloser)}
|
||||
}
|
||||
@@ -44,7 +43,7 @@ func newReadAutoCloser(r io.Reader) readAutoCloser {
|
||||
func main() {
|
||||
p := &pipe{}
|
||||
p.Reader = newReadAutoCloser(strings.NewReader("test"))
|
||||
b, err := ioutil.ReadAll(p.Reader)
|
||||
b, err := io.ReadAll(p.Reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
14
_test/method38.go
Normal file
14
_test/method38.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/traefik/yaegi/_test/method38"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(method38.Get())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{[] {<nil>}}
|
||||
19
_test/method38/a.go
Normal file
19
_test/method38/a.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package method38
|
||||
|
||||
import "sync"
|
||||
|
||||
func NewPool() Pool { return Pool{} }
|
||||
|
||||
type Buffer struct {
|
||||
bs []byte
|
||||
pool Pool
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
var (
|
||||
_pool = NewPool()
|
||||
Get = _pool.Get
|
||||
)
|
||||
3
_test/method38/b.go
Normal file
3
_test/method38/b.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package method38
|
||||
|
||||
func (p Pool) Get() *Buffer { return &Buffer{} }
|
||||
32
_test/method39.go
Normal file
32
_test/method39.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/traefik/yaegi/_test/method38"
|
||||
)
|
||||
|
||||
func NewPool() Pool { return Pool{} }
|
||||
|
||||
type Buffer struct {
|
||||
bs []byte
|
||||
pool Pool
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
var (
|
||||
_pool = NewPool()
|
||||
Get = _pool.Get
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
fmt.Println(Get())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 17:11: undefined selector Get
|
||||
@@ -7,4 +7,4 @@ func main() {
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:2: invalid operation: mismatched types int and float64
|
||||
// 5:2: invalid operation: mismatched types int and untyped float
|
||||
|
||||
@@ -2,11 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
_, err := ioutil.ReadFile("__NotExisting__")
|
||||
_, err := os.ReadFile("__NotExisting__")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
22
_test/rune2.go
Normal file
22
_test/rune2.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const majorVersion = '2'
|
||||
|
||||
type hashed struct {
|
||||
major byte
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(majorVersion)
|
||||
|
||||
p := new(hashed)
|
||||
p.major = majorVersion
|
||||
|
||||
fmt.Println(p)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 50
|
||||
// &{50}
|
||||
@@ -9,10 +9,20 @@ func test(time string, t time.Time) string {
|
||||
return time
|
||||
}
|
||||
|
||||
var zero = time.Time{}
|
||||
|
||||
func test2(time string) time.Time {
|
||||
return zero
|
||||
}
|
||||
|
||||
func main() {
|
||||
str := test("test", time.Now())
|
||||
fmt.Println(str)
|
||||
|
||||
str2 := test2("test2")
|
||||
fmt.Println(str2)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
// 0001-01-01 00:00:00 +0000 UTC
|
||||
|
||||
@@ -8,6 +8,7 @@ type A struct {
|
||||
}
|
||||
|
||||
type D struct {
|
||||
F *A
|
||||
E *A
|
||||
}
|
||||
|
||||
|
||||
22
_test/struct61.go
Normal file
22
_test/struct61.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type A struct {
|
||||
B string
|
||||
D
|
||||
}
|
||||
|
||||
type D struct {
|
||||
*A
|
||||
E *A
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := &A{B: "b"}
|
||||
a.D = D{E: a}
|
||||
fmt.Println(a.D.E.B)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// b
|
||||
11
_test/struct62.go
Normal file
11
_test/struct62.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
type A struct{ *A }
|
||||
v := &A{}
|
||||
v.A = v
|
||||
println("v.A.A = v", v.A.A == v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// v.A.A = v true
|
||||
17
_test/type27.go
Normal file
17
_test/type27.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Foo = int
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return f * f
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 7:1: cannot define new methods on non-local type int
|
||||
17
_test/type28.go
Normal file
17
_test/type28.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Foo = int
|
||||
|
||||
func (f *Foo) Bar() int {
|
||||
return *f * *f
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 7:1: cannot define new methods on non-local type int
|
||||
12
_test/type29.go
Normal file
12
_test/type29.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var Foo int
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
12
_test/type30.go
Normal file
12
_test/type30.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var Foo *int
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
17
_test/type31.go
Normal file
17
_test/type31.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return f * f
|
||||
}
|
||||
|
||||
type Foo = int
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
17
_test/type32.go
Normal file
17
_test/type32.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (f *Foo) Bar() int {
|
||||
return *f * *f
|
||||
}
|
||||
|
||||
type Foo = int
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
11
_test/type33.go
Normal file
11
_test/type33.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func (f *Foo) Bar() int {
|
||||
return *f * *f
|
||||
}
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 3:1: undefined: Foo
|
||||
@@ -1,11 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const SSize = 16
|
||||
const (
|
||||
SSize = 16
|
||||
WSize = bits.UintSize / 8
|
||||
)
|
||||
|
||||
type S struct {
|
||||
X int
|
||||
@@ -13,14 +16,19 @@ type S struct {
|
||||
}
|
||||
|
||||
func main() {
|
||||
bigEndian := (*(*[2]uint8)(unsafe.Pointer(&[]uint16{1}[0])))[0] == 0
|
||||
var sBuf [SSize]byte
|
||||
s := (*S)(unsafe.Pointer(&sBuf[0]))
|
||||
|
||||
s.X = 2
|
||||
s.Y = 4
|
||||
|
||||
fmt.Println(sBuf)
|
||||
if bigEndian {
|
||||
println(sBuf[0+WSize-1], sBuf[WSize+WSize-1])
|
||||
} else {
|
||||
println(sBuf[0], sBuf[WSize])
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [2 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0]
|
||||
// 2 4
|
||||
|
||||
@@ -2,10 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const SSize = 24
|
||||
const WSize = bits.UintSize / 8
|
||||
|
||||
type S struct {
|
||||
X int
|
||||
@@ -20,7 +21,8 @@ func main() {
|
||||
{X: 3},
|
||||
}
|
||||
addr := unsafe.Pointer(&arr[0])
|
||||
s := *(*S)(unsafe.Pointer(uintptr(addr) + SSize*2))
|
||||
// s := *(*S)(unsafe.Pointer(uintptr(addr) + SSize*2))
|
||||
s := *(*S)(unsafe.Pointer(uintptr(addr) + WSize*6))
|
||||
|
||||
fmt.Println(s.X)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const WSize = bits.UintSize / 8
|
||||
|
||||
type S struct {
|
||||
X int
|
||||
Y int
|
||||
@@ -13,12 +15,12 @@ type S struct {
|
||||
|
||||
func main() {
|
||||
x := S{}
|
||||
size := unsafe.Sizeof(x)
|
||||
align := unsafe.Alignof(x.Y)
|
||||
offset := unsafe.Offsetof(x.Z)
|
||||
size := unsafe.Sizeof(x) / WSize
|
||||
align := unsafe.Alignof(x.Y) / WSize
|
||||
offset := unsafe.Offsetof(x.Z) / WSize
|
||||
|
||||
fmt.Println(size, align, offset)
|
||||
println(size, align, offset)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 24 8 16
|
||||
// 3 1 2
|
||||
|
||||
23
_test/unsafe6.go
Normal file
23
_test/unsafe6.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
X int
|
||||
Y int
|
||||
Z int
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := S{Z: 5}
|
||||
ptr := unsafe.Pointer(&x)
|
||||
offset := int(unsafe.Offsetof(x.Z))
|
||||
p := unsafe.Add(ptr, offset)
|
||||
|
||||
i := *(*int)(p)
|
||||
|
||||
fmt.Println(i)
|
||||
}
|
||||
20
_test/unsafe7.go
Normal file
20
_test/unsafe7.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
X int
|
||||
Y int
|
||||
Z int
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := [2]S{{Z: 5}, {Z: 10}}
|
||||
|
||||
s := unsafe.Slice(&x[0], 2)
|
||||
|
||||
fmt.Println(s)
|
||||
}
|
||||
@@ -18,12 +18,14 @@ func extractCmd(arg []string) error {
|
||||
var name string
|
||||
var exclude string
|
||||
var include string
|
||||
var tag string
|
||||
|
||||
eflag := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||
eflag.StringVar(&licensePath, "license", "", "path to a LICENSE file")
|
||||
eflag.StringVar(&name, "name", "", "the namespace for the extracted symbols")
|
||||
eflag.StringVar(&exclude, "exclude", "", "comma separated list of regexp matching symbols to exclude")
|
||||
eflag.StringVar(&include, "include", "", "comma separated list of regexp matching symbols to include")
|
||||
eflag.StringVar(&tag, "tag", "", "comma separated list of build tags to be added to the created package")
|
||||
eflag.Usage = func() {
|
||||
fmt.Println("Usage: yaegi extract [options] packages...")
|
||||
fmt.Println("Options:")
|
||||
@@ -56,6 +58,9 @@ func extractCmd(arg []string) error {
|
||||
Dest: name,
|
||||
License: license,
|
||||
}
|
||||
if tag != "" {
|
||||
ext.Tag = strings.Split(tag, ",")
|
||||
}
|
||||
|
||||
if exclude != "" {
|
||||
ext.Exclude = strings.Split(exclude, ",")
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -47,7 +46,12 @@ func run(arg []string) error {
|
||||
}
|
||||
args := rflag.Args()
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i := interp.New(interp.Options{
|
||||
GoPath: build.Default.GOPATH,
|
||||
BuildTags: strings.Split(tags, ","),
|
||||
Env: os.Environ(),
|
||||
Unrestricted: useUnrestricted,
|
||||
})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -130,7 +134,7 @@ func isFile(path string) bool {
|
||||
}
|
||||
|
||||
func runFile(i *interp.Interpreter, path string, noAutoImport bool) error {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -116,7 +116,12 @@ func test(arg []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i := interp.New(interp.Options{
|
||||
GoPath: build.Default.GOPATH,
|
||||
BuildTags: strings.Split(tags, ","),
|
||||
Env: os.Environ(),
|
||||
Unrestricted: useUnrestricted,
|
||||
})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -37,7 +37,7 @@ func applyCIMultiplier(timeout time.Duration) time.Duration {
|
||||
}
|
||||
|
||||
func TestYaegiCmdCancel(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "yaegi-")
|
||||
tmp, err := os.MkdirTemp("", "yaegi-")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tmp directory: %v", err)
|
||||
}
|
||||
@@ -49,7 +49,15 @@ func TestYaegiCmdCancel(t *testing.T) {
|
||||
}()
|
||||
|
||||
yaegi := filepath.Join(tmp, "yaegi")
|
||||
build := exec.Command("go", "build", "-race", "-o", yaegi, ".")
|
||||
|
||||
args := []string{"build"}
|
||||
if raceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
|
||||
args = append(args, "-race")
|
||||
}
|
||||
args = append(args, "-o", yaegi, ".")
|
||||
|
||||
build := exec.Command("go", args...)
|
||||
|
||||
out, err := build.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to build yaegi command: %v: %s", err, out)
|
||||
@@ -115,3 +123,16 @@ func TestYaegiCmdCancel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func raceDetectorSupported(goos, goarch string) bool {
|
||||
switch goos {
|
||||
case "linux":
|
||||
return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64"
|
||||
case "darwin":
|
||||
return goarch == "amd64" || goarch == "arm64"
|
||||
case "freebsd", "netbsd", "openbsd", "windows":
|
||||
return goarch == "amd64"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
67
example/fs/fs_test.go
Normal file
67
example/fs/fs_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package fs1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
// only available from 1.16.
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/traefik/yaegi/interp"
|
||||
"github.com/traefik/yaegi/stdlib"
|
||||
)
|
||||
|
||||
var testFilesystem = fstest.MapFS{
|
||||
"main.go": &fstest.MapFile{
|
||||
Data: []byte(`package main
|
||||
|
||||
import (
|
||||
"foo/bar"
|
||||
"./localfoo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
bar.PrintSomething()
|
||||
localfoo.PrintSomethingElse()
|
||||
}
|
||||
`),
|
||||
},
|
||||
"_pkg/src/foo/bar/bar.go": &fstest.MapFile{
|
||||
Data: []byte(`package bar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func PrintSomething() {
|
||||
fmt.Println("I am a virtual filesystem printing something from _pkg/src/foo/bar/bar.go!")
|
||||
}
|
||||
`),
|
||||
},
|
||||
"localfoo/foo.go": &fstest.MapFile{
|
||||
Data: []byte(`package localfoo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func PrintSomethingElse() {
|
||||
fmt.Println("I am virtual filesystem printing else from localfoo/foo.go!")
|
||||
}
|
||||
`),
|
||||
},
|
||||
}
|
||||
|
||||
func TestFilesystemMapFS(t *testing.T) {
|
||||
i := interp.New(interp.Options{
|
||||
GoPath: "./_pkg",
|
||||
SourcecodeFilesystem: testFilesystem,
|
||||
})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err := i.EvalPath(`main.go`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,14 @@ func init() {
|
||||
{{end}}
|
||||
}
|
||||
{{range $m := $value.Method -}}
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} { {{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}} }
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} {
|
||||
{{- if eq $m.Name "String"}}
|
||||
if W.WString == nil {
|
||||
return ""
|
||||
}
|
||||
{{end -}}
|
||||
{{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}}
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
@@ -270,6 +277,15 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range e.Tag {
|
||||
if len(t) != 0 {
|
||||
buildTags += "," + t
|
||||
}
|
||||
}
|
||||
if len(buildTags) != 0 && buildTags[0] == ',' {
|
||||
buildTags = buildTags[1:]
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
data := map[string]interface{}{
|
||||
"Dest": e.Dest,
|
||||
@@ -336,6 +352,7 @@ type Extractor struct {
|
||||
License string // License text to be included in the created package, optional.
|
||||
Exclude []string // Comma separated list of regexp matching symbols to exclude.
|
||||
Include []string // Comma separated list of regexp matching symbols to include.
|
||||
Tag []string // Comma separated of build tags to be added to the created package.
|
||||
}
|
||||
|
||||
// importPath checks whether pkgIdent is an existing directory relative to
|
||||
|
||||
@@ -3,8 +3,8 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
@@ -557,7 +557,7 @@ func {{$name}}(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -579,7 +579,7 @@ func {{$name}}(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -602,7 +602,7 @@ func {{$name}}(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -1183,7 +1183,7 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile("op.go", source, 0666); err != nil {
|
||||
if err = os.WriteFile("op.go", source, 0o666); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
61
internal/unsafe2/unsafe.go
Normal file
61
internal/unsafe2/unsafe.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package unsafe2
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type dummy struct{}
|
||||
|
||||
// DummyType represents a stand-in for a recursive type.
|
||||
var DummyType = reflect.TypeOf(dummy{})
|
||||
|
||||
// the following type sizes must match their original definition in Go src/reflect/type.go.
|
||||
|
||||
type rtype struct {
|
||||
_ uintptr
|
||||
_ uintptr
|
||||
_ uint32
|
||||
_ uint32
|
||||
_ uintptr
|
||||
_ uintptr
|
||||
_ uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type emptyInterface struct {
|
||||
typ *rtype
|
||||
_ unsafe.Pointer
|
||||
}
|
||||
|
||||
type structField struct {
|
||||
_ uintptr
|
||||
typ *rtype
|
||||
_ uintptr
|
||||
}
|
||||
|
||||
type structType struct {
|
||||
rtype
|
||||
_ uintptr
|
||||
fields []structField
|
||||
}
|
||||
|
||||
// SetFieldType sets the type of the struct field at the given index, to the given type.
|
||||
//
|
||||
// The struct type must have been created at runtime. This is very unsafe.
|
||||
func SetFieldType(s reflect.Type, idx int, t reflect.Type) {
|
||||
if s.Kind() != reflect.Struct || idx >= s.NumField() {
|
||||
return
|
||||
}
|
||||
|
||||
rtyp := unpackType(s)
|
||||
styp := (*structType)(unsafe.Pointer(rtyp))
|
||||
f := styp.fields[idx]
|
||||
f.typ = unpackType(t)
|
||||
styp.fields[idx] = f
|
||||
}
|
||||
|
||||
func unpackType(t reflect.Type) *rtype {
|
||||
v := reflect.New(t).Elem().Interface()
|
||||
return (*emptyInterface)(unsafe.Pointer(&v)).typ
|
||||
}
|
||||
33
internal/unsafe2/unsafe_test.go
Normal file
33
internal/unsafe2/unsafe_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package unsafe2_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/traefik/yaegi/internal/unsafe2"
|
||||
)
|
||||
|
||||
func TestSwapFieldType(t *testing.T) {
|
||||
f := []reflect.StructField{
|
||||
{
|
||||
Name: "A",
|
||||
Type: reflect.TypeOf(int(0)),
|
||||
},
|
||||
{
|
||||
Name: "B",
|
||||
Type: reflect.PtrTo(unsafe2.DummyType),
|
||||
},
|
||||
{
|
||||
Name: "C",
|
||||
Type: reflect.TypeOf(int64(0)),
|
||||
},
|
||||
}
|
||||
typ := reflect.StructOf(f)
|
||||
ntyp := reflect.PtrTo(typ)
|
||||
|
||||
unsafe2.SetFieldType(typ, 1, ntyp)
|
||||
|
||||
if typ.Field(1).Type != ntyp {
|
||||
t.Fatalf("unexpected field type: want %s; got %s", ntyp, typ.Field(1).Type)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
@@ -93,7 +92,8 @@ const (
|
||||
switchIfStmt
|
||||
typeAssertExpr
|
||||
typeDecl
|
||||
typeSpec
|
||||
typeSpec // type A int
|
||||
typeSpecAssign // type A = int
|
||||
typeSwitch
|
||||
unaryExpr
|
||||
valueSpec
|
||||
@@ -176,6 +176,7 @@ var kinds = [...]string{
|
||||
typeAssertExpr: "typeAssertExpr",
|
||||
typeDecl: "typeDecl",
|
||||
typeSpec: "typeSpec",
|
||||
typeSpecAssign: "typeSpecAssign",
|
||||
typeSwitch: "typeSwitch",
|
||||
unaryExpr: "unaryExpr",
|
||||
valueSpec: "valueSpec",
|
||||
@@ -360,21 +361,14 @@ func wrapInMain(src string) string {
|
||||
return fmt.Sprintf("package main; func main() {%s\n}", src)
|
||||
}
|
||||
|
||||
// Note: no type analysis is performed at this stage, it is done in pre-order
|
||||
// processing of CFG, in order to accommodate forward type declarations.
|
||||
|
||||
// ast parses src string containing Go code and generates the corresponding AST.
|
||||
// The package name and the AST root node are returned.
|
||||
// The given name is used to set the filename of the relevant source file in the
|
||||
// interpreter's FileSet.
|
||||
func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error) {
|
||||
var inFunc bool
|
||||
func (interp *Interpreter) parse(src, name string, inc bool) (node ast.Node, err error) {
|
||||
mode := parser.DeclarationErrors
|
||||
|
||||
// Allow incremental parsing of declarations or statements, by inserting
|
||||
// them in a pseudo file package or function. Those statements or
|
||||
// declarations will be always evaluated in the global scope.
|
||||
var tok token.Token
|
||||
var inFunc bool
|
||||
if inc {
|
||||
tok = interp.firstToken(src)
|
||||
switch tok {
|
||||
@@ -391,18 +385,18 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
}
|
||||
|
||||
if ok, err := interp.buildOk(&interp.context, name, src); !ok || err != nil {
|
||||
return "", nil, err // skip source not matching build constraints
|
||||
return nil, err // skip source not matching build constraints
|
||||
}
|
||||
|
||||
f, err := parser.ParseFile(interp.fset, name, src, mode)
|
||||
if err != nil {
|
||||
// only retry if we're on an expression/statement about a func
|
||||
if !inc || tok != token.FUNC {
|
||||
return "", nil, err
|
||||
return nil, err
|
||||
}
|
||||
// do not bother retrying if we know it's an error we're going to ignore later on.
|
||||
if ignoreError(err, src) {
|
||||
return "", nil, err
|
||||
return nil, err
|
||||
}
|
||||
// do not lose initial error, in case retrying fails.
|
||||
initialError := err
|
||||
@@ -410,16 +404,32 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
src := wrapInMain(strings.TrimPrefix(src, "package main;"))
|
||||
f, err = parser.ParseFile(interp.fset, name, src, mode)
|
||||
if err != nil {
|
||||
return "", nil, initialError
|
||||
return nil, initialError
|
||||
}
|
||||
}
|
||||
|
||||
setYaegiTags(&interp.context, f.Comments)
|
||||
if inFunc {
|
||||
// return the body of the wrapper main function
|
||||
return f.Decls[0].(*ast.FuncDecl).Body, nil
|
||||
}
|
||||
|
||||
setYaegiTags(&interp.context, f.Comments)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Note: no type analysis is performed at this stage, it is done in pre-order
|
||||
// processing of CFG, in order to accommodate forward type declarations.
|
||||
|
||||
// ast parses src string containing Go code and generates the corresponding AST.
|
||||
// The package name and the AST root node are returned.
|
||||
// The given name is used to set the filename of the relevant source file in the
|
||||
// interpreter's FileSet.
|
||||
func (interp *Interpreter) ast(f ast.Node) (string, *node, error) {
|
||||
var err error
|
||||
var root *node
|
||||
var anc astNode
|
||||
var st nodestack
|
||||
var pkgName string
|
||||
pkgName := "main"
|
||||
|
||||
addChild := func(root **node, anc astNode, pos token.Pos, kind nkind, act action) *node {
|
||||
var i interface{}
|
||||
@@ -627,7 +637,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
}
|
||||
st.push(addChild(&root, anc, pos, kind, aNop), nod)
|
||||
|
||||
case *ast.CommentGroup:
|
||||
case *ast.CommentGroup, *ast.EmptyStmt:
|
||||
return false
|
||||
|
||||
case *ast.CompositeLit:
|
||||
@@ -826,6 +836,10 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
st.push(addChild(&root, anc, pos, typeAssertExpr, aTypeAssert), nod)
|
||||
|
||||
case *ast.TypeSpec:
|
||||
if a.Assign.IsValid() {
|
||||
st.push(addChild(&root, anc, pos, typeSpecAssign, aNop), nod)
|
||||
break
|
||||
}
|
||||
st.push(addChild(&root, anc, pos, typeSpec, aNop), nod)
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
@@ -892,15 +906,8 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
}
|
||||
return true
|
||||
})
|
||||
if inFunc {
|
||||
// Incremental parsing: statements were inserted in a pseudo function.
|
||||
// Set root to function body so its statements are evaluated in global scope.
|
||||
root = root.child[1].child[3]
|
||||
root.anc = nil
|
||||
}
|
||||
if pkgName == "" {
|
||||
return "", root, errors.New("no package name found")
|
||||
}
|
||||
|
||||
interp.roots = append(interp.roots, root)
|
||||
return pkgName, root, err
|
||||
}
|
||||
|
||||
|
||||
228
interp/cfg.go
228
interp/cfg.go
@@ -49,9 +49,11 @@ const nilIdent = "nil"
|
||||
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
||||
// variables. A list of nodes of init functions is returned.
|
||||
// Following this pass, the CFG is ready to run.
|
||||
func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(importPath)
|
||||
check := typecheck{}
|
||||
func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string) ([]*node, error) {
|
||||
if sc == nil {
|
||||
sc = interp.initScopePkg(importPath, pkgName)
|
||||
}
|
||||
check := typecheck{scope: sc}
|
||||
var initNodes []*node
|
||||
var err error
|
||||
|
||||
@@ -62,6 +64,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if n.scope == nil {
|
||||
n.scope = sc
|
||||
}
|
||||
switch n.kind {
|
||||
case binaryExpr, unaryExpr, parenExpr:
|
||||
if isBoolAction(n) {
|
||||
@@ -145,10 +150,10 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
switch typ.Kind() {
|
||||
case reflect.Map:
|
||||
n.anc.gen = rangeMap
|
||||
ityp := &itype{cat: valueT, rtype: reflect.TypeOf((*reflect.MapIter)(nil))}
|
||||
ityp := valueTOf(reflect.TypeOf((*reflect.MapIter)(nil)))
|
||||
sc.add(ityp)
|
||||
ktyp = &itype{cat: valueT, rtype: typ.Key()}
|
||||
vtyp = &itype{cat: valueT, rtype: typ.Elem()}
|
||||
ktyp = valueTOf(typ.Key())
|
||||
vtyp = valueTOf(typ.Elem())
|
||||
case reflect.String:
|
||||
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
|
||||
sc.add(sc.getType("int")) // Add a dummy type to store index for range
|
||||
@@ -157,11 +162,11 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
case reflect.Array, reflect.Slice:
|
||||
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
|
||||
ktyp = sc.getType("int")
|
||||
vtyp = &itype{cat: valueT, rtype: typ.Elem()}
|
||||
vtyp = valueTOf(typ.Elem())
|
||||
}
|
||||
case mapT:
|
||||
n.anc.gen = rangeMap
|
||||
ityp := &itype{cat: valueT, rtype: reflect.TypeOf((*reflect.MapIter)(nil))}
|
||||
ityp := valueTOf(reflect.TypeOf((*reflect.MapIter)(nil)))
|
||||
sc.add(ityp)
|
||||
ktyp = o.typ.key
|
||||
vtyp = o.typ.val
|
||||
@@ -169,7 +174,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
ktyp = sc.getType("int")
|
||||
vtyp = o.typ.val
|
||||
if vtyp.cat == valueT {
|
||||
vtyp = &itype{cat: valueT, rtype: vtyp.rtype.Elem()}
|
||||
vtyp = valueTOf(vtyp.rtype.Elem())
|
||||
} else {
|
||||
vtyp = vtyp.val
|
||||
}
|
||||
@@ -284,19 +289,19 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
|
||||
case compositeLitExpr:
|
||||
if len(n.child) > 0 && n.child[0].isType(sc) {
|
||||
// Get type from 1st child
|
||||
// Get type from 1st child.
|
||||
if n.typ, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return false
|
||||
}
|
||||
// Indicate that the first child is the type
|
||||
// Indicate that the first child is the type.
|
||||
n.nleft = 1
|
||||
} else {
|
||||
// Get type from ancestor (implicit type)
|
||||
if n.anc.kind == keyValueExpr && n == n.anc.child[0] {
|
||||
n.typ = n.anc.typ.key
|
||||
} else if atyp := n.anc.typ; atyp != nil {
|
||||
if atyp.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: atyp.rtype.Elem()}
|
||||
if atyp.cat == valueT && hasElem(atyp.rtype) {
|
||||
n.typ = valueTOf(atyp.rtype.Elem())
|
||||
} else {
|
||||
n.typ = atyp.val
|
||||
}
|
||||
@@ -410,11 +415,11 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
sc.loop = n
|
||||
|
||||
case importSpec:
|
||||
// already all done in gta
|
||||
// Already all done in GTA.
|
||||
return false
|
||||
|
||||
case typeSpec:
|
||||
// processing already done in GTA pass for global types, only parses inlined types
|
||||
// Processing already done in GTA pass for global types, only parses inlined types.
|
||||
if sc.def == nil {
|
||||
return false
|
||||
}
|
||||
@@ -424,13 +429,16 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
return false
|
||||
}
|
||||
if typ.incomplete {
|
||||
err = n.cfgErrorf("invalid type declaration")
|
||||
return false
|
||||
// Type may still be incomplete in case of a local recursive struct declaration.
|
||||
if typ, err = typ.finalize(); err != nil {
|
||||
err = n.cfgErrorf("invalid type declaration")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
switch n.child[1].kind {
|
||||
case identExpr, selectorExpr:
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
|
||||
n.typ = namedOf(typ, pkgName, typeName)
|
||||
default:
|
||||
n.typ = typ
|
||||
n.typ.name = typeName
|
||||
@@ -443,7 +451,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// values which may be used in further declarations.
|
||||
if !sc.global {
|
||||
for _, c := range n.child {
|
||||
if _, err = interp.cfg(c, importPath); err != nil {
|
||||
if _, err = interp.cfg(c, sc, importPath, pkgName); err != nil {
|
||||
// No error processing here, to allow recovery in subtree nodes.
|
||||
err = nil
|
||||
}
|
||||
@@ -477,7 +485,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
break
|
||||
}
|
||||
|
||||
n.typ = &itype{cat: ptrT, val: n.child[0].typ}
|
||||
n.typ = ptrOf(n.child[0].typ)
|
||||
n.findex = sc.add(n.typ)
|
||||
|
||||
case assignStmt, defineStmt:
|
||||
@@ -513,7 +521,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
return
|
||||
}
|
||||
if src.typ.isBinMethod {
|
||||
dest.typ = &itype{cat: valueT, rtype: src.typ.methodCallType()}
|
||||
dest.typ = valueTOf(src.typ.methodCallType())
|
||||
} else {
|
||||
// In a new definition, propagate the source type to the destination
|
||||
// type. If the source is an untyped constant, make sure that the
|
||||
@@ -572,7 +580,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
dest.gen = nop
|
||||
case isFuncField(dest):
|
||||
// Setting a struct field of function type requires an extra step. Do not optimize.
|
||||
case isCall(src) && !isInterfaceSrc(dest.typ) && !isRecursiveField(dest) && n.kind != defineStmt:
|
||||
case isCall(src) && !isInterfaceSrc(dest.typ) && n.kind != defineStmt:
|
||||
// Call action may perform the assignment directly.
|
||||
if dest.typ.id() != src.typ.id() {
|
||||
// Skip optimitization if returned type doesn't match assigned one.
|
||||
@@ -594,8 +602,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// Skip optimisation for assigned interface.
|
||||
break
|
||||
}
|
||||
if dest.action == aGetIndex {
|
||||
// Skip optimization, as it does not work when assigning to a struct field.
|
||||
if dest.action == aGetIndex || dest.action == aStar {
|
||||
// Skip optimization, as it does not work when assigning to a struct field or a dereferenced pointer.
|
||||
break
|
||||
}
|
||||
n.gen = nop
|
||||
@@ -657,7 +665,12 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if r := lc.child[0].typ.numOut(); r != l {
|
||||
err = n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, lc.child[0].name(), r)
|
||||
}
|
||||
n.gen = nop
|
||||
if isBinCall(lc, sc) {
|
||||
n.gen = nop
|
||||
} else {
|
||||
// TODO (marc): skip if no conversion or wrapping is needed.
|
||||
n.gen = assignFromCall
|
||||
}
|
||||
case indexExpr:
|
||||
lc.gen = getIndexMap2
|
||||
n.gen = nop
|
||||
@@ -762,7 +775,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
case ptrT:
|
||||
n.typ = t.val
|
||||
if t.val.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: t.val.rtype.Elem()}
|
||||
n.typ = valueTOf(t.val.rtype.Elem())
|
||||
} else {
|
||||
n.typ = t.val.val
|
||||
}
|
||||
@@ -772,7 +785,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if t.rtype.Kind() == reflect.String {
|
||||
n.typ = sc.getType("byte")
|
||||
} else {
|
||||
n.typ = &itype{cat: valueT, rtype: t.rtype.Elem()}
|
||||
n.typ = valueTOf(t.rtype.Elem())
|
||||
}
|
||||
default:
|
||||
n.typ = t.val
|
||||
@@ -858,7 +871,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
|
||||
case labeledStmt:
|
||||
wireChild(n)
|
||||
n.start = n.child[1].start
|
||||
if len(n.child) > 1 {
|
||||
n.start = n.child[1].start
|
||||
}
|
||||
gotoLabel(n.sym)
|
||||
|
||||
case callExpr:
|
||||
@@ -885,7 +900,11 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// Store result directly to frame output location, to avoid a frame copy.
|
||||
n.findex = 0
|
||||
case bname == "cap" && isInConstOrTypeDecl(n):
|
||||
switch n.child[1].typ.TypeOf().Kind() {
|
||||
t := n.child[1].typ.TypeOf()
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan:
|
||||
capConst(n)
|
||||
default:
|
||||
@@ -894,7 +913,11 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
n.findex = notInFrame
|
||||
n.gen = nop
|
||||
case bname == "len" && isInConstOrTypeDecl(n):
|
||||
switch n.child[1].typ.TypeOf().Kind() {
|
||||
t := n.child[1].typ.TypeOf()
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.String:
|
||||
lenConst(n)
|
||||
default:
|
||||
@@ -955,7 +978,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
n.typ = c0.typ
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
case isBinCall(n):
|
||||
case isBinCall(n, sc):
|
||||
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
|
||||
if err != nil {
|
||||
break
|
||||
@@ -973,13 +996,13 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
sc.add(funcType.ret[i])
|
||||
}
|
||||
} else {
|
||||
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
|
||||
n.typ = valueTOf(typ.Out(0))
|
||||
if n.anc.kind == returnStmt {
|
||||
n.findex = childPos(n)
|
||||
} else {
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < typ.NumOut(); i++ {
|
||||
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
|
||||
sc.add(valueTOf(typ.Out(i)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -995,7 +1018,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
err = n.cfgErrorf("struct does not contain field: %s", c1.child[1].ident)
|
||||
break
|
||||
}
|
||||
n.typ = &itype{cat: valueT, rtype: reflect.TypeOf(field.Offset)}
|
||||
n.typ = valueTOf(reflect.TypeOf(field.Offset))
|
||||
n.rval = reflect.ValueOf(field.Offset)
|
||||
n.gen = nop
|
||||
default:
|
||||
@@ -1081,8 +1104,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
case reflect.Struct:
|
||||
err = check.structBinLitExpr(child, rtype)
|
||||
case reflect.Map:
|
||||
ktyp := &itype{cat: valueT, rtype: rtype.Key()}
|
||||
vtyp := &itype{cat: valueT, rtype: rtype.Elem()}
|
||||
ktyp := valueTOf(rtype.Key())
|
||||
vtyp := valueTOf(rtype.Elem())
|
||||
err = check.mapLitExpr(child, ktyp, vtyp)
|
||||
}
|
||||
}
|
||||
@@ -1223,7 +1246,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
|
||||
case funcDecl:
|
||||
n.start = n.child[3].start
|
||||
n.types = sc.types
|
||||
n.types, n.scope = sc.types, sc
|
||||
sc = sc.pop()
|
||||
funcName := n.child[1].ident
|
||||
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil {
|
||||
@@ -1234,7 +1257,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
}
|
||||
|
||||
case funcLit:
|
||||
n.types = sc.types
|
||||
n.types, n.scope = sc.types, sc
|
||||
sc = sc.pop()
|
||||
err = genRun(n)
|
||||
|
||||
@@ -1255,7 +1278,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// 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", n.ident)
|
||||
err = n.cfgErrorf("undefined: %s %d", n.ident, n.index)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1480,9 +1503,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
pkg := n.child[0].sym.typ.path
|
||||
if s, ok := interp.binPkg[pkg][name]; ok {
|
||||
if isBinType(s) {
|
||||
n.typ = &itype{cat: valueT, rtype: s.Type().Elem()}
|
||||
n.typ = valueTOf(s.Type().Elem())
|
||||
} else {
|
||||
n.typ = &itype{cat: valueT, rtype: fixPossibleConstType(s.Type()), untyped: isValueUntyped(s)}
|
||||
n.typ = valueTOf(fixPossibleConstType(s.Type()), withUntyped(isValueUntyped(s)))
|
||||
n.rval = s
|
||||
}
|
||||
n.action = aGetSym
|
||||
@@ -1534,16 +1557,14 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
n.gen = getPtrIndexSeq
|
||||
if n.typ.cat == funcT {
|
||||
// Function in a struct field is always wrapped in reflect.Value.
|
||||
rtype := n.typ.TypeOf()
|
||||
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
|
||||
n.typ = wrapperValueTOf(n.typ.TypeOf(), n.typ)
|
||||
}
|
||||
default:
|
||||
n.gen = getIndexSeq
|
||||
n.typ = n.typ.fieldSeq(ti)
|
||||
if n.typ.cat == funcT {
|
||||
// Function in a struct field is always wrapped in reflect.Value.
|
||||
rtype := n.typ.TypeOf()
|
||||
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
|
||||
n.typ = wrapperValueTOf(n.typ.TypeOf(), n.typ)
|
||||
}
|
||||
}
|
||||
break
|
||||
@@ -1565,7 +1586,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
n.val = lind
|
||||
n.typ = &itype{cat: valueT, rtype: s.Type}
|
||||
n.typ = valueTOf(s.Type)
|
||||
break
|
||||
}
|
||||
// No field (embedded or not) matched. Try to match a method.
|
||||
@@ -1582,13 +1603,13 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
n.gen = getIndexBinMethod
|
||||
n.action = aGetMethod
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true}
|
||||
n.typ = valueTOf(method.Type, isBinMethod())
|
||||
if hasRecvType {
|
||||
n.typ.recv = n.typ
|
||||
}
|
||||
case n.typ.rtype.Kind() == reflect.Ptr:
|
||||
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
|
||||
n.typ = &itype{cat: valueT, rtype: field.Type}
|
||||
n.typ = valueTOf(field.Type)
|
||||
n.val = field.Index
|
||||
n.gen = getPtrIndexSeq
|
||||
break
|
||||
@@ -1596,7 +1617,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident)
|
||||
case n.typ.rtype.Kind() == reflect.Struct:
|
||||
if field, ok := n.typ.rtype.FieldByName(n.child[1].ident); ok {
|
||||
n.typ = &itype{cat: valueT, rtype: field.Type}
|
||||
n.typ = valueTOf(field.Type)
|
||||
n.val = field.Index
|
||||
n.gen = getIndexSeq
|
||||
break
|
||||
@@ -1608,7 +1629,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if m2, ok2 := pt.MethodByName(n.child[1].ident); ok2 {
|
||||
n.val = m2.Index
|
||||
n.gen = getIndexBinPtrMethod
|
||||
n.typ = &itype{cat: valueT, rtype: m2.Type, recv: &itype{cat: valueT, rtype: pt}, isBinMethod: true}
|
||||
n.typ = valueTOf(m2.Type, isBinMethod(), withRecv(valueTOf(pt)))
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.action = aGetMethod
|
||||
break
|
||||
@@ -1619,18 +1640,18 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// Handle pointer on object defined in runtime
|
||||
if method, ok := n.typ.val.rtype.MethodByName(n.child[1].ident); ok {
|
||||
n.val = method.Index
|
||||
n.typ = &itype{cat: valueT, rtype: method.Type, recv: n.typ, isBinMethod: true}
|
||||
n.typ = valueTOf(method.Type, isBinMethod(), withRecv(n.typ))
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.gen = getIndexBinElemMethod
|
||||
n.action = aGetMethod
|
||||
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
|
||||
n.val = method.Index
|
||||
n.gen = getIndexBinMethod
|
||||
n.typ = &itype{cat: valueT, rtype: method.Type, recv: &itype{cat: valueT, rtype: reflect.PtrTo(n.typ.val.rtype)}, isBinMethod: true}
|
||||
n.typ = valueTOf(method.Type, withRecv(valueTOf(reflect.PtrTo(n.typ.val.rtype), isBinMethod())))
|
||||
n.recv = &receiver{node: n.child[0]}
|
||||
n.action = aGetMethod
|
||||
} else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
|
||||
n.typ = &itype{cat: valueT, rtype: field.Type}
|
||||
n.typ = valueTOf(field.Type)
|
||||
n.val = field.Index
|
||||
n.gen = getPtrIndexSeq
|
||||
} else {
|
||||
@@ -1665,7 +1686,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
}
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
n.val = append([]int{m.Index}, lind...)
|
||||
n.typ = &itype{cat: valueT, rtype: m.Type, recv: n.child[0].typ, isBinMethod: true}
|
||||
n.typ = valueTOf(m.Type, isBinMethod(), withRecv(n.child[0].typ))
|
||||
} else {
|
||||
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
|
||||
}
|
||||
@@ -1734,7 +1755,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
case n.child[0].isType(sc):
|
||||
// pointer type expression
|
||||
n.gen = nop
|
||||
n.typ = &itype{cat: ptrT, val: n.child[0].typ}
|
||||
n.typ = ptrOf(n.child[0].typ)
|
||||
default:
|
||||
// dereference expression
|
||||
wireChild(n)
|
||||
@@ -1745,7 +1766,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
}
|
||||
|
||||
if c0 := n.child[0]; c0.typ.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: c0.typ.rtype.Elem()}
|
||||
n.typ = valueTOf(c0.typ.rtype.Elem())
|
||||
} else {
|
||||
n.typ = c0.typ.val
|
||||
}
|
||||
@@ -1879,7 +1900,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if n.anc.action != aAssignX {
|
||||
if c0.typ.cat == valueT && isFunc(c1.typ) {
|
||||
// Avoid special wrapping of interfaces and func types.
|
||||
n.typ = &itype{cat: valueT, rtype: c1.typ.TypeOf()}
|
||||
n.typ = valueTOf(c1.typ.TypeOf())
|
||||
} else {
|
||||
n.typ = c1.typ
|
||||
}
|
||||
@@ -1911,7 +1932,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if n.action == aRecv {
|
||||
// Channel receive operation: set type to the channel data type
|
||||
if n.typ.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: n.typ.rtype.Elem()}
|
||||
n.typ = valueTOf(n.typ.rtype.Elem())
|
||||
} else {
|
||||
n.typ = n.typ.val
|
||||
}
|
||||
@@ -1995,18 +2016,23 @@ func compDefineX(sc *scope, n *node) error {
|
||||
if funtype.cat == valueT {
|
||||
// Handle functions imported from runtime.
|
||||
for i := 0; i < funtype.rtype.NumOut(); i++ {
|
||||
types = append(types, &itype{cat: valueT, rtype: funtype.rtype.Out(i)})
|
||||
types = append(types, valueTOf(funtype.rtype.Out(i)))
|
||||
}
|
||||
} else {
|
||||
types = funtype.ret
|
||||
}
|
||||
if n.child[l-1].isType(sc) {
|
||||
if n.anc.kind == varDecl && n.child[l-1].isType(sc) {
|
||||
l--
|
||||
}
|
||||
if len(types) != l {
|
||||
return n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, src.child[0].name(), len(types))
|
||||
}
|
||||
n.gen = nop
|
||||
if isBinCall(src, sc) {
|
||||
n.gen = nop
|
||||
} else {
|
||||
// TODO (marc): skip if no conversion or wrapping is needed.
|
||||
n.gen = assignFromCall
|
||||
}
|
||||
|
||||
case indexExpr:
|
||||
types = append(types, src.typ, sc.getType("bool"))
|
||||
@@ -2405,25 +2431,11 @@ func isField(n *node) bool {
|
||||
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
|
||||
}
|
||||
|
||||
func isRecursiveField(n *node) bool {
|
||||
if !isField(n) {
|
||||
return false
|
||||
}
|
||||
t := n.typ
|
||||
for t != nil {
|
||||
if t.recursive {
|
||||
return true
|
||||
}
|
||||
t = t.val
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isInConstOrTypeDecl(n *node) bool {
|
||||
anc := n.anc
|
||||
for anc != nil {
|
||||
switch anc.kind {
|
||||
case constDecl, typeDecl:
|
||||
case constDecl, typeDecl, arrayType, chanType:
|
||||
return true
|
||||
case varDecl, funcDecl:
|
||||
return false
|
||||
@@ -2469,8 +2481,19 @@ func isCall(n *node) bool {
|
||||
return n.action == aCall || n.action == aCallSlice
|
||||
}
|
||||
|
||||
func isBinCall(n *node) bool {
|
||||
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
|
||||
func isBinCall(n *node, sc *scope) bool {
|
||||
if !isCall(n) || len(n.child) == 0 {
|
||||
return false
|
||||
}
|
||||
c0 := n.child[0]
|
||||
if c0.typ == nil {
|
||||
// If called early in parsing, child type may not be known yet.
|
||||
c0.typ, _ = nodeType(n.interp, sc, c0)
|
||||
if c0.typ == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return c0.typ.cat == valueT && c0.typ.rtype.Kind() == reflect.Func
|
||||
}
|
||||
|
||||
func isOffsetof(n *node) bool {
|
||||
@@ -2576,7 +2599,7 @@ func gotoLabel(s *symbol) {
|
||||
func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerator) {
|
||||
switch typ.cat {
|
||||
case aliasT, ptrT:
|
||||
gen = compositeGenerator(n, n.typ.val, rtyp)
|
||||
gen = compositeGenerator(n, typ.val, rtyp)
|
||||
case arrayT, sliceT:
|
||||
gen = arrayLit
|
||||
case mapT:
|
||||
@@ -2630,23 +2653,54 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
|
||||
// arrayTypeLen returns the node's array length. If the expression is an
|
||||
// array variable it is determined from the value's type, otherwise it is
|
||||
// computed from the source definition.
|
||||
func arrayTypeLen(n *node) int {
|
||||
func arrayTypeLen(n *node, sc *scope) (int, error) {
|
||||
if n.typ != nil && n.typ.cat == arrayT {
|
||||
return n.typ.length
|
||||
return n.typ.length, nil
|
||||
}
|
||||
max := -1
|
||||
for i, c := range n.child[1:] {
|
||||
r := i
|
||||
if c.kind == keyValueExpr {
|
||||
if v := c.child[0].rval; v.IsValid() {
|
||||
r = int(c.child[0].rval.Int())
|
||||
for _, c := range n.child[1:] {
|
||||
var r int
|
||||
|
||||
if c.kind != keyValueExpr {
|
||||
r = max + 1
|
||||
max = r
|
||||
continue
|
||||
}
|
||||
|
||||
c0 := c.child[0]
|
||||
v := c0.rval
|
||||
if v.IsValid() {
|
||||
r = int(v.Int())
|
||||
} else {
|
||||
// Resolve array key value as a constant.
|
||||
if c0.kind == identExpr {
|
||||
// Key is defined by a symbol which must be a constant integer.
|
||||
sym, _, ok := sc.lookup(c0.ident)
|
||||
if !ok {
|
||||
return 0, c0.cfgErrorf("undefined: %s", c0.ident)
|
||||
}
|
||||
if sym.kind != constSym {
|
||||
return 0, c0.cfgErrorf("non-constant array bound %q", c0.ident)
|
||||
}
|
||||
r = int(vInt(sym.rval))
|
||||
} else {
|
||||
// Key is defined by a numeric constant expression.
|
||||
if _, err := c0.interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cv, ok := c0.rval.Interface().(constant.Value)
|
||||
if !ok {
|
||||
return 0, c0.cfgErrorf("non-constant expression")
|
||||
}
|
||||
r = constToInt(cv)
|
||||
}
|
||||
}
|
||||
|
||||
if r > max {
|
||||
max = r
|
||||
}
|
||||
}
|
||||
return max + 1
|
||||
return max + 1, nil
|
||||
}
|
||||
|
||||
// isValueUntyped returns true if value is untyped.
|
||||
|
||||
84
interp/compile_test.go
Normal file
84
interp/compile_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"testing"
|
||||
|
||||
"github.com/traefik/yaegi/stdlib"
|
||||
)
|
||||
|
||||
func TestCompileAST(t *testing.T) {
|
||||
file, err := parser.ParseFile(token.NewFileSet(), "_.go", `
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Foo struct{}
|
||||
|
||||
var foo Foo
|
||||
const bar = "asdf"
|
||||
|
||||
func main() {
|
||||
fmt.Println(1)
|
||||
}
|
||||
`, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(file.Imports) != 1 || len(file.Decls) != 5 {
|
||||
panic("wrong number of imports or decls")
|
||||
}
|
||||
|
||||
dType := file.Decls[1].(*ast.GenDecl)
|
||||
dVar := file.Decls[2].(*ast.GenDecl)
|
||||
dConst := file.Decls[3].(*ast.GenDecl)
|
||||
dFunc := file.Decls[4].(*ast.FuncDecl)
|
||||
|
||||
if dType.Tok != token.TYPE {
|
||||
panic("decl[1] is not a type")
|
||||
}
|
||||
if dVar.Tok != token.VAR {
|
||||
panic("decl[2] is not a var")
|
||||
}
|
||||
if dConst.Tok != token.CONST {
|
||||
panic("decl[3] is not a const")
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
node ast.Node
|
||||
skip string
|
||||
}{
|
||||
{desc: "file", node: file},
|
||||
{desc: "import", node: file.Imports[0]},
|
||||
{desc: "type", node: dType},
|
||||
{desc: "var", node: dVar, skip: "not supported"},
|
||||
{desc: "const", node: dConst},
|
||||
{desc: "func", node: dFunc},
|
||||
{desc: "block", node: dFunc.Body},
|
||||
{desc: "expr", node: dFunc.Body.List[0]},
|
||||
}
|
||||
|
||||
i := New(Options{})
|
||||
_ = i.Use(stdlib.Symbols)
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
if c.skip != "" {
|
||||
t.Skip(c.skip)
|
||||
}
|
||||
|
||||
i := i
|
||||
if _, ok := c.node.(*ast.File); ok {
|
||||
i = New(Options{})
|
||||
_ = i.Use(stdlib.Symbols)
|
||||
}
|
||||
_, err := i.CompileAST(c.node)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to compile %s: %v", c.desc, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
730
interp/debugger.go
Normal file
730
interp/debugger.go
Normal file
@@ -0,0 +1,730 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotLive indicates that the specified ID does not refer to a (live) Go
|
||||
// routine.
|
||||
ErrNotLive = errors.New("not live")
|
||||
|
||||
// ErrRunning indicates that the specified Go routine is running.
|
||||
ErrRunning = errors.New("running")
|
||||
|
||||
// ErrNotRunning indicates that the specified Go routine is running.
|
||||
ErrNotRunning = errors.New("not running")
|
||||
)
|
||||
|
||||
var rNodeType = reflect.TypeOf((*node)(nil)).Elem()
|
||||
|
||||
// A Debugger can be used to debug a Yaegi program.
|
||||
type Debugger struct {
|
||||
interp *Interpreter
|
||||
events func(*DebugEvent)
|
||||
context context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
gWait *sync.WaitGroup
|
||||
gLock *sync.Mutex
|
||||
gID int
|
||||
gLive map[int]*debugRoutine
|
||||
|
||||
result reflect.Value
|
||||
err error
|
||||
}
|
||||
|
||||
// go routine debug state.
|
||||
type debugRoutine struct {
|
||||
id int
|
||||
|
||||
mode DebugEventReason
|
||||
running bool
|
||||
resume chan struct{}
|
||||
|
||||
fDepth int
|
||||
fStep int
|
||||
}
|
||||
|
||||
// node debug state.
|
||||
type nodeDebugData struct {
|
||||
program *Program
|
||||
breakOnLine bool
|
||||
breakOnCall bool
|
||||
}
|
||||
|
||||
// frame debug state.
|
||||
type frameDebugData struct {
|
||||
g *debugRoutine
|
||||
node *node
|
||||
name string
|
||||
kind frameKind
|
||||
scope *scope
|
||||
}
|
||||
|
||||
// frame kind.
|
||||
type frameKind int
|
||||
|
||||
const (
|
||||
// interpreter root frame.
|
||||
frameRoot frameKind = iota + 1
|
||||
|
||||
// function call frame.
|
||||
frameCall
|
||||
|
||||
// closure capture frame.
|
||||
frameClosure
|
||||
)
|
||||
|
||||
// DebugOptions are the debugger options.
|
||||
type DebugOptions struct {
|
||||
// If true, Go routine IDs start at 1 instead of 0.
|
||||
GoRoutineStartAt1 bool
|
||||
}
|
||||
|
||||
// A DebugEvent is an event generated by a debugger.
|
||||
type DebugEvent struct {
|
||||
debugger *Debugger
|
||||
reason DebugEventReason
|
||||
frame *frame
|
||||
}
|
||||
|
||||
// DebugFrame provides access to stack frame information while debugging a
|
||||
// program.
|
||||
type DebugFrame struct {
|
||||
event *DebugEvent
|
||||
frames []*frame
|
||||
}
|
||||
|
||||
// DebugFrameScope provides access to scoped variables while debugging a
|
||||
// program.
|
||||
type DebugFrameScope struct {
|
||||
frame *frame
|
||||
}
|
||||
|
||||
// DebugVariable is the name and value of a variable from a debug session.
|
||||
type DebugVariable struct {
|
||||
Name string
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
// DebugGoRoutine provides access to information about a Go routine while
|
||||
// debugging a program.
|
||||
type DebugGoRoutine struct {
|
||||
id int
|
||||
}
|
||||
|
||||
// Breakpoint is the result of attempting to set a breakpoint.
|
||||
type Breakpoint struct {
|
||||
// Valid indicates whether the breakpoint was successfully set.
|
||||
Valid bool
|
||||
|
||||
// Position indicates the source position of the breakpoint.
|
||||
Position token.Position
|
||||
}
|
||||
|
||||
// DebugEventReason is the reason a debug event occurred.
|
||||
type DebugEventReason int
|
||||
|
||||
const (
|
||||
// continue execution normally.
|
||||
debugRun DebugEventReason = iota
|
||||
|
||||
// DebugPause is emitted when a pause request is completed. Can be used with
|
||||
// Interrupt to request a pause.
|
||||
DebugPause
|
||||
|
||||
// DebugBreak is emitted when a debug target hits a breakpoint.
|
||||
DebugBreak
|
||||
|
||||
// DebugEntry is emitted when a debug target starts executing. Can be used
|
||||
// with Step to produce a corresponding event when execution starts.
|
||||
DebugEntry
|
||||
|
||||
// DebugStepInto is emitted when a stepInto request is completed. Can be
|
||||
// used with Step or Interrupt to request a stepInto.
|
||||
DebugStepInto
|
||||
|
||||
// DebugStepOver is emitted when a stepOver request is completed. Can be
|
||||
// used with Step or Interrupt to request a stepOver.
|
||||
DebugStepOver
|
||||
|
||||
// DebugStepOut is emitted when a stepOut request is completed. Can be used
|
||||
// with Step or Interrupt to request a stepOut.
|
||||
DebugStepOut
|
||||
|
||||
// DebugTerminate is emitted when a debug target terminates. Can be used
|
||||
// with Interrupt to attempt to terminate the program.
|
||||
DebugTerminate
|
||||
|
||||
// DebugEnterGoRoutine is emitted when a Go routine is entered.
|
||||
DebugEnterGoRoutine
|
||||
|
||||
// DebugExitGoRoutine is emitted when a Go routine is exited.
|
||||
DebugExitGoRoutine
|
||||
)
|
||||
|
||||
// Debug initializes a debugger for the given program.
|
||||
//
|
||||
// The program will not start running until Step or Continue has been called. If
|
||||
// Step is called with DebugEntry, an entry event will be generated before the
|
||||
// first statement is executed. Otherwise, the debugger will behave as usual.
|
||||
func (interp *Interpreter) Debug(ctx context.Context, prog *Program, events func(*DebugEvent), opts *DebugOptions) *Debugger {
|
||||
dbg := new(Debugger)
|
||||
dbg.interp = interp
|
||||
dbg.events = events
|
||||
dbg.context, dbg.cancel = context.WithCancel(ctx)
|
||||
dbg.gWait = new(sync.WaitGroup)
|
||||
dbg.gLock = new(sync.Mutex)
|
||||
dbg.gLive = make(map[int]*debugRoutine, 1)
|
||||
|
||||
if opts == nil {
|
||||
opts = new(DebugOptions)
|
||||
}
|
||||
if opts.GoRoutineStartAt1 {
|
||||
dbg.gID = 1
|
||||
}
|
||||
|
||||
mainG := dbg.enterGoRoutine()
|
||||
mainG.mode = DebugEntry
|
||||
|
||||
interp.debugger = dbg
|
||||
interp.frame.debug = &frameDebugData{kind: frameRoot, g: mainG}
|
||||
|
||||
prog.root.Walk(func(n *node) bool {
|
||||
n.setProgram(prog)
|
||||
return true
|
||||
}, nil)
|
||||
|
||||
go func() {
|
||||
defer func() { interp.debugger = nil }()
|
||||
defer events(&DebugEvent{reason: DebugTerminate})
|
||||
defer dbg.cancel()
|
||||
|
||||
<-mainG.resume
|
||||
dbg.events(&DebugEvent{dbg, DebugEnterGoRoutine, interp.frame})
|
||||
dbg.result, dbg.err = interp.ExecuteWithContext(ctx, prog)
|
||||
dbg.exitGoRoutine(mainG)
|
||||
dbg.events(&DebugEvent{dbg, DebugExitGoRoutine, interp.frame})
|
||||
dbg.gWait.Wait()
|
||||
}()
|
||||
|
||||
return dbg
|
||||
}
|
||||
|
||||
// Wait blocks until all Go routines launched by the program have terminated.
|
||||
// Wait returns the results of `(*Interpreter).Execute`.
|
||||
func (dbg *Debugger) Wait() (reflect.Value, error) {
|
||||
<-dbg.context.Done()
|
||||
return dbg.result, dbg.err
|
||||
}
|
||||
|
||||
// mark entry into a go routine.
|
||||
func (dbg *Debugger) enterGoRoutine() *debugRoutine {
|
||||
g := new(debugRoutine)
|
||||
g.resume = make(chan struct{})
|
||||
|
||||
dbg.gWait.Add(1)
|
||||
|
||||
dbg.gLock.Lock()
|
||||
g.id = dbg.gID
|
||||
dbg.gID++
|
||||
dbg.gLive[g.id] = g
|
||||
dbg.gLock.Unlock()
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
// mark exit from a go routine.
|
||||
func (dbg *Debugger) exitGoRoutine(g *debugRoutine) {
|
||||
dbg.gLock.Lock()
|
||||
delete(dbg.gLive, g.id)
|
||||
dbg.gLock.Unlock()
|
||||
|
||||
dbg.gWait.Done()
|
||||
}
|
||||
|
||||
// get the state for a given go routine, if it's live.
|
||||
func (dbg *Debugger) getGoRoutine(id int) (*debugRoutine, bool) {
|
||||
dbg.gLock.Lock()
|
||||
g, ok := dbg.gLive[id]
|
||||
dbg.gLock.Unlock()
|
||||
return g, ok
|
||||
}
|
||||
|
||||
// mark entry into a function call.
|
||||
func (dbg *Debugger) enterCall(nFunc, nCall *node, f *frame) {
|
||||
if f.debug != nil {
|
||||
f.debug.g.fDepth++
|
||||
return
|
||||
}
|
||||
|
||||
f.debug = new(frameDebugData)
|
||||
f.debug.g = f.anc.debug.g
|
||||
f.debug.scope = nFunc.scope
|
||||
|
||||
switch nFunc.kind {
|
||||
case funcLit:
|
||||
f.debug.kind = frameCall
|
||||
if nFunc.frame != nil {
|
||||
nFunc.frame.debug.kind = frameClosure
|
||||
nFunc.frame.debug.node = nFunc
|
||||
}
|
||||
|
||||
case funcDecl:
|
||||
f.debug.kind = frameCall
|
||||
f.debug.name = nFunc.child[1].ident
|
||||
}
|
||||
|
||||
if nCall != nil && nCall.anc.kind == goStmt {
|
||||
f.debug.g = dbg.enterGoRoutine()
|
||||
dbg.events(&DebugEvent{dbg, DebugEnterGoRoutine, f})
|
||||
}
|
||||
|
||||
f.debug.g.fDepth++
|
||||
}
|
||||
|
||||
// mark exit from a function call.
|
||||
func (dbg *Debugger) exitCall(nFunc, nCall *node, f *frame) {
|
||||
_ = nFunc // ignore unused, so exitCall can have the same signature as enterCall
|
||||
|
||||
f.debug.g.fDepth--
|
||||
|
||||
if nCall != nil && nCall.anc.kind == goStmt {
|
||||
dbg.exitGoRoutine(f.debug.g)
|
||||
dbg.events(&DebugEvent{dbg, DebugExitGoRoutine, f})
|
||||
}
|
||||
}
|
||||
|
||||
// called by the interpreter prior to executing the node.
|
||||
func (dbg *Debugger) exec(n *node, f *frame) (stop bool) {
|
||||
f.debug.node = n
|
||||
|
||||
if n != nil && n.pos == token.NoPos {
|
||||
return false
|
||||
}
|
||||
|
||||
g := f.debug.g
|
||||
defer func() { g.running = true }()
|
||||
|
||||
e := &DebugEvent{dbg, g.mode, f}
|
||||
switch {
|
||||
case g.mode == DebugTerminate:
|
||||
dbg.cancel()
|
||||
return true
|
||||
|
||||
case n.shouldBreak():
|
||||
e.reason = DebugBreak
|
||||
|
||||
case g.mode == debugRun:
|
||||
return false
|
||||
|
||||
case g.mode == DebugStepOut:
|
||||
if g.fDepth >= g.fStep {
|
||||
return false
|
||||
}
|
||||
|
||||
case g.mode == DebugStepOver:
|
||||
if g.fDepth > g.fStep {
|
||||
return false
|
||||
}
|
||||
}
|
||||
dbg.events(e)
|
||||
|
||||
g.running = false
|
||||
select {
|
||||
case <-g.resume:
|
||||
return false
|
||||
case <-dbg.context.Done():
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Continue continues execution of the specified Go routine. Continue returns
|
||||
// ErrNotLive if there is no Go routine with the corresponding ID, or if it is not
|
||||
// live.
|
||||
func (dbg *Debugger) Continue(id int) error {
|
||||
g, ok := dbg.getGoRoutine(id)
|
||||
if !ok {
|
||||
return ErrNotLive
|
||||
}
|
||||
|
||||
g.mode = debugRun
|
||||
g.resume <- struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// update the exec mode of this routine.
|
||||
func (g *debugRoutine) setMode(reason DebugEventReason) {
|
||||
if g.mode == DebugTerminate {
|
||||
return
|
||||
}
|
||||
|
||||
if g.mode == DebugEntry && reason == DebugEntry {
|
||||
return
|
||||
}
|
||||
|
||||
switch reason {
|
||||
case DebugStepInto, DebugStepOver, DebugStepOut:
|
||||
g.mode, g.fStep = reason, g.fDepth
|
||||
default:
|
||||
g.mode = DebugPause
|
||||
}
|
||||
}
|
||||
|
||||
// Step issues a stepInto, stepOver, or stepOut request to a stopped Go routine.
|
||||
// Step returns ErrRunning if the Go routine is running. Step returns ErrNotLive
|
||||
// if there is no Go routine with the corresponding ID, or if it is not live.
|
||||
func (dbg *Debugger) Step(id int, reason DebugEventReason) error {
|
||||
g, ok := dbg.getGoRoutine(id)
|
||||
if !ok {
|
||||
return ErrNotLive
|
||||
}
|
||||
|
||||
if g.running {
|
||||
return ErrRunning
|
||||
}
|
||||
|
||||
g.setMode(reason)
|
||||
g.resume <- struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Interrupt issues a stepInto, stepOver, or stepOut request to a running Go
|
||||
// routine. Interrupt returns ErrRunning if the Go routine is running. Interrupt
|
||||
// returns ErrNotLive if there is no Go routine with the corresponding ID, or if
|
||||
// it is not live.
|
||||
func (dbg *Debugger) Interrupt(id int, reason DebugEventReason) bool {
|
||||
g, ok := dbg.getGoRoutine(id)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
g.setMode(reason)
|
||||
return true
|
||||
}
|
||||
|
||||
// Terminate attempts to terminate the program.
|
||||
func (dbg *Debugger) Terminate() {
|
||||
dbg.gLock.Lock()
|
||||
g := dbg.gLive
|
||||
dbg.gLive = nil
|
||||
dbg.gLock.Unlock()
|
||||
|
||||
for _, g := range g {
|
||||
g.mode = DebugTerminate
|
||||
close(g.resume)
|
||||
}
|
||||
}
|
||||
|
||||
// BreakpointTarget is the target of a request to set breakpoints.
|
||||
type BreakpointTarget func(*Debugger, func(*node))
|
||||
|
||||
// PathBreakpointTarget is used to set breapoints on compiled code by path. This
|
||||
// can be used to set breakpoints on code compiled with EvalPath, or source
|
||||
// packages loaded by Yaegi.
|
||||
func PathBreakpointTarget(path string) BreakpointTarget {
|
||||
return func(dbg *Debugger, cb func(*node)) {
|
||||
for _, r := range dbg.interp.roots {
|
||||
f := dbg.interp.fset.File(r.pos)
|
||||
if f != nil && f.Name() == path {
|
||||
cb(r)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ProgramBreakpointTarget is used to set breakpoints on a Program.
|
||||
func ProgramBreakpointTarget(prog *Program) BreakpointTarget {
|
||||
return func(_ *Debugger, cb func(*node)) {
|
||||
cb(prog.root)
|
||||
}
|
||||
}
|
||||
|
||||
// AllBreakpointTarget is used to set breakpoints on all compiled code. Do not
|
||||
// use with LineBreakpoint.
|
||||
func AllBreakpointTarget() BreakpointTarget {
|
||||
return func(dbg *Debugger, cb func(*node)) {
|
||||
for _, r := range dbg.interp.roots {
|
||||
cb(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type breakpointSetup struct {
|
||||
roots []*node
|
||||
lines map[int]int
|
||||
funcs map[string]int
|
||||
}
|
||||
|
||||
// BreakpointRequest is a request to set a breakpoint.
|
||||
type BreakpointRequest func(*breakpointSetup, int)
|
||||
|
||||
// LineBreakpoint requests a breakpoint on the given line.
|
||||
func LineBreakpoint(line int) BreakpointRequest {
|
||||
return func(b *breakpointSetup, i int) {
|
||||
b.lines[line] = i
|
||||
}
|
||||
}
|
||||
|
||||
// FunctionBreakpoint requests a breakpoint on the named function.
|
||||
func FunctionBreakpoint(name string) BreakpointRequest {
|
||||
return func(b *breakpointSetup, i int) {
|
||||
b.funcs[name] = i
|
||||
}
|
||||
}
|
||||
|
||||
// SetBreakpoints sets breakpoints for the given target. The returned array has
|
||||
// an entry for every request, in order. If a given breakpoint request cannot be
|
||||
// satisfied, the corresponding entry will be marked invalid. If the target
|
||||
// cannot be found, all entries will be marked invalid.
|
||||
func (dbg *Debugger) SetBreakpoints(target BreakpointTarget, requests ...BreakpointRequest) []Breakpoint {
|
||||
// start with all breakpoints unverified
|
||||
results := make([]Breakpoint, len(requests))
|
||||
|
||||
// prepare all the requests
|
||||
setup := new(breakpointSetup)
|
||||
target(dbg, func(root *node) {
|
||||
setup.roots = append(setup.roots, root)
|
||||
setup.lines = make(map[int]int, len(requests))
|
||||
setup.funcs = make(map[string]int, len(requests))
|
||||
for i, rq := range requests {
|
||||
rq(setup, i)
|
||||
}
|
||||
})
|
||||
|
||||
// find breakpoints
|
||||
for _, root := range setup.roots {
|
||||
root.Walk(func(n *node) bool {
|
||||
// function breakpoints
|
||||
if len(setup.funcs) > 0 && n.kind == funcDecl {
|
||||
// reset stale breakpoints
|
||||
n.start.setBreakOnCall(false)
|
||||
|
||||
if i, ok := setup.funcs[n.child[1].ident]; ok && !results[i].Valid {
|
||||
results[i].Valid = true
|
||||
results[i].Position = dbg.interp.fset.Position(n.start.pos)
|
||||
n.start.setBreakOnCall(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// line breakpoints
|
||||
if len(setup.lines) > 0 && n.pos.IsValid() && n.action != aNop && getExec(n) != nil {
|
||||
// reset stale breakpoints
|
||||
n.setBreakOnLine(false)
|
||||
|
||||
pos := dbg.interp.fset.Position(n.pos)
|
||||
if i, ok := setup.lines[pos.Line]; ok && !results[i].Valid {
|
||||
results[i].Valid = true
|
||||
results[i].Position = pos
|
||||
n.setBreakOnLine(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}, nil)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// GoRoutines returns an array of live Go routines.
|
||||
func (dbg *Debugger) GoRoutines() []*DebugGoRoutine {
|
||||
dbg.gLock.Lock()
|
||||
r := make([]*DebugGoRoutine, 0, len(dbg.gLive))
|
||||
for id := range dbg.gLive {
|
||||
r = append(r, &DebugGoRoutine{id})
|
||||
}
|
||||
dbg.gLock.Unlock()
|
||||
sort.Slice(r, func(i, j int) bool { return r[i].id < r[j].id })
|
||||
return r
|
||||
}
|
||||
|
||||
// ID returns the ID of the Go routine.
|
||||
func (r *DebugGoRoutine) ID() int { return r.id }
|
||||
|
||||
// Name returns "Goroutine {ID}".
|
||||
func (r *DebugGoRoutine) Name() string { return fmt.Sprintf("Goroutine %d", r.id) }
|
||||
|
||||
// GoRoutine returns the ID of the Go routine that generated the event.
|
||||
func (evt *DebugEvent) GoRoutine() int {
|
||||
if evt.frame.debug == nil {
|
||||
return 0
|
||||
}
|
||||
return evt.frame.debug.g.id
|
||||
}
|
||||
|
||||
// Reason returns the reason for the event.
|
||||
func (evt *DebugEvent) Reason() DebugEventReason {
|
||||
return evt.reason
|
||||
}
|
||||
|
||||
// Walk the stack trace frames. The root frame is included if and only if it is
|
||||
// the only frame. Closure frames are rolled up into the following call frame.
|
||||
func (evt *DebugEvent) walkFrames(fn func([]*frame) bool) {
|
||||
if evt.frame == evt.frame.root {
|
||||
fn([]*frame{evt.frame})
|
||||
return
|
||||
}
|
||||
|
||||
var g *debugRoutine
|
||||
if evt.frame.debug != nil {
|
||||
g = evt.frame.debug.g
|
||||
}
|
||||
|
||||
var frames []*frame
|
||||
for f := evt.frame; f != nil && f != f.root && (f.debug == nil || f.debug.g == g); f = f.anc {
|
||||
if f.debug == nil || f.debug.kind != frameCall {
|
||||
frames = append(frames, f)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(frames) > 0 {
|
||||
if !fn(frames) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
frames = frames[:0]
|
||||
frames = append(frames, f)
|
||||
}
|
||||
|
||||
if len(frames) > 0 {
|
||||
fn(frames)
|
||||
}
|
||||
}
|
||||
|
||||
// FrameDepth returns the number of call frames in the stack trace.
|
||||
func (evt *DebugEvent) FrameDepth() int {
|
||||
if evt.frame == evt.frame.root {
|
||||
return 1
|
||||
}
|
||||
|
||||
var n int
|
||||
evt.walkFrames(func([]*frame) bool { n++; return true })
|
||||
return n
|
||||
}
|
||||
|
||||
// Frames returns the call frames in the range [start, end).
|
||||
func (evt *DebugEvent) Frames(start, end int) []*DebugFrame {
|
||||
count := end - start
|
||||
if count < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
frames := []*DebugFrame{}
|
||||
evt.walkFrames(func(f []*frame) bool {
|
||||
df := &DebugFrame{evt, make([]*frame, len(f))}
|
||||
copy(df.frames, f)
|
||||
frames = append(frames, df)
|
||||
return len(frames) < count
|
||||
})
|
||||
return frames
|
||||
}
|
||||
|
||||
// Name returns the name of the stack frame. For function calls to named
|
||||
// functions, this is the function name.
|
||||
func (f *DebugFrame) Name() string {
|
||||
d := f.frames[0].debug
|
||||
if d == nil {
|
||||
return "<unknown>"
|
||||
}
|
||||
switch d.kind {
|
||||
case frameRoot:
|
||||
return "<init>"
|
||||
case frameClosure:
|
||||
return "<closure>"
|
||||
case frameCall:
|
||||
if d.name == "" {
|
||||
return "<anonymous>"
|
||||
}
|
||||
return d.name
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
// Position returns the current position of the frame. This is effectively the
|
||||
// program counter/link register. May return `Position{}`.
|
||||
func (f *DebugFrame) Position() token.Position {
|
||||
d := f.frames[0].debug
|
||||
if d == nil || d.node == nil {
|
||||
return token.Position{}
|
||||
}
|
||||
return f.event.debugger.interp.fset.Position(d.node.pos)
|
||||
}
|
||||
|
||||
// Program returns the program associated with the current position of the
|
||||
// frame. May return nil.
|
||||
func (f *DebugFrame) Program() *Program {
|
||||
d := f.frames[0].debug
|
||||
if d == nil || d.node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return d.node.debug.program
|
||||
}
|
||||
|
||||
// Scopes returns the variable scopes of the frame.
|
||||
func (f *DebugFrame) Scopes() []*DebugFrameScope {
|
||||
s := make([]*DebugFrameScope, len(f.frames))
|
||||
for i, f := range f.frames {
|
||||
s[i] = &DebugFrameScope{f}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// IsClosure returns true if this is the capture scope of a closure.
|
||||
func (f *DebugFrameScope) IsClosure() bool {
|
||||
return f.frame.debug != nil && f.frame.debug.kind == frameClosure
|
||||
}
|
||||
|
||||
// Variables returns the names and values of the variables of the scope.
|
||||
func (f *DebugFrameScope) Variables() []*DebugVariable {
|
||||
d := f.frame.debug
|
||||
if d == nil || d.scope == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
index := map[int]string{}
|
||||
scanScope(d.scope, index)
|
||||
|
||||
m := make([]*DebugVariable, 0, len(f.frame.data))
|
||||
for i, v := range f.frame.data {
|
||||
if typ := v.Type(); typ.AssignableTo(rNodeType) || typ.Kind() == reflect.Ptr && typ.Elem().AssignableTo(rNodeType) {
|
||||
continue
|
||||
}
|
||||
name, ok := index[i]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
m = append(m, &DebugVariable{name, v})
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func scanScope(sc *scope, index map[int]string) {
|
||||
for name, sym := range sc.sym {
|
||||
if _, ok := index[sym.index]; ok {
|
||||
continue
|
||||
}
|
||||
index[sym.index] = name
|
||||
}
|
||||
|
||||
for _, ch := range sc.child {
|
||||
if ch.def != sc.def {
|
||||
continue
|
||||
}
|
||||
scanScope(ch, index)
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package interp
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -69,7 +68,7 @@ func (nopCloser) Close() error { return nil }
|
||||
// dotWriter returns an output stream to a dot(1) co-process where to write data in .dot format.
|
||||
func dotWriter(dotCmd string) io.WriteCloser {
|
||||
if dotCmd == "" {
|
||||
return nopCloser{ioutil.Discard}
|
||||
return nopCloser{io.Discard}
|
||||
}
|
||||
fields := strings.Fields(dotCmd)
|
||||
cmd := exec.Command(fields[0], fields[1:]...)
|
||||
|
||||
116
interp/gta.go
116
interp/gta.go
@@ -3,7 +3,6 @@ package interp
|
||||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// gta performs a global types analysis on the AST, registering types,
|
||||
@@ -11,8 +10,8 @@ import (
|
||||
// All function bodies are skipped. GTA is necessary to handle out of
|
||||
// order declarations and multiple source files packages.
|
||||
// rpath is the relative path to the directory containing the source for the package.
|
||||
func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(importPath)
|
||||
func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(importPath, pkgName)
|
||||
var err error
|
||||
var revisit []*node
|
||||
|
||||
@@ -26,7 +25,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
case constDecl:
|
||||
// Early parse of constDecl subtree, to compute all constant
|
||||
// values which may be used in further declarations.
|
||||
if _, err = interp.cfg(n, importPath); err != nil {
|
||||
if _, err = interp.cfg(n, sc, importPath, pkgName); err != nil {
|
||||
// No error processing here, to allow recovery in subtree nodes.
|
||||
// TODO(marc): check for a non recoverable error and return it for better diagnostic.
|
||||
err = nil
|
||||
@@ -38,10 +37,17 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
}
|
||||
|
||||
case defineStmt:
|
||||
var atyp *itype
|
||||
var (
|
||||
atyp *itype
|
||||
err2 error
|
||||
)
|
||||
if n.nleft+n.nright < len(n.child) {
|
||||
// Type is declared explicitly in the assign expression.
|
||||
if atyp, err = nodeType(interp, sc, n.child[n.nleft]); err != nil {
|
||||
if atyp, err2 = nodeType(interp, sc, n.child[n.nleft]); err2 != nil {
|
||||
// The type does not exist yet, stash the error and come back
|
||||
// when the type is known.
|
||||
n.meta = err2
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -53,9 +59,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
|
||||
for i := 0; i < n.nleft; i++ {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
val := reflect.ValueOf(sc.iota)
|
||||
val := src.rval
|
||||
if n.anc.kind == constDecl {
|
||||
if _, err2 := interp.cfg(n, importPath); err2 != nil {
|
||||
if _, err2 := interp.cfg(n, sc, importPath, pkgName); err2 != nil {
|
||||
// Constant value can not be computed yet.
|
||||
// Come back when child dependencies are known.
|
||||
revisit = append(revisit, n)
|
||||
@@ -64,7 +70,11 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
}
|
||||
typ := atyp
|
||||
if typ == nil {
|
||||
if typ, err = nodeType(interp, sc, src); err != nil {
|
||||
if typ, err2 = nodeType(interp, sc, src); err2 != nil || typ == nil {
|
||||
// The type does is not known yet, stash the error and come back
|
||||
// when the type is known.
|
||||
n.meta = err2
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
val = src.rval
|
||||
@@ -79,7 +89,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
return false
|
||||
}
|
||||
if typ.isBinMethod {
|
||||
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true, scope: sc}
|
||||
typ = valueTOf(typ.methodCallType(), isBinMethod(), withScope(sc))
|
||||
}
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val, node: n}
|
||||
if n.anc.kind == constDecl {
|
||||
@@ -139,27 +149,28 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
n.ident = ident
|
||||
rcvr := n.child[0].child[0]
|
||||
rtn := rcvr.lastChild()
|
||||
typeName := rtn.ident
|
||||
if typeName == "" {
|
||||
// The receiver is a pointer, retrieve typeName from indirection
|
||||
typeName = rtn.child[0].ident
|
||||
elementType := sc.getType(typeName)
|
||||
if elementType == nil {
|
||||
// Add type if necessary, so method can be registered
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: importPath, incomplete: true, node: rtn.child[0], scope: sc}}
|
||||
elementType = sc.sym[typeName].typ
|
||||
}
|
||||
rcvrtype = &itype{cat: ptrT, val: elementType, incomplete: elementType.incomplete, node: rtn, scope: sc}
|
||||
elementType.method = append(elementType.method, n)
|
||||
} else {
|
||||
rcvrtype = sc.getType(typeName)
|
||||
if rcvrtype == nil {
|
||||
// Add type if necessary, so method can be registered
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: importPath, incomplete: true, node: rtn, scope: sc}}
|
||||
rcvrtype = sc.sym[typeName].typ
|
||||
}
|
||||
typName, typPtr := rtn.ident, false
|
||||
if typName == "" {
|
||||
typName, typPtr = rtn.child[0].ident, true
|
||||
}
|
||||
rcvrtype.method = append(rcvrtype.method, n)
|
||||
sym, _, found := sc.lookup(typName)
|
||||
if !found {
|
||||
n.meta = n.cfgErrorf("undefined: %s", typName)
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
if sym.kind != typeSym || (sym.node != nil && sym.node.kind == typeSpecAssign) {
|
||||
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(sym.typ).id())
|
||||
return false
|
||||
}
|
||||
rcvrtype = sym.typ
|
||||
if typPtr {
|
||||
elementType := sym.typ
|
||||
rcvrtype = ptrOf(elementType, withNode(rtn), withScope(sc))
|
||||
rcvrtype.incomplete = elementType.incomplete
|
||||
elementType.addMethod(n)
|
||||
}
|
||||
rcvrtype.addMethod(n)
|
||||
n.child[0].child[0].lastChild().typ = rcvrtype
|
||||
case ident == "init":
|
||||
// init functions do not get declared as per the Go spec.
|
||||
@@ -200,7 +211,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
if isBinType(v) {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
sc.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ, scope: sc}, rval: v}
|
||||
sc.sym[n] = &symbol{kind: binSym, typ: valueTOf(typ, withScope(sc)), rval: v}
|
||||
}
|
||||
default: // import symbols in package namespace
|
||||
if name == "" {
|
||||
@@ -254,7 +265,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
err = n.cfgErrorf("import %q error: %v", ipath, err)
|
||||
}
|
||||
|
||||
case typeSpec:
|
||||
case typeSpec, typeSpecAssign:
|
||||
typeName := n.child[0].ident
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
@@ -265,13 +276,16 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
|
||||
switch n.child[1].kind {
|
||||
case identExpr, selectorExpr:
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: importPath, field: typ.field, incomplete: typ.incomplete, scope: sc, node: n.child[0]}
|
||||
n.typ = namedOf(typ, pkgName, typeName, withNode(n.child[0]), withScope(sc))
|
||||
n.typ.incomplete = typ.incomplete
|
||||
n.typ.field = typ.field
|
||||
copy(n.typ.method, typ.method)
|
||||
default:
|
||||
n.typ = typ
|
||||
n.typ.name = typeName
|
||||
n.typ.path = importPath
|
||||
n.typ.path = pkgName
|
||||
}
|
||||
n.typ.str = n.typ.path + "." + n.typ.name
|
||||
|
||||
asImportName := filepath.Join(typeName, baseName)
|
||||
if _, exists := sc.sym[asImportName]; exists {
|
||||
@@ -281,15 +295,21 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
}
|
||||
sym, exists := sc.sym[typeName]
|
||||
if !exists {
|
||||
sc.sym[typeName] = &symbol{kind: typeSym}
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
} else {
|
||||
if sym.typ != nil && (len(sym.typ.method) > 0) {
|
||||
if n.kind == typeSpecAssign {
|
||||
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(typ).id())
|
||||
return false
|
||||
}
|
||||
// Type has already been seen as a receiver in a method function
|
||||
n.typ.method = append(n.typ.method, sym.typ.method...)
|
||||
for _, m := range sym.typ.method {
|
||||
n.typ.addMethod(m)
|
||||
}
|
||||
} else {
|
||||
// TODO(mpl): figure out how to detect redeclarations without breaking type aliases.
|
||||
// Allow redeclarations for now.
|
||||
sc.sym[typeName] = &symbol{kind: typeSym}
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
}
|
||||
}
|
||||
sc.sym[typeName].typ = n.typ
|
||||
@@ -307,12 +327,23 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
return revisit, err
|
||||
}
|
||||
|
||||
func baseType(t *itype) *itype {
|
||||
for {
|
||||
switch t.cat {
|
||||
case ptrT, aliasT:
|
||||
t = t.val
|
||||
default:
|
||||
return t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gtaRetry (re)applies gta until all global constants and types are defined.
|
||||
func (interp *Interpreter) gtaRetry(nodes []*node, importPath string) error {
|
||||
func (interp *Interpreter) gtaRetry(nodes []*node, importPath, pkgName string) error {
|
||||
revisit := []*node{}
|
||||
for {
|
||||
for _, n := range nodes {
|
||||
list, err := interp.gta(n, importPath, importPath)
|
||||
list, err := interp.gta(n, importPath, importPath, pkgName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -329,10 +360,15 @@ func (interp *Interpreter) gtaRetry(nodes []*node, importPath string) error {
|
||||
|
||||
if len(revisit) > 0 {
|
||||
n := revisit[0]
|
||||
if n.kind == typeSpec {
|
||||
switch n.kind {
|
||||
case typeSpec, typeSpecAssign:
|
||||
if err := definedType(n.typ); err != nil {
|
||||
return err
|
||||
}
|
||||
case defineStmt, funcDecl:
|
||||
if err, ok := n.meta.(error); ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return n.cfgErrorf("constant definition loop")
|
||||
}
|
||||
|
||||
330
interp/interp.go
330
interp/interp.go
@@ -7,18 +7,18 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"log"
|
||||
"math/bits"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
|
||||
// Interpreter node structure for AST and CFG.
|
||||
type node struct {
|
||||
debug *nodeDebugData // debug info
|
||||
child []*node // child subtrees (AST)
|
||||
anc *node // ancestor (AST)
|
||||
start *node // entry point in subtree (CFG)
|
||||
@@ -45,12 +46,53 @@ type node struct {
|
||||
typ *itype // type of value in frame, or nil
|
||||
recv *receiver // method receiver node for call, or nil
|
||||
types []reflect.Type // frame types, used by function literals only
|
||||
scope *scope // frame scope
|
||||
action action // action
|
||||
exec bltn // generated function to execute
|
||||
gen bltnGenerator // generator function to produce above bltn
|
||||
val interface{} // static generic value (CFG execution)
|
||||
rval reflect.Value // reflection value to let runtime access interpreter (CFG)
|
||||
ident string // set if node is a var or func
|
||||
meta interface{} // meta stores meta information between gta runs, like errors
|
||||
}
|
||||
|
||||
func (n *node) shouldBreak() bool {
|
||||
if n == nil || n.debug == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if n.debug.breakOnLine || n.debug.breakOnCall {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (n *node) setProgram(p *Program) {
|
||||
if n.debug == nil {
|
||||
n.debug = new(nodeDebugData)
|
||||
}
|
||||
n.debug.program = p
|
||||
}
|
||||
|
||||
func (n *node) setBreakOnCall(v bool) {
|
||||
if n.debug == nil {
|
||||
if !v {
|
||||
return
|
||||
}
|
||||
n.debug = new(nodeDebugData)
|
||||
}
|
||||
n.debug.breakOnCall = v
|
||||
}
|
||||
|
||||
func (n *node) setBreakOnLine(v bool) {
|
||||
if n.debug == nil {
|
||||
if !v {
|
||||
return
|
||||
}
|
||||
n.debug = new(nodeDebugData)
|
||||
}
|
||||
n.debug.breakOnLine = v
|
||||
}
|
||||
|
||||
// receiver stores method receiver object access path.
|
||||
@@ -67,6 +109,8 @@ type frame struct {
|
||||
// Located at start of struct to ensure proper aligment.
|
||||
id uint64
|
||||
|
||||
debug *frameDebugData
|
||||
|
||||
root *frame // global space
|
||||
anc *frame // ancestor frame (caller space)
|
||||
data []reflect.Value // values
|
||||
@@ -104,6 +148,7 @@ func (f *frame) clone(fork bool) *frame {
|
||||
recovered: f.recovered,
|
||||
id: f.runid(),
|
||||
done: f.done,
|
||||
debug: f.debug,
|
||||
}
|
||||
if fork {
|
||||
nf.data = make([]reflect.Value, len(f.data))
|
||||
@@ -124,18 +169,22 @@ type imports map[string]map[string]*symbol
|
||||
|
||||
// opt stores interpreter options.
|
||||
type opt struct {
|
||||
astDot bool // display AST graph (debug)
|
||||
cfgDot bool // display CFG graph (debug)
|
||||
// dotCmd is the command to process the dot graph produced when astDot and/or
|
||||
// cfgDot is enabled. It defaults to 'dot -Tdot -o <filename>.dot'.
|
||||
dotCmd string
|
||||
noRun bool // compile, but do not run
|
||||
fastChan bool // disable cancellable chan operations
|
||||
context build.Context // build context: GOPATH, build constraints
|
||||
specialStdio bool // Allows os.Stdin, os.Stdout, os.Stderr to not be file descriptors
|
||||
stdin io.Reader // standard input
|
||||
stdout io.Writer // standard output
|
||||
stderr io.Writer // standard error
|
||||
context build.Context // build context: GOPATH, build constraints
|
||||
stdin io.Reader // standard input
|
||||
stdout io.Writer // standard output
|
||||
stderr io.Writer // standard error
|
||||
args []string // cmdline args
|
||||
env map[string]string // environment of interpreter, entries in form of "key=value"
|
||||
filesystem fs.FS // filesystem containing sources
|
||||
astDot bool // display AST graph (debug)
|
||||
cfgDot bool // display CFG graph (debug)
|
||||
noRun bool // compile, but do not run
|
||||
fastChan bool // disable cancellable chan operations
|
||||
specialStdio bool // allows os.Stdin, os.Stdout, os.Stderr to not be file descriptors
|
||||
unrestricted bool // allow use of non sandboxed symbols
|
||||
}
|
||||
|
||||
// Interpreter contains global resources and state.
|
||||
@@ -166,8 +215,11 @@ type Interpreter struct {
|
||||
srcPkg imports // source packages used in interpreter, indexed by path
|
||||
pkgNames map[string]string // package names, indexed by import path
|
||||
done chan struct{} // for cancellation of channel operations
|
||||
roots []*node
|
||||
|
||||
hooks *hooks // symbol hooks
|
||||
|
||||
debugger *Debugger
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -253,12 +305,27 @@ type Options struct {
|
||||
// They default to os.Stdin, os.Stdout and os.Stderr respectively.
|
||||
Stdin io.Reader
|
||||
Stdout, Stderr io.Writer
|
||||
|
||||
// Cmdline args, defaults to os.Args.
|
||||
Args []string
|
||||
|
||||
// Environment of interpreter. Entries are in the form "key=values".
|
||||
Env []string
|
||||
|
||||
// SourcecodeFilesystem is where the _sourcecode_ is loaded from and does
|
||||
// NOT affect the filesystem of scripts when they run.
|
||||
// It can be any fs.FS compliant filesystem (e.g. embed.FS, or fstest.MapFS for testing)
|
||||
// See example/fs/fs_test.go for an example.
|
||||
SourcecodeFilesystem fs.FS
|
||||
|
||||
// Unrestricted allows to run non sandboxed stdlib symbols such as os/exec and environment
|
||||
Unrestricted bool
|
||||
}
|
||||
|
||||
// New returns a new interpreter.
|
||||
func New(options Options) *Interpreter {
|
||||
i := Interpreter{
|
||||
opt: opt{context: build.Default},
|
||||
opt: opt{context: build.Default, filesystem: &realFS{}, env: map[string]string{}},
|
||||
frame: newFrame(nil, 0, 0),
|
||||
fset: token.NewFileSet(),
|
||||
universe: initUniverse(),
|
||||
@@ -282,6 +349,28 @@ func New(options Options) *Interpreter {
|
||||
i.opt.stderr = os.Stderr
|
||||
}
|
||||
|
||||
if i.opt.args = options.Args; i.opt.args == nil {
|
||||
i.opt.args = os.Args
|
||||
}
|
||||
|
||||
// unrestricted allows to use non sandboxed stdlib symbols and env.
|
||||
if options.Unrestricted {
|
||||
i.opt.unrestricted = true
|
||||
} else {
|
||||
for _, e := range options.Env {
|
||||
a := strings.SplitN(e, "=", 2)
|
||||
if len(a) == 2 {
|
||||
i.opt.env[a[0]] = a[1]
|
||||
} else {
|
||||
i.opt.env[a[0]] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if options.SourcecodeFilesystem != nil {
|
||||
i.opt.filesystem = options.SourcecodeFilesystem
|
||||
}
|
||||
|
||||
i.opt.context.GOPATH = options.GoPath
|
||||
if len(options.BuildTags) > 0 {
|
||||
i.opt.context.BuildTags = options.BuildTags
|
||||
@@ -332,27 +421,27 @@ const (
|
||||
func initUniverse() *scope {
|
||||
sc := &scope{global: true, sym: map[string]*symbol{
|
||||
// predefined Go types
|
||||
"bool": {kind: typeSym, typ: &itype{cat: boolT, name: "bool"}},
|
||||
"byte": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8"}},
|
||||
"complex64": {kind: typeSym, typ: &itype{cat: complex64T, name: "complex64"}},
|
||||
"complex128": {kind: typeSym, typ: &itype{cat: complex128T, name: "complex128"}},
|
||||
"error": {kind: typeSym, typ: &itype{cat: errorT, name: "error"}},
|
||||
"float32": {kind: typeSym, typ: &itype{cat: float32T, name: "float32"}},
|
||||
"float64": {kind: typeSym, typ: &itype{cat: float64T, name: "float64"}},
|
||||
"int": {kind: typeSym, typ: &itype{cat: intT, name: "int"}},
|
||||
"int8": {kind: typeSym, typ: &itype{cat: int8T, name: "int8"}},
|
||||
"int16": {kind: typeSym, typ: &itype{cat: int16T, name: "int16"}},
|
||||
"int32": {kind: typeSym, typ: &itype{cat: int32T, name: "int32"}},
|
||||
"int64": {kind: typeSym, typ: &itype{cat: int64T, name: "int64"}},
|
||||
"interface{}": {kind: typeSym, typ: &itype{cat: interfaceT}},
|
||||
"rune": {kind: typeSym, typ: &itype{cat: int32T, name: "int32"}},
|
||||
"string": {kind: typeSym, typ: &itype{cat: stringT, name: "string"}},
|
||||
"uint": {kind: typeSym, typ: &itype{cat: uintT, name: "uint"}},
|
||||
"uint8": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8"}},
|
||||
"uint16": {kind: typeSym, typ: &itype{cat: uint16T, name: "uint16"}},
|
||||
"uint32": {kind: typeSym, typ: &itype{cat: uint32T, name: "uint32"}},
|
||||
"uint64": {kind: typeSym, typ: &itype{cat: uint64T, name: "uint64"}},
|
||||
"uintptr": {kind: typeSym, typ: &itype{cat: uintptrT, name: "uintptr"}},
|
||||
"bool": {kind: typeSym, typ: &itype{cat: boolT, name: "bool", str: "bool"}},
|
||||
"byte": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}},
|
||||
"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"}},
|
||||
"float32": {kind: typeSym, typ: &itype{cat: float32T, name: "float32", str: "float32"}},
|
||||
"float64": {kind: typeSym, typ: &itype{cat: float64T, name: "float64", str: "float64"}},
|
||||
"int": {kind: typeSym, typ: &itype{cat: intT, name: "int", str: "int"}},
|
||||
"int8": {kind: typeSym, typ: &itype{cat: int8T, name: "int8", str: "int8"}},
|
||||
"int16": {kind: typeSym, typ: &itype{cat: int16T, name: "int16", str: "int16"}},
|
||||
"int32": {kind: typeSym, typ: &itype{cat: int32T, name: "int32", str: "int32"}},
|
||||
"int64": {kind: typeSym, typ: &itype{cat: int64T, name: "int64", str: "int64"}},
|
||||
"interface{}": {kind: typeSym, typ: &itype{cat: interfaceT, str: "interface{}"}},
|
||||
"rune": {kind: typeSym, typ: &itype{cat: int32T, name: "int32", str: "int32"}},
|
||||
"string": {kind: typeSym, typ: &itype{cat: stringT, name: "string", str: "string"}},
|
||||
"uint": {kind: typeSym, typ: &itype{cat: uintT, name: "uint", str: "uint"}},
|
||||
"uint8": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}},
|
||||
"uint16": {kind: typeSym, typ: &itype{cat: uint16T, name: "uint16", str: "uint16"}},
|
||||
"uint32": {kind: typeSym, typ: &itype{cat: uint32T, name: "uint32", str: "uint32"}},
|
||||
"uint64": {kind: typeSym, typ: &itype{cat: uint64T, name: "uint64", str: "uint64"}},
|
||||
"uintptr": {kind: typeSym, typ: &itype{cat: uintptrT, name: "uintptr", str: "uintptr"}},
|
||||
|
||||
// predefined Go constants
|
||||
"false": {kind: constSym, typ: untypedBool(), rval: reflect.ValueOf(false)},
|
||||
@@ -360,7 +449,7 @@ func initUniverse() *scope {
|
||||
"iota": {kind: constSym, typ: untypedInt()},
|
||||
|
||||
// predefined Go zero value
|
||||
"nil": {typ: &itype{cat: nilT, untyped: true}},
|
||||
"nil": {typ: &itype{cat: nilT, untyped: true, str: "nil"}},
|
||||
|
||||
// predefined Go builtins
|
||||
bltnAppend: {kind: bltnSym, builtin: _append},
|
||||
@@ -407,18 +496,42 @@ func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
|
||||
// by the interpreter, and a non nil error in case of failure.
|
||||
// The main function of the main package is executed if present.
|
||||
func (interp *Interpreter) EvalPath(path string) (res reflect.Value, err error) {
|
||||
if !isFile(path) {
|
||||
if !isFile(interp.opt.filesystem, path) {
|
||||
_, err := interp.importSrc(mainID, path, NoTest)
|
||||
return res, err
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := fs.ReadFile(interp.filesystem, path)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return interp.eval(string(b), path, false)
|
||||
}
|
||||
|
||||
// EvalPathWithContext evaluates Go code located at path and returns the last
|
||||
// result computed by the interpreter, and a non nil error in case of failure.
|
||||
// The main function of the main package is executed if present.
|
||||
func (interp *Interpreter) EvalPathWithContext(ctx context.Context, path string) (res reflect.Value, err error) {
|
||||
interp.mutex.Lock()
|
||||
interp.done = make(chan struct{})
|
||||
interp.cancelChan = !interp.opt.fastChan
|
||||
interp.mutex.Unlock()
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
res, err = interp.EvalPath(path)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
interp.stop()
|
||||
return reflect.Value{}, ctx.Err()
|
||||
case <-done:
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// EvalTest evaluates Go code located at path, including test files with "_test.go" suffix.
|
||||
// A non nil error is returned in case of failure.
|
||||
// The main function, test functions and benchmark functions are internally compiled but not
|
||||
@@ -484,129 +597,22 @@ func (interp *Interpreter) Symbols(importPath string) Exports {
|
||||
return m
|
||||
}
|
||||
|
||||
func isFile(path string) bool {
|
||||
fi, err := os.Stat(path)
|
||||
func isFile(filesystem fs.FS, path string) bool {
|
||||
fi, err := fs.Stat(filesystem, path)
|
||||
return err == nil && fi.Mode().IsRegular()
|
||||
}
|
||||
|
||||
func (interp *Interpreter) eval(src, name string, inc bool) (res reflect.Value, err error) {
|
||||
if name != "" {
|
||||
interp.name = name
|
||||
}
|
||||
if interp.name == "" {
|
||||
interp.name = DefaultSourceName
|
||||
}
|
||||
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r != nil {
|
||||
var pc [64]uintptr // 64 frames should be enough.
|
||||
n := runtime.Callers(1, pc[:])
|
||||
err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()}
|
||||
}
|
||||
}()
|
||||
|
||||
// Parse source to AST.
|
||||
pkgName, root, err := interp.ast(src, interp.name, inc)
|
||||
if err != nil || root == nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if interp.astDot {
|
||||
dotCmd := interp.dotCmd
|
||||
if dotCmd == "" {
|
||||
dotCmd = defaultDotCmd(interp.name, "yaegi-ast-")
|
||||
}
|
||||
root.astDot(dotWriter(dotCmd), interp.name)
|
||||
if interp.noRun {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
// Perform global types analysis.
|
||||
if err = interp.gtaRetry([]*node{root}, pkgName); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Annotate AST with CFG informations.
|
||||
initNodes, err := interp.cfg(root, pkgName)
|
||||
prog, err := interp.compileSrc(src, name, inc)
|
||||
if err != nil {
|
||||
if interp.cfgDot {
|
||||
dotCmd := interp.dotCmd
|
||||
if dotCmd == "" {
|
||||
dotCmd = defaultDotCmd(interp.name, "yaegi-cfg-")
|
||||
}
|
||||
root.cfgDot(dotWriter(dotCmd))
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
if root.kind != fileStmt {
|
||||
// REPL may skip package statement.
|
||||
setExec(root.start)
|
||||
}
|
||||
interp.mutex.Lock()
|
||||
gs := interp.scopes[pkgName]
|
||||
if interp.universe.sym[pkgName] == nil {
|
||||
// Make the package visible under a path identical to its name.
|
||||
interp.srcPkg[pkgName] = gs.sym
|
||||
interp.universe.sym[pkgName] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: pkgName}}
|
||||
interp.pkgNames[pkgName] = pkgName
|
||||
}
|
||||
interp.mutex.Unlock()
|
||||
|
||||
// Add main to list of functions to run, after all inits.
|
||||
if m := gs.sym[mainID]; pkgName == mainID && m != nil {
|
||||
initNodes = append(initNodes, m.node)
|
||||
}
|
||||
|
||||
if interp.cfgDot {
|
||||
dotCmd := interp.dotCmd
|
||||
if dotCmd == "" {
|
||||
dotCmd = defaultDotCmd(interp.name, "yaegi-cfg-")
|
||||
}
|
||||
root.cfgDot(dotWriter(dotCmd))
|
||||
}
|
||||
|
||||
if interp.noRun {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Generate node exec closures.
|
||||
if err = genRun(root); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Init interpreter execution memory frame.
|
||||
interp.frame.setrunid(interp.runid())
|
||||
interp.frame.mutex.Lock()
|
||||
interp.resizeFrame()
|
||||
interp.frame.mutex.Unlock()
|
||||
|
||||
// Execute node closures.
|
||||
interp.run(root, nil)
|
||||
|
||||
// Wire and execute global vars.
|
||||
n, err := genGlobalVars([]*node{root}, interp.scopes[pkgName])
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
interp.run(n, nil)
|
||||
|
||||
for _, n := range initNodes {
|
||||
interp.run(n, interp.frame)
|
||||
}
|
||||
v := genValue(root)
|
||||
res = v(interp.frame)
|
||||
|
||||
// If result is an interpreter node, wrap it in a runtime callable function.
|
||||
if res.IsValid() {
|
||||
if n, ok := res.Interface().(*node); ok {
|
||||
res = genFunctionWrapper(n)(interp.frame)
|
||||
}
|
||||
}
|
||||
|
||||
return res, err
|
||||
return interp.Execute(prog)
|
||||
}
|
||||
|
||||
// EvalWithContext evaluates Go code represented as a string. It returns
|
||||
@@ -685,17 +691,17 @@ func (interp *Interpreter) Use(values Exports) error {
|
||||
// Checks if input values correspond to stdlib packages by looking for one
|
||||
// well known stdlib package path.
|
||||
if _, ok := values["fmt/fmt"]; ok {
|
||||
fixStdio(interp)
|
||||
fixStdlib(interp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixStdio redefines interpreter stdlib symbols to use the standard input,
|
||||
// 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 fixStdio(interp *Interpreter) {
|
||||
func fixStdlib(interp *Interpreter) {
|
||||
p := interp.binPkg["fmt"]
|
||||
if p == nil {
|
||||
return
|
||||
@@ -740,6 +746,7 @@ func fixStdio(interp *Interpreter) {
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -757,6 +764,27 @@ func fixStdio(interp *Interpreter) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package interp_test
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -20,13 +20,13 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
}
|
||||
dir := filepath.Join("..", "_test", "tmp")
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if err := os.Mkdir(dir, 0700); err != nil {
|
||||
if err := os.Mkdir(dir, 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
baseDir := filepath.Join("..", "_test")
|
||||
files, err := ioutil.ReadDir(baseDir)
|
||||
files, err := os.ReadDir(baseDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -51,11 +51,14 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "init1.go" || // expect error
|
||||
file.Name() == "io0.go" || // use random number
|
||||
file.Name() == "issue-1093.go" || // expect error
|
||||
file.Name() == "issue-1276.go" || // expect error
|
||||
file.Name() == "issue-1330.go" || // expect error
|
||||
file.Name() == "op1.go" || // expect error
|
||||
file.Name() == "op7.go" || // expect error
|
||||
file.Name() == "op9.go" || // expect error
|
||||
file.Name() == "bltn0.go" || // expect error
|
||||
file.Name() == "method16.go" || // private struct field
|
||||
file.Name() == "method39.go" || // expect error
|
||||
file.Name() == "switch8.go" || // expect error
|
||||
file.Name() == "switch9.go" || // expect error
|
||||
file.Name() == "switch13.go" || // expect error
|
||||
@@ -98,7 +101,16 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "server1.go" || // syntax parsing
|
||||
file.Name() == "server0.go" || // syntax parsing
|
||||
file.Name() == "server.go" || // syntax parsing
|
||||
file.Name() == "range9.go" { // expect error
|
||||
file.Name() == "range9.go" || // expect error
|
||||
file.Name() == "unsafe6.go" || // needs go.mod to be 1.17
|
||||
file.Name() == "unsafe7.go" || // needs go.mod to be 1.17
|
||||
file.Name() == "type27.go" || // expect error
|
||||
file.Name() == "type28.go" || // expect error
|
||||
file.Name() == "type29.go" || // expect error
|
||||
file.Name() == "type30.go" || // expect error
|
||||
file.Name() == "type31.go" || // expect error
|
||||
file.Name() == "type32.go" || // expect error
|
||||
file.Name() == "type33.go" { // expect error
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -134,7 +146,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
if err = w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outInterp, err := ioutil.ReadAll(r)
|
||||
outInterp, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -218,12 +230,12 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
},
|
||||
{
|
||||
fileName: "issue-1093.go",
|
||||
expectedInterp: "9:6: cannot use type string as type int in assignment",
|
||||
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`,
|
||||
},
|
||||
{
|
||||
fileName: "op1.go",
|
||||
expectedInterp: "5:2: invalid operation: mismatched types int and float64",
|
||||
expectedInterp: "5:2: invalid operation: mismatched types int and untyped float",
|
||||
expectedExec: "5:4: constant 1.3 truncated to integer",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -43,8 +42,8 @@ func TestEvalArithmetic(t *testing.T) {
|
||||
{desc: "add_FI", src: "2.3 + 3", res: "5.3"},
|
||||
{desc: "add_IF", src: "2 + 3.3", res: "5.3"},
|
||||
{desc: "add_SS", src: `"foo" + "bar"`, res: "foobar"},
|
||||
{desc: "add_SI", src: `"foo" + 1`, err: "1:28: invalid operation: mismatched types string and int"},
|
||||
{desc: "sub_SS", src: `"foo" - "bar"`, err: "1:28: invalid operation: operator - not defined on string"},
|
||||
{desc: "add_SI", src: `"foo" + 1`, err: "1:28: invalid operation: mismatched types untyped string and untyped int"},
|
||||
{desc: "sub_SS", src: `"foo" - "bar"`, err: "1:28: invalid operation: operator - not defined on untyped string"},
|
||||
{desc: "sub_II", src: "7 - 3", res: "4"},
|
||||
{desc: "sub_FI", src: "7.2 - 3", res: "4.2"},
|
||||
{desc: "sub_IF", src: "7 - 3.2", res: "3.8"},
|
||||
@@ -52,23 +51,23 @@ func TestEvalArithmetic(t *testing.T) {
|
||||
{desc: "mul_FI", src: "2.2 * 3", res: "6.6"},
|
||||
{desc: "mul_IF", src: "3 * 2.2", res: "6.6"},
|
||||
{desc: "quo_Z", src: "3 / 0", err: "1:28: invalid operation: division by zero"},
|
||||
{desc: "rem_FI", src: "8.2 % 4", err: "1:28: invalid operation: operator % not defined on float64"},
|
||||
{desc: "rem_FI", src: "8.2 % 4", err: "1:28: invalid operation: operator % not defined on untyped float"},
|
||||
{desc: "rem_Z", src: "8 % 0", err: "1:28: invalid operation: division by zero"},
|
||||
{desc: "shl_II", src: "1 << 8", res: "256"},
|
||||
{desc: "shl_IN", src: "1 << -1", err: "1:28: invalid operation: shift count type int, must be integer"},
|
||||
{desc: "shl_IN", src: "1 << -1", err: "1:28: invalid operation: shift count type untyped int, must be integer"},
|
||||
{desc: "shl_IF", src: "1 << 1.0", res: "2"},
|
||||
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: invalid operation: shift count type float64, must be integer"},
|
||||
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: invalid operation: shift count type untyped float, must be integer"},
|
||||
{desc: "shl_IF2", src: "1.0 << 1", res: "2"},
|
||||
{desc: "shr_II", src: "1 >> 8", res: "0"},
|
||||
{desc: "shr_IN", src: "1 >> -1", err: "1:28: invalid operation: shift count type int, must be integer"},
|
||||
{desc: "shr_IN", src: "1 >> -1", err: "1:28: invalid operation: shift count type untyped int, must be integer"},
|
||||
{desc: "shr_IF", src: "1 >> 1.0", res: "0"},
|
||||
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: invalid operation: shift count type float64, must be integer"},
|
||||
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: invalid operation: shift count type untyped float, must be integer"},
|
||||
{desc: "neg_I", src: "-2", res: "-2"},
|
||||
{desc: "pos_I", src: "+2", res: "2"},
|
||||
{desc: "bitnot_I", src: "^2", res: "-3"},
|
||||
{desc: "bitnot_F", src: "^0.2", err: "1:28: invalid operation: operator ^ not defined on float64"},
|
||||
{desc: "bitnot_F", src: "^0.2", err: "1:28: invalid operation: operator ^ not defined on untyped float"},
|
||||
{desc: "not_B", src: "!false", res: "true"},
|
||||
{desc: "not_I", src: "!0", err: "1:28: invalid operation: operator ! not defined on int"},
|
||||
{desc: "not_I", src: "!0", err: "1:28: invalid operation: operator ! not defined on untyped int"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -76,7 +75,7 @@ func TestEvalShift(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
{src: "a, b, m := uint32(1), uint32(2), uint32(0); m = a + (1 << b)", res: "5"},
|
||||
{src: "c := uint(1); d := uint(+(-(1 << c)))", res: "18446744073709551614"},
|
||||
{src: "c := uint(1); d := uint64(+(-(1 << c)))", res: "18446744073709551614"},
|
||||
{src: "e, f := uint32(0), uint32(0); f = 1 << -(e * 2)", res: "1"},
|
||||
{src: "p := uint(0xdead); byte((1 << (p & 7)) - 1)", res: "31"},
|
||||
{pre: func() { eval(t, i, "const k uint = 1 << 17") }, src: "int(k)", res: "131072"},
|
||||
@@ -119,10 +118,10 @@ func TestEvalAssign(t *testing.T) {
|
||||
|
||||
runTests(t, i, []testCase{
|
||||
{src: `a := "Hello"; a += " world"`, res: "Hello world"},
|
||||
{src: `b := "Hello"; b += 1`, err: "1:42: invalid operation: mismatched types string and int"},
|
||||
{src: `b := "Hello"; b += 1`, err: "1:42: invalid operation: mismatched types string and untyped int"},
|
||||
{src: `c := "Hello"; c -= " world"`, err: "1:42: invalid operation: operator -= not defined on string"},
|
||||
{src: "e := 64.4; e %= 64", err: "1:39: invalid operation: operator %= not defined on float64"},
|
||||
{src: "f := int64(3.2)", err: "1:39: cannot convert expression of type float64 to type int64"},
|
||||
{src: "f := int64(3.2)", err: "1:39: cannot convert expression of type untyped float to type int64"},
|
||||
{src: "g := 1; g <<= 8", res: "256"},
|
||||
{src: "h := 1; h >>= 8", res: "0"},
|
||||
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
|
||||
@@ -139,7 +138,9 @@ func TestEvalBuiltin(t *testing.T) {
|
||||
{src: `c := []int{1}; d := []int{2, 3}; c = append(c, d...); c`, res: "[1 2 3]"},
|
||||
{src: `string(append([]byte("hello "), "world"...))`, res: "hello world"},
|
||||
{src: `e := "world"; string(append([]byte("hello "), e...))`, res: "hello world"},
|
||||
{src: `b := []int{1}; b = append(1, 2, 3); b`, err: "1:54: first argument to append must be slice; have int"},
|
||||
{src: `b := []int{1}; b = append(1, 2, 3); b`, err: "1:54: first argument to append must be slice; have untyped int"},
|
||||
{src: `a1 := []int{0,1,2}; append(a1)`, res: "[0 1 2]"},
|
||||
{src: `append(nil)`, err: "first argument to append must be slice; have nil"},
|
||||
{src: `g := len(a)`, res: "1"},
|
||||
{src: `g := cap(a)`, res: "1"},
|
||||
{src: `g := len("test")`, res: "4"},
|
||||
@@ -161,10 +162,10 @@ func TestEvalBuiltin(t *testing.T) {
|
||||
{src: `k := []int{3, 4}; copy(k, []int{1,2}); k`, res: "[1 2]"},
|
||||
{src: `f := []byte("Hello"); copy(f, "world"); string(f)`, res: "world"},
|
||||
{src: `copy(g, g)`, err: "1:28: copy expects slice arguments"},
|
||||
{src: `copy(a, "world")`, err: "1:28: arguments to copy have different element types []int and string"},
|
||||
{src: `copy(a, "world")`, err: "1:28: arguments to copy have different element types []int and untyped string"},
|
||||
{src: `l := map[string]int{"a": 1, "b": 2}; delete(l, "a"); l`, res: "map[b:2]"},
|
||||
{src: `delete(a, 1)`, err: "1:35: first argument to delete must be map; have []int"},
|
||||
{src: `l := map[string]int{"a": 1, "b": 2}; delete(l, 1)`, err: "1:75: cannot use int as type string in delete"},
|
||||
{src: `l := map[string]int{"a": 1, "b": 2}; delete(l, 1)`, err: "1:75: cannot use untyped int as type string in delete"},
|
||||
{src: `a := []int{1,2}; println(a...)`, err: "invalid use of ... with builtin println"},
|
||||
{src: `m := complex(3, 2); real(m)`, res: "3"},
|
||||
{src: `m := complex(3, 2); imag(m)`, res: "2"},
|
||||
@@ -416,6 +417,8 @@ func TestEvalComparison(t *testing.T) {
|
||||
{src: `2 > 1`, res: "true"},
|
||||
{src: `1.2 > 1.1`, res: "true"},
|
||||
{src: `"hhh" > "ggg"`, res: "true"},
|
||||
{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",
|
||||
src: `
|
||||
@@ -438,11 +441,12 @@ func TestEvalCompositeArray(t *testing.T) {
|
||||
{src: "a := []int{1, 2, 7: 20, 30}", res: "[1 2 0 0 0 0 0 20 30]"},
|
||||
{src: `a := []int{1, 1.2}`, err: "1:42: 6/5 truncated to int"},
|
||||
{src: `a := []int{0:1, 0:1}`, err: "1:46: duplicate index 0 in array or slice literal"},
|
||||
{src: `a := []int{1.1:1, 1.2:"test"}`, err: "1:39: index float64 must be integer constant"},
|
||||
{src: `a := []int{1.1:1, 1.2:"test"}`, err: "1:39: index untyped float must be integer constant"},
|
||||
{src: `a := [2]int{1, 1.2}`, err: "1:43: 6/5 truncated to int"},
|
||||
{src: `a := [1]int{1, 2}`, err: "1:43: index 1 is out of bounds (>= 1)"},
|
||||
{src: `b := [l]int{1, 2}`, res: "[1 2 0 0 0 0 0 0 0 0]"},
|
||||
{src: `i := 10; a := [i]int{1, 2}`, err: "1:43: non-constant array bound \"i\""},
|
||||
{src: `c := [...]float64{1, 3: 3.4, 5}`, res: "[1 0 0 3.4 5]"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -505,7 +509,7 @@ func TestEvalConversion(t *testing.T) {
|
||||
{src: `a := uint64(1)`, res: "1"},
|
||||
{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 float64 to type uint64"},
|
||||
{src: `c := uint64(1.1)`, err: "1:40: cannot convert expression of type untyped float to type uint64"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -634,7 +638,7 @@ func TestEvalCall(t *testing.T) {
|
||||
a := test([]int{1}...)`, err: "2:10: invalid use of ..., corresponding parameter is non-variadic"},
|
||||
{src: ` test := func(a ...int) int { return 1 }
|
||||
blah := func() (int, int) { return 1, 1 }
|
||||
a := test(blah()...)`, err: "3:15: cannot use ... with 2-valued func()(int,int)"},
|
||||
a := test(blah()...)`, err: "3:15: cannot use ... with 2-valued func() (int,int)"},
|
||||
{src: ` test := func(a, b int) int { return a }
|
||||
blah := func() (int, int) { return 1, 1 }
|
||||
a := test(blah())`, res: "1"},
|
||||
@@ -643,10 +647,10 @@ func TestEvalCall(t *testing.T) {
|
||||
a := test(blah(), blah())`, res: "1"},
|
||||
{src: ` test := func(a, b, c, d int) int { return a }
|
||||
blah := func() (int, int) { return 1, 1 }
|
||||
a := test(blah(), blah())`, err: "3:15: cannot use func()(int,int) as type int"},
|
||||
a := test(blah(), blah())`, err: "3:15: cannot use func() (int,int) as type int"},
|
||||
{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)"},
|
||||
a := test(blah())`, err: "3:15: cannot use func() (int,float64) as type (int,int)"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -880,7 +884,7 @@ func TestMultiEval(t *testing.T) {
|
||||
if err = w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outInterp, err := ioutil.ReadAll(r)
|
||||
outInterp, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -911,7 +915,7 @@ func TestMultiEvalNoName(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for k, v := range names {
|
||||
data, err := ioutil.ReadFile(filepath.Join(f.Name(), v))
|
||||
data, err := os.ReadFile(filepath.Join(f.Name(), v))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1710,3 +1714,38 @@ func TestIssue1151(t *testing.T) {
|
||||
{src: "x := pkg.Array{1}", res: "[1]"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestPassArgs(t *testing.T) {
|
||||
i := interp.New(interp.Options{Args: []string{"arg0", "arg1"}})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i.ImportUsed()
|
||||
runTests(t, i, []testCase{
|
||||
{src: "os.Args", res: "[arg0 arg1]"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestRestrictedEnv(t *testing.T) {
|
||||
i := interp.New(interp.Options{Env: []string{"foo=bar"}})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i.ImportUsed()
|
||||
runTests(t, i, []testCase{
|
||||
{src: `os.Getenv("foo")`, res: "bar"},
|
||||
{src: `s, ok := os.LookupEnv("foo"); s`, res: "bar"},
|
||||
{src: `s, ok := os.LookupEnv("foo"); ok`, res: "true"},
|
||||
{src: `s, ok := os.LookupEnv("PATH"); s`, res: ""},
|
||||
{src: `s, ok := os.LookupEnv("PATH"); ok`, res: "false"},
|
||||
{src: `os.Setenv("foo", "baz"); os.Environ()`, res: "[foo=baz]"},
|
||||
{src: `os.ExpandEnv("foo is ${foo}")`, res: "foo is baz"},
|
||||
{src: `os.Unsetenv("foo"); os.Environ()`, res: "[]"},
|
||||
{src: `os.Setenv("foo", "baz"); os.Environ()`, res: "[foo=baz]"},
|
||||
{src: `os.Clearenv(); os.Environ()`, res: "[]"},
|
||||
{src: `os.Setenv("foo", "baz"); os.Environ()`, res: "[foo=baz]"},
|
||||
})
|
||||
if s, ok := os.LookupEnv("foo"); ok {
|
||||
t.Fatal("expected \"\", got " + s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -26,7 +25,7 @@ func TestFile(t *testing.T) {
|
||||
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "1")
|
||||
|
||||
baseDir := filepath.Join("..", "_test")
|
||||
files, err := ioutil.ReadDir(baseDir)
|
||||
files, err := os.ReadDir(baseDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2644,7 +2644,7 @@ func equal(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 == i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -2666,7 +2666,7 @@ func equal(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 == i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -2689,7 +2689,7 @@ func equal(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 == i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
|
||||
192
interp/program.go
Normal file
192
interp/program.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go/ast"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
// A Program is Go code that has been parsed and compiled.
|
||||
type Program struct {
|
||||
pkgName string
|
||||
root *node
|
||||
init []*node
|
||||
}
|
||||
|
||||
// Compile parses and compiles a Go code represented as a string.
|
||||
func (interp *Interpreter) Compile(src string) (*Program, error) {
|
||||
return interp.compileSrc(src, "", true)
|
||||
}
|
||||
|
||||
// CompilePath parses and compiles a Go code located at the given path.
|
||||
func (interp *Interpreter) CompilePath(path string) (*Program, error) {
|
||||
if !isFile(interp.filesystem, path) {
|
||||
_, err := interp.importSrc(mainID, path, NoTest)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return interp.compileSrc(string(b), path, false)
|
||||
}
|
||||
|
||||
func (interp *Interpreter) compileSrc(src, name string, inc bool) (*Program, error) {
|
||||
if name != "" {
|
||||
interp.name = name
|
||||
}
|
||||
if interp.name == "" {
|
||||
interp.name = DefaultSourceName
|
||||
}
|
||||
|
||||
// Parse source to AST.
|
||||
n, err := interp.parse(src, interp.name, inc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return interp.CompileAST(n)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (interp *Interpreter) CompileAST(n ast.Node) (*Program, error) {
|
||||
// Convert AST.
|
||||
pkgName, root, err := interp.ast(n)
|
||||
if err != nil || root == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if interp.astDot {
|
||||
dotCmd := interp.dotCmd
|
||||
if dotCmd == "" {
|
||||
dotCmd = defaultDotCmd(interp.name, "yaegi-ast-")
|
||||
}
|
||||
root.astDot(dotWriter(dotCmd), interp.name)
|
||||
if interp.noRun {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Perform global types analysis.
|
||||
if err = interp.gtaRetry([]*node{root}, pkgName, pkgName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Annotate AST with CFG informations.
|
||||
initNodes, err := interp.cfg(root, nil, pkgName, pkgName)
|
||||
if err != nil {
|
||||
if interp.cfgDot {
|
||||
dotCmd := interp.dotCmd
|
||||
if dotCmd == "" {
|
||||
dotCmd = defaultDotCmd(interp.name, "yaegi-cfg-")
|
||||
}
|
||||
root.cfgDot(dotWriter(dotCmd))
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if root.kind != fileStmt {
|
||||
// REPL may skip package statement.
|
||||
setExec(root.start)
|
||||
}
|
||||
interp.mutex.Lock()
|
||||
gs := interp.scopes[pkgName]
|
||||
if interp.universe.sym[pkgName] == nil {
|
||||
// Make the package visible under a path identical to its name.
|
||||
interp.srcPkg[pkgName] = gs.sym
|
||||
interp.universe.sym[pkgName] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: pkgName}}
|
||||
interp.pkgNames[pkgName] = pkgName
|
||||
}
|
||||
interp.mutex.Unlock()
|
||||
|
||||
// Add main to list of functions to run, after all inits.
|
||||
if m := gs.sym[mainID]; pkgName == mainID && m != nil {
|
||||
initNodes = append(initNodes, m.node)
|
||||
}
|
||||
|
||||
if interp.cfgDot {
|
||||
dotCmd := interp.dotCmd
|
||||
if dotCmd == "" {
|
||||
dotCmd = defaultDotCmd(interp.name, "yaegi-cfg-")
|
||||
}
|
||||
root.cfgDot(dotWriter(dotCmd))
|
||||
}
|
||||
|
||||
return &Program{pkgName, root, initNodes}, nil
|
||||
}
|
||||
|
||||
// Execute executes compiled Go code.
|
||||
func (interp *Interpreter) Execute(p *Program) (res reflect.Value, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r != nil {
|
||||
var pc [64]uintptr // 64 frames should be enough.
|
||||
n := runtime.Callers(1, pc[:])
|
||||
err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()}
|
||||
}
|
||||
}()
|
||||
|
||||
// Generate node exec closures.
|
||||
if err = genRun(p.root); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Init interpreter execution memory frame.
|
||||
interp.frame.setrunid(interp.runid())
|
||||
interp.frame.mutex.Lock()
|
||||
interp.resizeFrame()
|
||||
interp.frame.mutex.Unlock()
|
||||
|
||||
// Execute node closures.
|
||||
interp.run(p.root, nil)
|
||||
|
||||
// Wire and execute global vars.
|
||||
n, err := genGlobalVars([]*node{p.root}, interp.scopes[p.pkgName])
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
interp.run(n, nil)
|
||||
|
||||
for _, n := range p.init {
|
||||
interp.run(n, interp.frame)
|
||||
}
|
||||
v := genValue(p.root)
|
||||
res = v(interp.frame)
|
||||
|
||||
// If result is an interpreter node, wrap it in a runtime callable function.
|
||||
if res.IsValid() {
|
||||
if n, ok := res.Interface().(*node); ok {
|
||||
res = genFunctionWrapper(n)(interp.frame)
|
||||
}
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ExecuteWithContext executes compiled Go code.
|
||||
func (interp *Interpreter) ExecuteWithContext(ctx context.Context, p *Program) (res reflect.Value, err error) {
|
||||
interp.mutex.Lock()
|
||||
interp.done = make(chan struct{})
|
||||
interp.cancelChan = !interp.opt.fastChan
|
||||
interp.mutex.Unlock()
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
res, err = interp.Execute(p)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
interp.stop()
|
||||
return reflect.Value{}, ctx.Err()
|
||||
case <-done:
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
21
interp/realfs.go
Normal file
21
interp/realfs.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
// realFS complies with the fs.FS interface (go 1.16 onwards)
|
||||
// We use this rather than os.DirFS as DirFS has no concept of
|
||||
// what the current working directory is, whereas this simple
|
||||
// passthru to os.Open knows about working dir automagically.
|
||||
type realFS struct{}
|
||||
|
||||
// Open complies with the fs.FS interface.
|
||||
func (dir realFS) Open(name string) (fs.File, error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
413
interp/run.go
413
interp/run.go
@@ -10,7 +10,6 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// bltn type defines functions which run at CFG execution.
|
||||
@@ -118,7 +117,17 @@ func (interp *Interpreter) run(n *node, cf *frame) {
|
||||
for i, t := range n.types {
|
||||
f.data[i] = reflect.New(t).Elem()
|
||||
}
|
||||
runCfg(n.start, f)
|
||||
runCfg(n.start, f, n, nil)
|
||||
}
|
||||
|
||||
func isExecNode(n *node, exec bltn) bool {
|
||||
if n == nil || n.exec == nil || exec == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
a1 := reflect.ValueOf(n.exec).Pointer()
|
||||
a2 := reflect.ValueOf(exec).Pointer()
|
||||
return a1 == a2
|
||||
}
|
||||
|
||||
// originalExecNode looks in the tree of nodes for the node which has exec,
|
||||
@@ -166,7 +175,7 @@ func originalExecNode(n *node, exec bltn) *node {
|
||||
// Functions set to run during execution of CFG.
|
||||
|
||||
// runCfg executes a node AST by walking its CFG and running node builtin at each step.
|
||||
func runCfg(n *node, f *frame) {
|
||||
func runCfg(n *node, f *frame, funcNode, callNode *node) {
|
||||
var exec bltn
|
||||
defer func() {
|
||||
f.mutex.Lock()
|
||||
@@ -186,8 +195,44 @@ func runCfg(n *node, f *frame) {
|
||||
f.mutex.Unlock()
|
||||
}()
|
||||
|
||||
for exec = n.exec; exec != nil && f.runid() == n.interp.runid(); {
|
||||
dbg := n.interp.debugger
|
||||
if dbg == nil {
|
||||
for exec := n.exec; exec != nil && f.runid() == n.interp.runid(); {
|
||||
exec = exec(f)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if n.exec == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dbg.enterCall(funcNode, callNode, f)
|
||||
defer dbg.exitCall(funcNode, callNode, f)
|
||||
|
||||
for m, exec := n, n.exec; f.runid() == n.interp.runid(); {
|
||||
if dbg.exec(m, f) {
|
||||
break
|
||||
}
|
||||
|
||||
exec = exec(f)
|
||||
if exec == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
m = originalExecNode(n, exec)
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case isExecNode(m.tnext, exec):
|
||||
m = m.tnext
|
||||
case isExecNode(m.fnext, exec):
|
||||
m = m.fnext
|
||||
default:
|
||||
m = originalExecNode(m, exec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,6 +297,9 @@ func typeAssert(n *node, withResult, withOk bool) {
|
||||
}
|
||||
return next
|
||||
}
|
||||
if c0.typ.cat == valueT {
|
||||
valf = reflect.ValueOf(v)
|
||||
}
|
||||
if v.node.typ.id() == typID {
|
||||
if withResult {
|
||||
value0(f).Set(valf)
|
||||
@@ -568,15 +616,31 @@ func convert(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func isRecursiveType(t *itype, rtype reflect.Type) bool {
|
||||
if t.cat == structT && rtype.Kind() == reflect.Interface {
|
||||
return true
|
||||
// assignFromCall assigns values from a function call.
|
||||
func assignFromCall(n *node) {
|
||||
ncall := n.lastChild()
|
||||
l := len(n.child) - 1
|
||||
if n.anc.kind == varDecl && n.child[l-1].isType(n.scope) {
|
||||
// Ignore the type in the assignment if it is part of a variable declaration.
|
||||
l--
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT, arrayT, mapT, ptrT, sliceT:
|
||||
return isRecursiveType(t.val, t.val.rtype)
|
||||
default:
|
||||
return false
|
||||
dvalue := make([]func(*frame) reflect.Value, l)
|
||||
for i := range dvalue {
|
||||
if n.child[i].ident == "_" {
|
||||
continue
|
||||
}
|
||||
dvalue[i] = genValue(n.child[i])
|
||||
}
|
||||
next := getExec(n.tnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
for i, v := range dvalue {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
s := f.data[ncall.findex+i]
|
||||
v(f).Set(s)
|
||||
}
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
@@ -916,24 +980,32 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
d[i] = reflect.New(t).Elem()
|
||||
}
|
||||
|
||||
// Copy method receiver as first argument, if defined.
|
||||
if rcvr != nil {
|
||||
if rcvr == nil {
|
||||
d = d[numRet:]
|
||||
} else {
|
||||
// Copy method receiver as first argument.
|
||||
src, dest := rcvr(f), d[numRet]
|
||||
if src.Type().Kind() != dest.Type().Kind() {
|
||||
sk, dk := src.Kind(), dest.Kind()
|
||||
switch {
|
||||
case sk == reflect.Ptr && dk != reflect.Ptr:
|
||||
dest.Set(src.Elem())
|
||||
case sk != reflect.Ptr && dk == reflect.Ptr:
|
||||
dest.Set(src.Addr())
|
||||
} else {
|
||||
default:
|
||||
if wrappedSrc, ok := src.Interface().(valueInterface); ok {
|
||||
src = wrappedSrc.value
|
||||
}
|
||||
dest.Set(src)
|
||||
}
|
||||
d = d[numRet+1:]
|
||||
} else {
|
||||
d = d[numRet:]
|
||||
}
|
||||
|
||||
// Copy function input arguments in local frame.
|
||||
for i, arg := range in {
|
||||
if i >= len(d) {
|
||||
// In case of unused arg, there may be not even a frame entry allocated, just skip.
|
||||
break
|
||||
}
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case isEmptyInterface(typ):
|
||||
@@ -948,7 +1020,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
|
||||
// Interpreter code execution.
|
||||
runCfg(start, fr)
|
||||
runCfg(start, fr, def, n)
|
||||
|
||||
result := fr.data[:numRet]
|
||||
for i, r := range result {
|
||||
@@ -962,7 +1034,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
|
||||
func genFunctionNode(v reflect.Value) *node {
|
||||
return &node{kind: funcType, action: aNop, rval: v, typ: &itype{cat: valueT, rtype: v.Type()}}
|
||||
return &node{kind: funcType, action: aNop, rval: v, typ: valueTOf(v.Type())}
|
||||
}
|
||||
|
||||
func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
@@ -970,9 +1042,14 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
if typ == nil || typ.Kind() != reflect.Interface || typ.NumMethod() == 0 || n.typ.cat == valueT {
|
||||
return value
|
||||
}
|
||||
nt := n.typ.frameType()
|
||||
if nt != nil && nt.Implements(typ) {
|
||||
return value
|
||||
tc := n.typ.cat
|
||||
if tc != structT {
|
||||
// Always force wrapper generation for struct types, as they may contain
|
||||
// embedded interface fields which require wrapping, even if reported as
|
||||
// implementing typ by reflect.
|
||||
if nt := n.typ.frameType(); nt != nil && nt.Implements(typ) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
mn := typ.NumMethod()
|
||||
names := make([]string, mn)
|
||||
@@ -990,35 +1067,41 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.Type().Implements(typ) {
|
||||
if tc != structT && v.Type().Implements(typ) {
|
||||
return v
|
||||
}
|
||||
vv := v
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
if v.IsNil() {
|
||||
return reflect.New(typ).Elem()
|
||||
}
|
||||
if v.Kind() == reflect.Ptr {
|
||||
vv = v.Elem()
|
||||
}
|
||||
}
|
||||
var n2 *node
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
n2 = vi.node
|
||||
}
|
||||
v = getConcreteValue(v)
|
||||
w := reflect.New(wrap).Elem()
|
||||
w.Field(0).Set(v)
|
||||
for i, m := range methods {
|
||||
if m == nil {
|
||||
if r := v.MethodByName(names[i]); r.IsValid() {
|
||||
// First direct method lookup on field.
|
||||
if r := methodByName(v, names[i], indexes[i]); r.IsValid() {
|
||||
w.Field(i + 1).Set(r)
|
||||
continue
|
||||
}
|
||||
o := vv.FieldByIndex(indexes[i])
|
||||
if r := o.MethodByName(names[i]); r.IsValid() {
|
||||
w.Field(i + 1).Set(r)
|
||||
} else {
|
||||
if n2 == nil {
|
||||
panic(n.cfgErrorf("method not found: %s", names[i]))
|
||||
}
|
||||
continue
|
||||
// Method lookup in embedded valueInterface.
|
||||
m2, i2 := n2.typ.lookupMethod(names[i])
|
||||
if m2 != nil {
|
||||
nod := *m2
|
||||
nod.recv = &receiver{n, v, i2}
|
||||
w.Field(i + 1).Set(genFunctionWrapper(&nod)(f))
|
||||
continue
|
||||
}
|
||||
panic(n.cfgErrorf("method not found: %s", names[i]))
|
||||
}
|
||||
nod := *m
|
||||
nod.recv = &receiver{n, v, indexes[i]}
|
||||
@@ -1028,62 +1111,119 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
// methodByName returns the method corresponding to name on value, or nil if not found.
|
||||
// The search is extended on valueInterface wrapper if present.
|
||||
// If valid, the returned value is a method function with the receiver already set
|
||||
// (no need to pass it at call).
|
||||
func methodByName(value reflect.Value, name string, index []int) (v reflect.Value) {
|
||||
if vi, ok := value.Interface().(valueInterface); ok {
|
||||
if v = getConcreteValue(vi.value).MethodByName(name); v.IsValid() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v = value.MethodByName(name); v.IsValid() {
|
||||
return
|
||||
}
|
||||
for value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
if checkFieldIndex(value.Type(), index) {
|
||||
value = value.FieldByIndex(index)
|
||||
}
|
||||
if v = value.MethodByName(name); v.IsValid() {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func checkFieldIndex(typ reflect.Type, index []int) bool {
|
||||
if len(index) == 0 {
|
||||
return false
|
||||
}
|
||||
t := typ
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
i := index[0]
|
||||
if i >= t.NumField() {
|
||||
return false
|
||||
}
|
||||
if len(index) > 1 {
|
||||
return checkFieldIndex(t.Field(i).Type, index[1:])
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func call(n *node) {
|
||||
goroutine := n.anc.kind == goStmt
|
||||
var method bool
|
||||
value := genValue(n.child[0])
|
||||
c0 := n.child[0]
|
||||
value := genValue(c0)
|
||||
var values []func(*frame) reflect.Value
|
||||
|
||||
recvIndexLater := false
|
||||
switch {
|
||||
case n.child[0].recv != nil:
|
||||
case c0.recv != nil:
|
||||
// Compute method receiver value.
|
||||
if isRecursiveType(n.child[0].recv.node.typ, n.child[0].recv.node.typ.rtype) {
|
||||
values = append(values, genValueRecvInterfacePtr(n.child[0]))
|
||||
} else {
|
||||
values = append(values, genValueRecv(n.child[0]))
|
||||
}
|
||||
values = append(values, genValueRecv(c0))
|
||||
method = true
|
||||
case len(n.child[0].child) > 0 && n.child[0].child[0].typ != nil && isInterfaceSrc(n.child[0].child[0].typ):
|
||||
case len(c0.child) > 0 && c0.child[0].typ != nil && isInterfaceSrc(c0.child[0].typ):
|
||||
recvIndexLater = true
|
||||
values = append(values, genValueBinRecv(n.child[0], &receiver{node: n.child[0].child[0]}))
|
||||
values = append(values, genValueBinRecv(c0, &receiver{node: c0.child[0]}))
|
||||
value = genValueBinMethodOnInterface(n, value)
|
||||
method = true
|
||||
case n.child[0].action == aMethod:
|
||||
case c0.action == aMethod:
|
||||
// Add a place holder for interface method receiver.
|
||||
values = append(values, nil)
|
||||
method = true
|
||||
}
|
||||
|
||||
numRet := len(n.child[0].typ.ret)
|
||||
numRet := len(c0.typ.ret)
|
||||
variadic := variadicPos(n)
|
||||
child := n.child[1:]
|
||||
tnext := getExec(n.tnext)
|
||||
fnext := getExec(n.fnext)
|
||||
hasVariadicArgs := n.action == aCallSlice // callSlice implies variadic call with ellipsis.
|
||||
|
||||
// Compute input argument value functions.
|
||||
for i, c := range child {
|
||||
var arg *itype
|
||||
if variadic >= 0 && i >= variadic {
|
||||
arg = c0.typ.arg[variadic].val
|
||||
} else {
|
||||
arg = c0.typ.arg[i]
|
||||
}
|
||||
switch {
|
||||
case isBinCall(c):
|
||||
case isBinCall(c, c.scope):
|
||||
// Handle nested function calls: pass returned values as arguments.
|
||||
numOut := c.child[0].typ.rtype.NumOut()
|
||||
for j := 0; j < numOut; j++ {
|
||||
ind := c.findex + j
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
if hasVariadicArgs || !isInterfaceSrc(arg) || isEmptyInterface(arg) {
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
continue
|
||||
}
|
||||
values = append(values, func(f *frame) reflect.Value {
|
||||
return reflect.ValueOf(valueInterface{value: f.data[ind]})
|
||||
})
|
||||
}
|
||||
case isRegularCall(c):
|
||||
// Arguments are return values of a nested function call.
|
||||
for j := range c.child[0].typ.ret {
|
||||
cc0 := c.child[0]
|
||||
for j := range cc0.typ.ret {
|
||||
ind := c.findex + j
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
if hasVariadicArgs || !isInterfaceSrc(arg) || isEmptyInterface(arg) {
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
continue
|
||||
}
|
||||
values = append(values, func(f *frame) reflect.Value {
|
||||
return reflect.ValueOf(valueInterface{node: cc0.typ.ret[j].node, value: f.data[ind]})
|
||||
})
|
||||
}
|
||||
default:
|
||||
var arg *itype
|
||||
if variadic >= 0 && i >= variadic {
|
||||
arg = n.child[0].typ.arg[variadic].val
|
||||
} else {
|
||||
arg = n.child[0].typ.arg[i]
|
||||
}
|
||||
if c.kind == basicLit || c.rval.IsValid() {
|
||||
argType := arg.TypeOf()
|
||||
convertLiteralValue(c, argType)
|
||||
@@ -1091,13 +1231,10 @@ func call(n *node) {
|
||||
switch {
|
||||
case isEmptyInterface(arg):
|
||||
values = append(values, genValue(c))
|
||||
case isInterfaceSrc(arg) && n.action != aCallSlice:
|
||||
// callSlice implies variadic call with ellipsis, do not wrap in valueInterface.
|
||||
case isInterfaceSrc(arg) && !hasVariadicArgs:
|
||||
values = append(values, genValueInterface(c))
|
||||
case isInterfaceBin(arg):
|
||||
values = append(values, genInterfaceWrapper(c, arg.rtype))
|
||||
case isRecursiveType(c.typ, c.typ.rtype):
|
||||
values = append(values, genValueRecursiveInterfacePtrValue(c))
|
||||
default:
|
||||
values = append(values, genValue(c))
|
||||
}
|
||||
@@ -1105,10 +1242,11 @@ func call(n *node) {
|
||||
}
|
||||
|
||||
// Compute output argument value functions.
|
||||
rtypes := n.child[0].typ.ret
|
||||
rtypes := c0.typ.ret
|
||||
rvalues := make([]func(*frame) reflect.Value, len(rtypes))
|
||||
switch n.anc.kind {
|
||||
case defineXStmt, assignXStmt:
|
||||
l := n.level
|
||||
for i := range rvalues {
|
||||
c := n.anc.child[i]
|
||||
switch {
|
||||
@@ -1117,7 +1255,8 @@ func call(n *node) {
|
||||
case isInterfaceSrc(c.typ) && !isEmptyInterface(c.typ) && !isInterfaceSrc(rtypes[i]):
|
||||
rvalues[i] = genValueInterfaceValue(c)
|
||||
default:
|
||||
rvalues[i] = genValue(c)
|
||||
j := n.findex + i
|
||||
rvalues[i] = func(f *frame) reflect.Value { return getFrame(f, l).data[j] }
|
||||
}
|
||||
}
|
||||
case returnStmt:
|
||||
@@ -1138,7 +1277,7 @@ func call(n *node) {
|
||||
|
||||
if n.anc.kind == deferStmt {
|
||||
// Store function call in frame for deferred execution.
|
||||
value = genFunctionWrapper(n.child[0])
|
||||
value = genFunctionWrapper(c0)
|
||||
if method {
|
||||
// The receiver is already passed in the function wrapper, skip it.
|
||||
values = values[1:]
|
||||
@@ -1268,7 +1407,7 @@ func call(n *node) {
|
||||
// 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].Type().Kind() == reflect.Interface {
|
||||
if !val.IsZero() || dest[i].Kind() == reflect.Interface {
|
||||
dest[i].Set(val)
|
||||
}
|
||||
}
|
||||
@@ -1277,10 +1416,10 @@ func call(n *node) {
|
||||
|
||||
// Execute function body
|
||||
if goroutine {
|
||||
go runCfg(def.child[3].start, nf)
|
||||
go runCfg(def.child[3].start, nf, def, n)
|
||||
return tnext
|
||||
}
|
||||
runCfg(def.child[3].start, nf)
|
||||
runCfg(def.child[3].start, nf, def, n)
|
||||
|
||||
// Handle branching according to boolean result
|
||||
if fnext != nil && !nf.data[0].Bool() {
|
||||
@@ -1344,7 +1483,7 @@ func callBin(n *node) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case isBinCall(c):
|
||||
case isBinCall(c, c.scope):
|
||||
// Handle nested function calls: pass returned values as arguments
|
||||
numOut := c.child[0].typ.rtype.NumOut()
|
||||
for j := 0; j < numOut; j++ {
|
||||
@@ -1457,12 +1596,13 @@ func callBin(n *node) {
|
||||
rvalues := make([]func(*frame) reflect.Value, funcType.NumOut())
|
||||
for i := range rvalues {
|
||||
c := n.anc.child[i]
|
||||
if c.ident != "_" {
|
||||
if isInterfaceSrc(c.typ) {
|
||||
rvalues[i] = genValueInterfaceValue(c)
|
||||
} else {
|
||||
rvalues[i] = genValue(c)
|
||||
}
|
||||
if c.ident == "_" {
|
||||
continue
|
||||
}
|
||||
if isInterfaceSrc(c.typ) {
|
||||
rvalues[i] = genValueInterfaceValue(c)
|
||||
} else {
|
||||
rvalues[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
@@ -1489,7 +1629,11 @@ func callBin(n *node) {
|
||||
}
|
||||
out := callFn(value(f), in)
|
||||
for i, v := range out {
|
||||
f.data[b+i].Set(v)
|
||||
dest := f.data[b+i]
|
||||
if _, ok := dest.Interface().(valueInterface); ok {
|
||||
v = reflect.ValueOf(valueInterface{value: v})
|
||||
}
|
||||
dest.Set(v)
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
@@ -1501,11 +1645,16 @@ func callBin(n *node) {
|
||||
}
|
||||
out := callFn(value(f), in)
|
||||
for i := 0; i < len(out); i++ {
|
||||
if out[i].Type().Kind() == reflect.Func {
|
||||
getFrame(f, n.level).data[n.findex+i] = out[i]
|
||||
} else {
|
||||
getFrame(f, n.level).data[n.findex+i].Set(out[i])
|
||||
r := out[i]
|
||||
if r.Kind() == reflect.Func {
|
||||
getFrame(f, n.level).data[n.findex+i] = r
|
||||
continue
|
||||
}
|
||||
dest := getFrame(f, n.level).data[n.findex+i]
|
||||
if _, ok := dest.Interface().(valueInterface); ok {
|
||||
r = reflect.ValueOf(valueInterface{value: r})
|
||||
}
|
||||
dest.Set(r)
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
@@ -1817,7 +1966,7 @@ func getMethodByName(n *node) {
|
||||
}
|
||||
return next
|
||||
}
|
||||
m, li := val.node.typ.lookupMethod(name)
|
||||
m, li := typ.lookupMethod(name)
|
||||
if m == nil {
|
||||
panic(n.cfgErrorf("method not found: %s", name))
|
||||
}
|
||||
@@ -1852,9 +2001,6 @@ func getIndexSeq(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f)
|
||||
if v.Type().Kind() == reflect.Interface && n.child[0].typ.recursive {
|
||||
v = writableDeref(v)
|
||||
}
|
||||
r := v.FieldByIndex(index)
|
||||
getFrame(f, l).data[i] = r
|
||||
if r.Bool() {
|
||||
@@ -1865,34 +2011,16 @@ func getIndexSeq(n *node) {
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f)
|
||||
if v.Type().Kind() == reflect.Interface && n.child[0].typ.recursive {
|
||||
v = writableDeref(v)
|
||||
}
|
||||
getFrame(f, l).data[i] = v.FieldByIndex(index)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func writableDeref(v reflect.Value) reflect.Value {
|
||||
// Here we have an interface to a struct. Any attempt to dereference it will
|
||||
// make a copy of the struct. We need to get a Value to the actual struct.
|
||||
// TODO: using unsafe is a temporary measure. Rethink this.
|
||||
// TODO: InterfaceData has been depreciated, this is even less of a good idea now.
|
||||
return reflect.NewAt(v.Elem().Type(), unsafe.Pointer(v.InterfaceData()[1])).Elem() //nolint:govet,staticcheck
|
||||
}
|
||||
|
||||
func getPtrIndexSeq(n *node) {
|
||||
index := n.val.([]int)
|
||||
tnext := getExec(n.tnext)
|
||||
var value func(*frame) reflect.Value
|
||||
if isRecursiveType(n.child[0].typ, n.child[0].typ.rtype) {
|
||||
v := genValue(n.child[0])
|
||||
value = func(f *frame) reflect.Value { return v(f).Elem().Elem() }
|
||||
} else {
|
||||
value = genValue(n.child[0])
|
||||
}
|
||||
value := genValue(n.child[0])
|
||||
i := n.findex
|
||||
l := n.level
|
||||
|
||||
@@ -2247,9 +2375,13 @@ func _return(n *node) {
|
||||
}
|
||||
values[i] = genValueInterface(c)
|
||||
case valueT:
|
||||
if t.rtype.Kind() == reflect.Interface {
|
||||
switch t.rtype.Kind() {
|
||||
case reflect.Interface:
|
||||
values[i] = genInterfaceWrapper(c, t.rtype)
|
||||
break
|
||||
continue
|
||||
case reflect.Func:
|
||||
values[i] = genFunctionWrapper(c)
|
||||
continue
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
@@ -2489,7 +2621,7 @@ func doCompositeBinStruct(n *node, hasType bool) {
|
||||
}
|
||||
d := value(f)
|
||||
switch {
|
||||
case d.Type().Kind() == reflect.Ptr:
|
||||
case d.Kind() == reflect.Ptr:
|
||||
d.Set(s.Addr())
|
||||
default:
|
||||
d.Set(s)
|
||||
@@ -2544,10 +2676,8 @@ 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):
|
||||
case isArray(val.typ) && val.typ.val != nil && isInterfaceSrc(val.typ.val) && !isEmptyInterface(val.typ.val):
|
||||
values[fieldIndex] = genValueInterfaceArray(val)
|
||||
case isRecursiveType(ft, rft):
|
||||
values[fieldIndex] = genValueRecursiveInterface(val, rft)
|
||||
case isInterfaceSrc(ft) && !isEmptyInterface(ft):
|
||||
values[fieldIndex] = genValueInterface(val)
|
||||
case isInterface(ft):
|
||||
@@ -2569,7 +2699,7 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
}
|
||||
d := value(f)
|
||||
switch {
|
||||
case d.Type().Kind() == reflect.Ptr:
|
||||
case d.Kind() == reflect.Ptr:
|
||||
d.Set(a.Addr())
|
||||
case destInterface:
|
||||
if len(destType(n).field) > 0 {
|
||||
@@ -2825,12 +2955,13 @@ func _case(n *node) {
|
||||
if typ.cat == nilT && v.IsNil() {
|
||||
return tnext
|
||||
}
|
||||
if typ.TypeOf().String() == t.String() {
|
||||
rtyp := typ.TypeOf()
|
||||
if rtyp != nil && rtyp.String() == t.String() && implementsInterface(v, typ) {
|
||||
destValue(f).Set(v.Elem())
|
||||
return tnext
|
||||
}
|
||||
ival := v.Interface()
|
||||
if ival != nil && typ.TypeOf().String() == reflect.TypeOf(ival).String() {
|
||||
if ival != nil && rtyp != nil && rtyp.String() == reflect.TypeOf(ival).String() {
|
||||
destValue(f).Set(v.Elem())
|
||||
return tnext
|
||||
}
|
||||
@@ -2893,6 +3024,22 @@ func _case(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func implementsInterface(v reflect.Value, t *itype) bool {
|
||||
rt := v.Type()
|
||||
if t.cat == valueT {
|
||||
return rt.Implements(t.rtype)
|
||||
}
|
||||
vt := &itype{cat: valueT, rtype: rt}
|
||||
if vt.methods().contains(t.methods()) {
|
||||
return true
|
||||
}
|
||||
vi, ok := v.Interface().(valueInterface)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return vi.node != nil && vi.node.typ.methods().contains(t.methods())
|
||||
}
|
||||
|
||||
func appendSlice(n *node) {
|
||||
dest := genValueOutput(n, n.typ.rtype)
|
||||
next := getExec(n.tnext)
|
||||
@@ -2928,20 +3075,24 @@ func _append(n *node) {
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if len(n.child) > 3 {
|
||||
switch l := len(n.child); {
|
||||
case l == 2:
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(value(f))
|
||||
return next
|
||||
}
|
||||
case l > 3:
|
||||
args := n.child[2:]
|
||||
l := len(args)
|
||||
values := make([]func(*frame) reflect.Value, l)
|
||||
for i, arg := range args {
|
||||
switch {
|
||||
case isEmptyInterface(n.typ.val):
|
||||
switch elem := n.typ.elem(); {
|
||||
case isEmptyInterface(elem):
|
||||
values[i] = genValue(arg)
|
||||
case isInterfaceSrc(n.typ.val):
|
||||
case isInterfaceSrc(elem):
|
||||
values[i] = genValueInterface(arg)
|
||||
case isInterfaceBin(n.typ.val):
|
||||
values[i] = genInterfaceWrapper(arg, n.typ.val.rtype)
|
||||
case isRecursiveType(n.typ.val, n.typ.val.rtype):
|
||||
values[i] = genValueRecursiveInterface(arg, n.typ.val.rtype)
|
||||
case isInterfaceBin(elem):
|
||||
values[i] = genInterfaceWrapper(arg, elem.rtype)
|
||||
case arg.typ.untyped:
|
||||
values[i] = genValueAs(arg, n.child[1].typ.TypeOf().Elem())
|
||||
default:
|
||||
@@ -2957,7 +3108,7 @@ func _append(n *node) {
|
||||
dest(f).Set(reflect.Append(value(f), sl...))
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
var value0 func(*frame) reflect.Value
|
||||
switch elem := n.typ.elem(); {
|
||||
case isEmptyInterface(elem):
|
||||
@@ -2966,8 +3117,6 @@ func _append(n *node) {
|
||||
value0 = genValueInterface(n.child[2])
|
||||
case isInterfaceBin(elem):
|
||||
value0 = genInterfaceWrapper(n.child[2], elem.rtype)
|
||||
case isRecursiveType(elem, elem.rtype):
|
||||
value0 = genValueRecursiveInterface(n.child[2], elem.rtype)
|
||||
case n.child[2].typ.untyped:
|
||||
value0 = genValueAs(n.child[2], n.child[1].typ.TypeOf().Elem())
|
||||
default:
|
||||
@@ -3100,23 +3249,37 @@ func _delete(n *node) {
|
||||
}
|
||||
|
||||
func capConst(n *node) {
|
||||
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
|
||||
// There is no Cap() method for reflect.Type, just return Len() instead.
|
||||
n.rval.SetInt(int64(n.child[1].typ.TypeOf().Len()))
|
||||
lenConst(n)
|
||||
}
|
||||
|
||||
func lenConst(n *node) {
|
||||
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
|
||||
if c1 := n.child[1]; c1.rval.IsValid() {
|
||||
c1 := n.child[1]
|
||||
if c1.rval.IsValid() {
|
||||
n.rval.SetInt(int64(len(vString(c1.rval))))
|
||||
} else {
|
||||
n.rval.SetInt(int64(c1.typ.TypeOf().Len()))
|
||||
return
|
||||
}
|
||||
t := c1.typ.TypeOf()
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
n.rval.SetInt(int64(t.Len()))
|
||||
}
|
||||
|
||||
func _len(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(int(0)))
|
||||
value := genValue(n.child[1])
|
||||
if isPtr(n.child[1].typ) {
|
||||
val := value
|
||||
value = func(f *frame) reflect.Value {
|
||||
v := val(f).Elem()
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
}
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if wantEmptyInterface(n) {
|
||||
|
||||
@@ -78,6 +78,7 @@ type scope struct {
|
||||
loop *node // loop exit node for break statement
|
||||
loopRestart *node // loop restart node for continue statement
|
||||
pkgID string // unique id of package in which scope is defined
|
||||
pkgName string // package name for the package
|
||||
types []reflect.Type // frame layout, may be shared by same level scopes
|
||||
level int // frame level: number of frame indirections to access var during execution
|
||||
sym map[string]*symbol // map of symbols defined in this current scope
|
||||
@@ -172,7 +173,14 @@ func (s *scope) rangeChanType(n *node) *itype {
|
||||
case c.typ.cat == chanT, c.typ.cat == chanRecvT:
|
||||
return c.typ
|
||||
case c.typ.cat == valueT && c.typ.rtype.Kind() == reflect.Chan:
|
||||
return &itype{cat: chanT, val: &itype{cat: valueT, rtype: c.typ.rtype.Elem()}}
|
||||
dir := chanSendRecv
|
||||
switch c.typ.rtype.ChanDir() {
|
||||
case reflect.RecvDir:
|
||||
dir = chanRecv
|
||||
case reflect.SendDir:
|
||||
dir = chanSend
|
||||
}
|
||||
return chanOf(valueTOf(c.typ.rtype.Elem()), dir)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -220,7 +228,7 @@ func (s *scope) add(typ *itype) (index int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (interp *Interpreter) initScopePkg(pkgID string) *scope {
|
||||
func (interp *Interpreter) initScopePkg(pkgID, pkgName string) *scope {
|
||||
sc := interp.universe
|
||||
|
||||
interp.mutex.Lock()
|
||||
@@ -229,6 +237,7 @@ func (interp *Interpreter) initScopePkg(pkgID string) *scope {
|
||||
}
|
||||
sc = interp.scopes[pkgID]
|
||||
sc.pkgID = pkgID
|
||||
sc.pkgName = pkgName
|
||||
interp.mutex.Unlock()
|
||||
return sc
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package interp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -48,7 +48,7 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
}
|
||||
interp.rdir[importPath] = true
|
||||
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
files, err := fs.ReadDir(interp.opt.filesystem, dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -69,12 +69,20 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
|
||||
name = filepath.Join(dir, name)
|
||||
var buf []byte
|
||||
if buf, err = ioutil.ReadFile(name); err != nil {
|
||||
if buf, err = fs.ReadFile(interp.opt.filesystem, name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
n, err := interp.parse(string(buf), name, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var pname string
|
||||
if pname, root, err = interp.ast(string(buf), name, false); err != nil {
|
||||
if pname, root, err = interp.ast(n); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if root == nil {
|
||||
@@ -97,7 +105,7 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
|
||||
subRPath := effectivePkg(rPath, importPath)
|
||||
var list []*node
|
||||
list, err = interp.gta(root, subRPath, importPath)
|
||||
list, err = interp.gta(root, subRPath, importPath, pkgName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -106,7 +114,7 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
|
||||
// Revisit incomplete nodes where GTA could not complete.
|
||||
for _, nodes := range revisit {
|
||||
if err = interp.gtaRetry(nodes, importPath); err != nil {
|
||||
if err = interp.gtaRetry(nodes, importPath, pkgName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
@@ -114,7 +122,7 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
// Generate control flow graphs.
|
||||
for _, root := range rootNodes {
|
||||
var nodes []*node
|
||||
if nodes, err = interp.cfg(root, importPath); err != nil {
|
||||
if nodes, err = interp.cfg(root, nil, importPath, pkgName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
initNodes = append(initNodes, nodes...)
|
||||
@@ -185,13 +193,13 @@ func (interp *Interpreter) pkgDir(goPath string, root, importPath string) (strin
|
||||
rPath := filepath.Join(root, "vendor")
|
||||
dir := filepath.Join(goPath, "src", rPath, importPath)
|
||||
|
||||
if _, err := os.Stat(dir); err == nil {
|
||||
if _, err := fs.Stat(interp.opt.filesystem, dir); err == nil {
|
||||
return dir, rPath, nil // found!
|
||||
}
|
||||
|
||||
dir = filepath.Join(goPath, "src", effectivePkg(root, importPath))
|
||||
|
||||
if _, err := os.Stat(dir); err == nil {
|
||||
if _, err := fs.Stat(interp.opt.filesystem, dir); err == nil {
|
||||
return dir, root, nil // found!
|
||||
}
|
||||
|
||||
@@ -203,7 +211,7 @@ func (interp *Interpreter) pkgDir(goPath string, root, importPath string) (strin
|
||||
}
|
||||
|
||||
rootPath := filepath.Join(goPath, "src", root)
|
||||
prevRoot, err := previousRoot(rootPath, root)
|
||||
prevRoot, err := previousRoot(interp.opt.filesystem, rootPath, root)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@@ -214,7 +222,7 @@ func (interp *Interpreter) pkgDir(goPath string, root, importPath string) (strin
|
||||
const vendor = "vendor"
|
||||
|
||||
// Find the previous source root (vendor > vendor > ... > GOPATH).
|
||||
func previousRoot(rootPath, root string) (string, error) {
|
||||
func previousRoot(filesystem fs.FS, rootPath, root string) (string, error) {
|
||||
rootPath = filepath.Clean(rootPath)
|
||||
parent, final := filepath.Split(rootPath)
|
||||
parent = filepath.Clean(parent)
|
||||
@@ -227,7 +235,7 @@ func previousRoot(rootPath, root string) (string, error) {
|
||||
// look for the closest vendor in one of our direct ancestors, as it takes priority.
|
||||
var vendored string
|
||||
for {
|
||||
fi, err := os.Lstat(filepath.Join(parent, vendor))
|
||||
fi, err := fs.Stat(filesystem, filepath.Join(parent, vendor))
|
||||
if err == nil && fi.IsDir() {
|
||||
vendored = strings.TrimPrefix(strings.TrimPrefix(parent, prefix), string(filepath.Separator))
|
||||
break
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -50,7 +49,7 @@ func Test_effectivePkg(t *testing.T) {
|
||||
|
||||
func Test_pkgDir(t *testing.T) {
|
||||
// create GOPATH
|
||||
goPath, err := ioutil.TempDir("", "pkdir")
|
||||
goPath, err := os.MkdirTemp("", "pkdir")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -60,7 +59,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
|
||||
// Create project
|
||||
project := filepath.Join(goPath, "src", "guthib.com", "foo", "root")
|
||||
if err := os.MkdirAll(project, 0700); err != nil {
|
||||
if err := os.MkdirAll(project, 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -81,7 +80,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: "",
|
||||
setup: func() error {
|
||||
return os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0700)
|
||||
return os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "bar"),
|
||||
@@ -93,7 +92,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: filepath.Join("guthib.com", "foo", "root"),
|
||||
setup: func() error {
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0700)
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bar"),
|
||||
@@ -105,7 +104,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: filepath.Join("guthib.com", "foo", "root"),
|
||||
setup: func() error {
|
||||
return os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0700)
|
||||
return os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "bar"),
|
||||
@@ -117,10 +116,10 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: filepath.Join("guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bir"),
|
||||
setup: func() error {
|
||||
if err := os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0700); err != nil {
|
||||
if err := os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bir"), 0700)
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bir"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bar"),
|
||||
@@ -132,10 +131,10 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: filepath.Join("guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bir"),
|
||||
setup: func() error {
|
||||
if err := os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0700); err != nil {
|
||||
if err := os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bir"), 0700)
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bir"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "bar"),
|
||||
@@ -149,10 +148,10 @@ func Test_pkgDir(t *testing.T) {
|
||||
setup: func() error {
|
||||
if err := os.MkdirAll(
|
||||
filepath.Join(goPath, "src", "guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bir", "vendor", "guthib.com", "foo", "bur"),
|
||||
0700); err != nil {
|
||||
0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0700)
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(project, "vendor", "guthib.com", "foo", "bar"),
|
||||
@@ -161,7 +160,11 @@ func Test_pkgDir(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
interp := &Interpreter{}
|
||||
interp := &Interpreter{
|
||||
opt: opt{
|
||||
filesystem: &realFS{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
@@ -169,7 +172,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
if err := os.RemoveAll(goPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(goPath, 0700); err != nil {
|
||||
if err := os.MkdirAll(goPath, 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -247,7 +250,7 @@ func Test_previousRoot(t *testing.T) {
|
||||
} else {
|
||||
rootPath = vendor
|
||||
}
|
||||
p, err := previousRoot(rootPath, test.root)
|
||||
p, err := previousRoot(&realFS{}, rootPath, test.root)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
1058
interp/type.go
1058
interp/type.go
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,9 @@ type opPredicates map[action]func(reflect.Type) bool
|
||||
// Due to variant type systems (itype vs reflect.Type) a single
|
||||
// type system should used, namely reflect.Type with exception
|
||||
// of the untyped flag on itype.
|
||||
type typecheck struct{}
|
||||
type typecheck struct {
|
||||
scope *scope
|
||||
}
|
||||
|
||||
// op type checks an expression against a set of expression predicates.
|
||||
func (check typecheck) op(p opPredicates, a action, n, c *node, t reflect.Type) error {
|
||||
@@ -40,7 +42,7 @@ func (check typecheck) assignment(n *node, typ *itype, context string) error {
|
||||
if typ == nil && n.typ.cat == nilT {
|
||||
return n.cfgErrorf("use of untyped nil in %s", context)
|
||||
}
|
||||
typ = n.typ.defaultType(n.rval)
|
||||
typ = n.typ.defaultType(n.rval, check.scope)
|
||||
}
|
||||
if err := check.convertUntyped(n, typ); err != nil {
|
||||
return err
|
||||
@@ -51,11 +53,7 @@ func (check typecheck) assignment(n *node, typ *itype, context string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if typ.isIndirectRecursive() || n.typ.isIndirectRecursive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !n.typ.assignableTo(typ) {
|
||||
if !n.typ.assignableTo(typ) && typ.str != "*unsafe2.dummy" {
|
||||
if context == "" {
|
||||
return n.cfgErrorf("cannot use type %s as type %s", n.typ.id(), typ.id())
|
||||
}
|
||||
@@ -72,7 +70,7 @@ func (check typecheck) assignExpr(n, dest, src *node) error {
|
||||
isConst := n.anc.kind == constDecl
|
||||
if !isConst {
|
||||
// var operations must be typed
|
||||
dest.typ = dest.typ.defaultType(src.rval)
|
||||
dest.typ = dest.typ.defaultType(src.rval, check.scope)
|
||||
}
|
||||
|
||||
return check.assignment(src, dest.typ, "assignment")
|
||||
@@ -167,7 +165,7 @@ func (check typecheck) shift(n *node) error {
|
||||
|
||||
switch {
|
||||
case c1.typ.untyped:
|
||||
if err := check.convertUntyped(c1, &itype{cat: uintT, name: "uint"}); err != nil {
|
||||
if err := check.convertUntyped(c1, check.scope.getType("uint")); err != nil {
|
||||
return n.cfgErrorf("invalid operation: shift count type %v, must be integer", c1.typ.id())
|
||||
}
|
||||
case isInt(t1):
|
||||
@@ -277,7 +275,7 @@ func zeroConst(n *node) bool {
|
||||
}
|
||||
|
||||
func (check typecheck) index(n *node, max int) error {
|
||||
if err := check.convertUntyped(n, &itype{cat: intT, name: "int"}); err != nil {
|
||||
if err := check.convertUntyped(n, check.scope.getType("int")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -440,7 +438,7 @@ func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error {
|
||||
return c.cfgErrorf("unknown field %s in struct literal", name)
|
||||
}
|
||||
|
||||
if err := check.assignment(val, &itype{cat: valueT, rtype: field.Type}, "struct literal"); err != nil {
|
||||
if err := check.assignment(val, valueTOf(field.Type), "struct literal"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -466,7 +464,7 @@ func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error {
|
||||
return c.cfgErrorf("implicit assignment to unexported field %s in %s literal", field.Name, typ)
|
||||
}
|
||||
|
||||
if err := check.assignment(c, &itype{cat: valueT, rtype: field.Type}, "struct literal"); err != nil {
|
||||
if err := check.assignment(c, valueTOf(field.Type), "struct literal"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -651,7 +649,7 @@ func (check typecheck) conversion(n *node, typ *itype) error {
|
||||
return nil
|
||||
}
|
||||
if isInterface(typ) || !isConstType(typ) {
|
||||
typ = n.typ.defaultType(n.rval)
|
||||
typ = n.typ.defaultType(n.rval, check.scope)
|
||||
}
|
||||
return check.convertUntyped(n, typ)
|
||||
}
|
||||
@@ -735,29 +733,28 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
|
||||
case bltnAppend:
|
||||
typ := params[0].Type()
|
||||
t := typ.TypeOf()
|
||||
if t.Kind() != reflect.Slice {
|
||||
if t == nil || t.Kind() != reflect.Slice {
|
||||
return params[0].nod.cfgErrorf("first argument to append must be slice; have %s", typ.id())
|
||||
}
|
||||
|
||||
if nparams == 1 {
|
||||
return nil
|
||||
}
|
||||
// Special case append([]byte, "test"...) is allowed.
|
||||
t1 := params[1].Type()
|
||||
if nparams == 2 && ellipsis && t.Elem().Kind() == reflect.Uint8 && t1.TypeOf().Kind() == reflect.String {
|
||||
if t1.untyped {
|
||||
return check.convertUntyped(params[1].nod, &itype{cat: stringT, name: "string"})
|
||||
return check.convertUntyped(params[1].nod, check.scope.getType("string"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// We cannot check a recursive type.
|
||||
if isRecursiveType(typ, typ.TypeOf()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
fun := &node{
|
||||
typ: &itype{
|
||||
cat: funcT,
|
||||
arg: []*itype{
|
||||
typ,
|
||||
{cat: variadicT, val: &itype{cat: valueT, rtype: t.Elem()}},
|
||||
{cat: variadicT, val: valueTOf(t.Elem())},
|
||||
},
|
||||
ret: []*itype{typ},
|
||||
},
|
||||
@@ -796,7 +793,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 := &itype{cat: float64T, name: "float64"}
|
||||
fltType := check.scope.getType("float64")
|
||||
err = check.convertUntyped(p0.nod, fltType)
|
||||
if err != nil {
|
||||
break
|
||||
@@ -819,7 +816,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, &itype{cat: complex128T, name: "complex128"}); err != nil {
|
||||
if err := check.convertUntyped(p.nod, check.scope.getType("complex128")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -853,7 +850,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
|
||||
return params[0].nod.cfgErrorf("first argument to delete must be map; have %s", typ.id())
|
||||
}
|
||||
ktyp := params[1].Type()
|
||||
if !ktyp.assignableTo(typ.key) {
|
||||
if typ.key != nil && !ktyp.assignableTo(typ.key) {
|
||||
return params[1].nod.cfgErrorf("cannot use %s as type %s in delete", ktyp.id(), typ.key.id())
|
||||
}
|
||||
case bltnMake:
|
||||
@@ -886,7 +883,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
|
||||
}
|
||||
|
||||
case bltnPanic:
|
||||
return check.assignment(params[0].nod, &itype{cat: interfaceT}, "argument to panic")
|
||||
return check.assignment(params[0].nod, check.scope.getType("interface{}"), "argument to panic")
|
||||
case bltnPrint, bltnPrintln:
|
||||
for _, param := range params {
|
||||
if param.typ != nil {
|
||||
@@ -910,7 +907,7 @@ func arrayDeref(typ *itype) *itype {
|
||||
if typ.cat == valueT && typ.TypeOf().Kind() == reflect.Ptr {
|
||||
t := typ.TypeOf()
|
||||
if t.Elem().Kind() == reflect.Array {
|
||||
return &itype{cat: valueT, rtype: t.Elem()}
|
||||
return valueTOf(t.Elem())
|
||||
}
|
||||
return typ
|
||||
}
|
||||
@@ -970,8 +967,8 @@ func (check typecheck) argument(p param, ftyp *itype, i, l int, ellipsis bool) e
|
||||
return p.nod.cfgErrorf("can only use ... with matching parameter")
|
||||
}
|
||||
t := p.Type().TypeOf()
|
||||
if t.Kind() != reflect.Slice || !(&itype{cat: valueT, rtype: t.Elem()}).assignableTo(atyp) {
|
||||
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (&itype{cat: sliceT, val: atyp}).id())
|
||||
if t.Kind() != reflect.Slice || !(valueTOf(t.Elem())).assignableTo(atyp) {
|
||||
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (sliceOf(atyp)).id())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -994,7 +991,7 @@ func getArg(ftyp *itype, i int) *itype {
|
||||
case i < l:
|
||||
return ftyp.in(i)
|
||||
case ftyp.cat == valueT && i < ftyp.rtype.NumIn():
|
||||
return &itype{cat: valueT, rtype: ftyp.rtype.In(i)}
|
||||
return valueTOf(ftyp.rtype.In(i))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -1054,7 +1051,7 @@ func (check typecheck) convertUntyped(n *node, typ *itype) error {
|
||||
if len(n.typ.methods()) > 0 { // untyped cannot be set to iface
|
||||
return convErr
|
||||
}
|
||||
ityp = n.typ.defaultType(n.rval)
|
||||
ityp = n.typ.defaultType(n.rval, check.scope)
|
||||
rtyp = ntyp
|
||||
case isArray(typ) || isMap(typ) || isChan(typ) || isFunc(typ) || isPtr(typ):
|
||||
// TODO(nick): above we are acting on itype, but really it is an rtype check. This is not clear which type
|
||||
|
||||
40
interp/typestring.go
Normal file
40
interp/typestring.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package interp
|
||||
|
||||
import "strings"
|
||||
|
||||
func paramsTypeString(params []*itype) string {
|
||||
strs := make([]string, 0, len(params))
|
||||
for _, param := range params {
|
||||
strs = append(strs, param.str)
|
||||
}
|
||||
return strings.Join(strs, ",")
|
||||
}
|
||||
|
||||
func methodsTypeString(fields []structField) string {
|
||||
strs := make([]string, 0, len(fields))
|
||||
for _, field := range fields {
|
||||
if field.embed {
|
||||
str := methodsTypeString(field.typ.field)
|
||||
if str != "" {
|
||||
strs = append(strs, str)
|
||||
}
|
||||
continue
|
||||
}
|
||||
strs = append(strs, field.name+field.typ.str[4:])
|
||||
}
|
||||
return strings.Join(strs, "; ")
|
||||
}
|
||||
|
||||
func fieldsTypeString(fields []structField) string {
|
||||
strs := make([]string, 0, len(fields))
|
||||
for _, field := range fields {
|
||||
var repr strings.Builder
|
||||
if !field.embed {
|
||||
repr.WriteString(field.name)
|
||||
repr.WriteByte(' ')
|
||||
}
|
||||
repr.WriteString(field.typ.str)
|
||||
strs = append(strs, repr.String())
|
||||
}
|
||||
return strings.Join(strs, "; ")
|
||||
}
|
||||
122
interp/value.go
122
interp/value.go
@@ -65,22 +65,30 @@ func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value
|
||||
nod = vi.node
|
||||
}
|
||||
|
||||
if nod == nil {
|
||||
if nod == nil || nod.typ.rtype == nil {
|
||||
return defaultGen(f)
|
||||
}
|
||||
|
||||
typ := nod.typ
|
||||
if typ.node != nil || typ.cat != valueT {
|
||||
// Try to get the bin method, if it doesnt exist, fall back to
|
||||
// the default generator function.
|
||||
meth, ok := nod.typ.rtype.MethodByName(c0.child[1].ident)
|
||||
if !ok {
|
||||
return defaultGen(f)
|
||||
}
|
||||
meth, _ := typ.rtype.MethodByName(c0.child[1].ident)
|
||||
|
||||
return meth.Func
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecvIndirect(n *node) func(*frame) reflect.Value {
|
||||
v := genValueRecv(n)
|
||||
return func(f *frame) reflect.Value { return v(f).Elem() }
|
||||
vr := genValueRecv(n)
|
||||
return func(f *frame) reflect.Value {
|
||||
v := vr(f)
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
return vi.value
|
||||
}
|
||||
return v.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecv(n *node) func(*frame) reflect.Value {
|
||||
@@ -129,25 +137,6 @@ func genValueBinRecv(n *node, recv *receiver) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecvInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
v := genValue(n.recv.node)
|
||||
fi := n.recv.index
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
r := v(f)
|
||||
r = r.Elem().Elem()
|
||||
|
||||
if len(fi) == 0 {
|
||||
return r
|
||||
}
|
||||
|
||||
if r.Kind() == reflect.Ptr {
|
||||
r = r.Elem()
|
||||
}
|
||||
return r.FieldByIndex(fi)
|
||||
}
|
||||
}
|
||||
|
||||
func genValueAsFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
typ := n.typ.TypeOf()
|
||||
@@ -158,7 +147,7 @@ func genValueAsFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
return reflect.New(typ).Elem()
|
||||
}
|
||||
vn, ok := v.Interface().(*node)
|
||||
if ok && vn.rval.IsValid() && vn.rval.Type().Kind() == reflect.Func {
|
||||
if ok && vn.rval.Kind() == reflect.Func {
|
||||
// The node value is already a callable func, no need to wrap it.
|
||||
return vn.rval
|
||||
}
|
||||
@@ -171,7 +160,7 @@ func genValueAs(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice, reflect.UnsafePointer:
|
||||
if v.IsNil() {
|
||||
return reflect.New(t).Elem()
|
||||
@@ -232,7 +221,7 @@ func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
|
||||
switch {
|
||||
case isInterfaceSrc(typ) && !isEmptyInterface(typ):
|
||||
return genValueInterface(n)
|
||||
case isFuncSrc(typ) && n.typ.cat == valueT:
|
||||
case isFuncSrc(typ) && (n.typ.cat == valueT || n.typ.cat == nilT):
|
||||
return genValueNode(n)
|
||||
case typ.cat == valueT && isFuncSrc(n.typ):
|
||||
return genFunctionWrapper(n)
|
||||
@@ -240,10 +229,6 @@ func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
|
||||
return genInterfaceWrapper(n, typ.rtype)
|
||||
case n.kind == basicLit && n.val == nil:
|
||||
return func(*frame) reflect.Value { return reflect.New(typ.rtype).Elem() }
|
||||
case isRecursiveType(typ, typ.rtype):
|
||||
return genValueRecursiveInterface(n, typ.rtype)
|
||||
case isRecursiveType(n.typ, n.typ.rtype):
|
||||
return genValueRecursiveInterfacePtrValue(n)
|
||||
case n.typ.untyped && isComplex(typ.TypeOf()):
|
||||
return genValueComplex(n)
|
||||
case n.typ.untyped && !typ.untyped:
|
||||
@@ -333,7 +318,7 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
|
||||
// empty interface, do not wrap.
|
||||
if nod.typ.cat == interfaceT && len(nod.typ.field) == 0 {
|
||||
if nod != nil && isEmptyInterface(nod.typ) {
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -353,13 +338,13 @@ func getConcreteValue(val reflect.Value) reflect.Value {
|
||||
if v.NumMethod() > 0 {
|
||||
return v
|
||||
}
|
||||
if v.Type().Kind() != reflect.Struct {
|
||||
if v.Kind() != reflect.Struct {
|
||||
return v
|
||||
}
|
||||
// Search a concrete value in fields of an emulated interface.
|
||||
for i := v.NumField() - 1; i >= 0; i-- {
|
||||
vv := v.Field(i)
|
||||
if vv.Type().Kind() == reflect.Interface {
|
||||
if vv.Kind() == reflect.Interface {
|
||||
vv = vv.Elem()
|
||||
}
|
||||
if vv.IsValid() {
|
||||
@@ -370,7 +355,7 @@ func getConcreteValue(val reflect.Value) reflect.Value {
|
||||
}
|
||||
|
||||
func zeroInterfaceValue() reflect.Value {
|
||||
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true}}
|
||||
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true, str: "nil"}}
|
||||
v := reflect.New(interf).Elem()
|
||||
return reflect.ValueOf(valueInterface{n, v})
|
||||
}
|
||||
@@ -440,69 +425,12 @@ func genValueNode(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecursiveInterface(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
vv := value(f)
|
||||
v := reflect.New(t).Elem()
|
||||
toRecursive(v, vv)
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func toRecursive(dest, src reflect.Value) {
|
||||
if !src.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
switch dest.Kind() {
|
||||
case reflect.Map:
|
||||
v := reflect.MakeMapWithSize(dest.Type(), src.Len())
|
||||
for _, kv := range src.MapKeys() {
|
||||
vv := reflect.New(dest.Type().Elem()).Elem()
|
||||
toRecursive(vv, src.MapIndex(kv))
|
||||
vv.SetMapIndex(kv, vv)
|
||||
}
|
||||
dest.Set(v)
|
||||
case reflect.Slice:
|
||||
l := src.Len()
|
||||
v := reflect.MakeSlice(dest.Type(), l, l)
|
||||
for i := 0; i < l; i++ {
|
||||
toRecursive(v.Index(i), src.Index(i))
|
||||
}
|
||||
dest.Set(v)
|
||||
case reflect.Ptr:
|
||||
v := reflect.New(dest.Type().Elem()).Elem()
|
||||
s := src
|
||||
if s.Elem().Kind() != reflect.Struct { // In the case of *interface{}, we want *struct{}
|
||||
s = s.Elem()
|
||||
}
|
||||
toRecursive(v, s)
|
||||
dest.Set(v.Addr())
|
||||
default:
|
||||
dest.Set(src)
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecursiveInterfacePtrValue(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.IsZero() {
|
||||
return v
|
||||
}
|
||||
return v.Elem().Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func vInt(v reflect.Value) (i int64) {
|
||||
if c := vConstantValue(v); c != nil {
|
||||
i, _ = constant.Int64Val(constant.ToInt(c))
|
||||
return i
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
i = v.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
@@ -520,7 +448,7 @@ func vUint(v reflect.Value) (i uint64) {
|
||||
i, _ = constant.Uint64Val(constant.ToInt(c))
|
||||
return i
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
i = uint64(v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
@@ -540,7 +468,7 @@ func vComplex(v reflect.Value) (c complex128) {
|
||||
img, _ := constant.Float64Val(constant.Imag(c))
|
||||
return complex(rel, img)
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
c = complex(float64(v.Int()), 0)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
@@ -558,7 +486,7 @@ func vFloat(v reflect.Value) (i float64) {
|
||||
i, _ = constant.Float64Val(constant.ToFloat(c))
|
||||
return i
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
i = float64(v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
|
||||
@@ -47,8 +47,12 @@ type _compress_flate_Reader struct {
|
||||
WReadByte func() (byte, error)
|
||||
}
|
||||
|
||||
func (W _compress_flate_Reader) Read(p []byte) (n int, err error) { return W.WRead(p) }
|
||||
func (W _compress_flate_Reader) ReadByte() (byte, error) { return W.WReadByte() }
|
||||
func (W _compress_flate_Reader) Read(p []byte) (n int, err error) {
|
||||
return W.WRead(p)
|
||||
}
|
||||
func (W _compress_flate_Reader) ReadByte() (byte, error) {
|
||||
return W.WReadByte()
|
||||
}
|
||||
|
||||
// _compress_flate_Resetter is an interface wrapper for Resetter type
|
||||
type _compress_flate_Resetter struct {
|
||||
@@ -56,4 +60,6 @@ type _compress_flate_Resetter struct {
|
||||
WReset func(r io.Reader, dict []byte) error
|
||||
}
|
||||
|
||||
func (W _compress_flate_Resetter) Reset(r io.Reader, dict []byte) error { return W.WReset(r, dict) }
|
||||
func (W _compress_flate_Resetter) Reset(r io.Reader, dict []byte) error {
|
||||
return W.WReset(r, dict)
|
||||
}
|
||||
|
||||
@@ -44,4 +44,6 @@ type _compress_zlib_Resetter struct {
|
||||
WReset func(r io.Reader, dict []byte) error
|
||||
}
|
||||
|
||||
func (W _compress_zlib_Resetter) Reset(r io.Reader, dict []byte) error { return W.WReset(r, dict) }
|
||||
func (W _compress_zlib_Resetter) Reset(r io.Reader, dict []byte) error {
|
||||
return W.WReset(r, dict)
|
||||
}
|
||||
|
||||
@@ -36,8 +36,18 @@ type _container_heap_Interface struct {
|
||||
WSwap func(i int, j int)
|
||||
}
|
||||
|
||||
func (W _container_heap_Interface) Len() int { return W.WLen() }
|
||||
func (W _container_heap_Interface) Less(i int, j int) bool { return W.WLess(i, j) }
|
||||
func (W _container_heap_Interface) Pop() interface{} { return W.WPop() }
|
||||
func (W _container_heap_Interface) Push(x interface{}) { W.WPush(x) }
|
||||
func (W _container_heap_Interface) Swap(i int, j int) { W.WSwap(i, j) }
|
||||
func (W _container_heap_Interface) Len() int {
|
||||
return W.WLen()
|
||||
}
|
||||
func (W _container_heap_Interface) Less(i int, j int) bool {
|
||||
return W.WLess(i, j)
|
||||
}
|
||||
func (W _container_heap_Interface) Pop() interface{} {
|
||||
return W.WPop()
|
||||
}
|
||||
func (W _container_heap_Interface) Push(x interface{}) {
|
||||
W.WPush(x)
|
||||
}
|
||||
func (W _container_heap_Interface) Swap(i int, j int) {
|
||||
W.WSwap(i, j)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,15 @@ type _context_Context struct {
|
||||
WValue func(key interface{}) interface{}
|
||||
}
|
||||
|
||||
func (W _context_Context) Deadline() (deadline time.Time, ok bool) { return W.WDeadline() }
|
||||
func (W _context_Context) Done() <-chan struct{} { return W.WDone() }
|
||||
func (W _context_Context) Err() error { return W.WErr() }
|
||||
func (W _context_Context) Value(key interface{}) interface{} { return W.WValue(key) }
|
||||
func (W _context_Context) Deadline() (deadline time.Time, ok bool) {
|
||||
return W.WDeadline()
|
||||
}
|
||||
func (W _context_Context) Done() <-chan struct{} {
|
||||
return W.WDone()
|
||||
}
|
||||
func (W _context_Context) Err() error {
|
||||
return W.WErr()
|
||||
}
|
||||
func (W _context_Context) Value(key interface{}) interface{} {
|
||||
return W.WValue(key)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user