Compare commits

...

53 Commits

Author SHA1 Message Date
Marc Vertes
23dfef0ac8 fix: define a correct zero value for an not initialized interface{} 2020-02-04 18:04:05 +01:00
Marc Vertes
4fd6a2dc56 fix: handle recursive type definition involving a map object 2020-02-04 16:36:05 +01:00
Marc Vertes
92a63dbe09 fix: handle out of order type declaration for global var declaration 2020-02-03 17:36:04 +01:00
Marc Vertes
e434892b9a fix: import different source packages with same base name 2020-02-03 17:22:04 +01:00
Marc Vertes
712891dd77 fix: allow reuse of _ symbol in the same scope 2020-02-03 17:08:05 +01:00
Marc Vertes
137b16580c fix: handle binary methods where receiver is implicit 2020-02-03 16:54:04 +01:00
Sven Dowideit
b057ada531 Add an example of yaegi in a shebang line 2020-01-15 16:54:06 +01:00
Camal Cakar
6d90c5a641 Case sensitivity fix 2020-01-15 16:40:06 +01:00
Marc Vertes
5381ee65d1 fix: continue statement was not applied correctly 2020-01-10 17:50:05 +01:00
Marc Vertes
f1cde2be0f fix: apply automatic type conversion to constant expressions (#484) 2020-01-09 18:01:44 +01:00
Marc Vertes
bb04af2d4d doc: document import of source and binary packages (#477) 2020-01-08 18:51:30 +01:00
Marc Vertes
9a8a88dcb9 fix: use branch operation in || and && operators, fix storage for ! (#476) 2020-01-07 17:27:22 +01:00
Marc Vertes
f3f54a5302 doc: add explanation about CLA in CONTRIBUTING.md (#483) 2020-01-07 16:30:05 +01:00
Marc Vertes
878fcc835c fix: add support for ^ and + as unary operators 2020-01-07 15:34:05 +01:00
Marc Vertes
a1f2d3bf1d fix: isNil was not forwarding result when used in a branch expression 2019-12-19 18:32:04 +01:00
Marc Vertes
3cd37645eb fix: correct isValueUntyped() to handle typed constants 2019-12-19 15:38:05 +01:00
Marc Vertes
e1ac83f7d8 fix: correct type extraction for returned value 2019-12-17 10:18:06 +01:00
Marc Vertes
4f93be7f19 fix: emulate struct by interface{} only for recursive struct types 2019-12-16 19:00:07 +01:00
Marc Vertes
7a0c09f5eb fix: detect untyped values when importing from binary packages 2019-12-13 11:18:04 +01:00
Marc Vertes
275391c1e8 fix: struct type detection, collision between field and type name 2019-12-12 14:40:05 +01:00
Marc Vertes
273df8af9f fix: improve interface type checks using method sets 2019-12-11 14:46:06 +01:00
Marc Vertes
0d2c39d155 fix: implicit import package name was not correctly generated 2019-12-11 11:54:05 +01:00
Marc Vertes
1ff1a50753 fix: add method checks for interface types 2019-12-09 18:24:04 +01:00
Marc Vertes
488e491bf8 fix: improve type switch clause with assign 2019-11-27 23:00:04 +01:00
Marc Vertes
eef59153d8 fix: detect non boolean condition for IF and FOR statements 2019-11-26 00:08:05 +01:00
Marc Vertes
d44e4af527 fix: handle selector expression in type switch 2019-11-25 23:52:03 +01:00
Marc Vertes
786ea366ab fix: handle nil function closure 2019-11-25 23:36:03 +01:00
Marc Vertes
e506969172 fix: correct handling of dynamic type for interface values 2019-11-25 23:20:04 +01:00
Marc Vertes
9f1f31210a fix: automatic type conversion when returning untyped value 2019-11-19 15:22:05 +01:00
Marc Vertes
56bec974e1 fix: handle index expression on binary string type 2019-11-19 15:06:05 +01:00
Marc Vertes
08a37fc4bf fix: handle type assertion from literal interface type 2019-11-19 14:50:06 +01:00
Marc Vertes
c5ec5e492f fix: assign a literal composite to an interface object 2019-11-19 14:34:05 +01:00
Marc Vertes
773147ef71 fix: properly align atomic counters 2019-11-08 00:34:04 +01:00
Marc Vertes
a6ecebab92 fix: automatic conversion of untyped literal float to int 2019-10-31 17:32:05 +01:00
Dan Kortschak
d893a7427e doc: fix spelling 2019-10-30 11:20:05 +01:00
Marc Vertes
3969ab16c4 fix: improve handling of untyped complex numbers 2019-10-29 18:14:05 +01:00
Dan Kortschak
714253c1e6 interp: add eval cancelation by semaphore 2019-10-29 16:18:04 +01:00
Marc Vertes
75a696a5c8 fix: goexports performs correct conversion on float32 constants 2019-10-29 10:20:05 +01:00
Marc Vertes
15686873e0 fix: assign binary func to func type var 2019-10-20 04:52:03 +02:00
Marc Vertes
ac504a2e8a fix: assign untyped value to typed var may require type conversion 2019-10-20 02:30:03 +02:00
Ludovic Fernandez
e193d95dc2 feat: update syscall for go1.12.12 2019-10-19 18:30:04 +02:00
Marc Vertes
7164a23664 fix: do not hide receiver type for method with anonymous receiver 2019-10-19 17:44:03 +02:00
Marc Vertes
0b4dcbf7bb feature: add support for custom build tags 2019-10-11 16:02:05 +02:00
Ludovic Fernandez
2765478137 chore: update linter to support go1.13 2019-10-09 14:14:04 +02:00
Marc Vertes
de5a6e1038 feature: rename exported func Repl into REPL 2019-10-08 23:54:04 +02:00
Dan Kortschak
398b0e0255 interp: use io.Reader and io.Writer for REPL parameters 2019-10-08 17:34:05 +02:00
Marc Vertes
4f95c27634 fix: generate closures for literal functions in CFG 2019-10-05 19:26:04 +02:00
Marc Vertes
7d19108f01 fix: compute type of slice expression globally 2019-10-05 19:14:04 +02:00
Ludovic Fernandez
1cf327bd7d Drop go1.11 2019-10-01 14:40:05 +02:00
Dan Kortschak
4bf4aeecbb interp: fix map range handling 2019-10-01 13:54:03 +02:00
Dan Kortschak
47923866ff interp: fix array size assignment type inference 2019-09-30 22:58:04 +02:00
Dan Kortschak
bb2921b42f interp: fix range expression handling 2019-09-30 22:44:04 +02:00
Marc Vertes
2c2b471cb9 fix: goexports to convert value type only for untyped constants 2019-09-27 15:44:05 +02:00
292 changed files with 3068 additions and 77309 deletions

View File

@@ -17,11 +17,11 @@ func main() {
Expected result:
```console
$ go run ./sample.go
// ouput
// output
```
Got:
```console
$ yaegi ./sample.go
// ouput
// output
```

View File

@@ -5,7 +5,7 @@
[linters-settings]
[linters-settings.govet]
check-shadowing = true
check-shadowing = false
[linters-settings.gocyclo]
min-complexity = 12.0
@@ -32,7 +32,11 @@
"gocyclo",
"gochecknoinits",
"gochecknoglobals",
"typecheck", # v1.17.1 and Go1.13 => bug
"wsl",
"godox",
"funlen",
"gocognit",
"stylecheck",
]
[issues]
@@ -41,10 +45,13 @@
max-same-issues = 0
exclude = []
[[issues.exclude-rules]]
path = "cmd/goexports/goexports.go"
text = "SA1019: importer.For is deprecated: use ForCompiler, which populates a FileSet with the positions of objects created by the importer."
[[issues.exclude-rules]]
path = "interp/.+_test\\.go"
linters = ["goconst"]
[[issues.exclude-rules]]
path = "interp/interp.go"
text = "`in` can be `io.Reader`"
[[issues.exclude-rules]]
path = "interp/interp.go"
text = "`out` can be `io.Writer`"

View File

@@ -19,7 +19,6 @@ cache:
matrix:
fast_finish: true
include:
- go: 1.11.x
- go: 1.12.x
- go: 1.13.x
env: STABLE=true

View File

@@ -13,7 +13,12 @@ discussions.
Once the proposal is approved, a Pull Request can be opened. If you want
to provide early visibility to reviewers, create a [Draft Pull Request].
We will also require you to sign the [Containous Contributor License Agreement]
after you submit your first pull request to this project. The link to sign the
agreement will be presented to you in the web interface of the pull request.
[Issues]: https://github.com/containous/yaegi/issues
[Pull Requests]: https://github.com/containous/yaegi/issues
[Feature Request]: https://github.com/containous/yaegi/issues/new?template=feature_request.md
[Draft Pull Request]: https://github.blog/2019-02-14-introducing-draft-pull-requests/
[Containous Contributor License Agreement]: https://cla-assistant.io/containous/yaegi

View File

@@ -18,5 +18,6 @@ generate: gen_all_syscall
tests:
GO111MODULE=off go test -v ./...
GO111MODULE=off go test -race ./interp
.PHONY: check gen_all_syscall gen_tests

View File

@@ -129,6 +129,24 @@ $ yaegi cmd/yaegi/yaegi.go
>
```
Or for Go scripting in the shebang line:
```console
$ cat /tmp/test
#!/usr/bin/env yaegi
package main
import "fmt"
func main() {
fmt.Println("test")
}
$ ls -la /tmp/test
-rwxr-xr-x 1 dow184 dow184 93 Jan 6 13:38 /tmp/test
$ /tmp/test
test
```
## Documentation
Documentation about Yaegi commands and libraries can be found at usual [godoc.org][docs].

12
_test/a33.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3}
b := a
fmt.Println(b)
}
// Output:
// [1 2 3]

12
_test/a34.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3}
var b [3]int = a
fmt.Println(b)
}
// Output:
// [1 2 3]

13
_test/a35.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3}
b := a
b[0] = -1
fmt.Println(a)
}
// Output:
// [1 2 3]

13
_test/a36.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3}
var b [3]int = a
b[0] = -1
fmt.Println(a)
}
// Output:
// [1 2 3]

11
_test/a37.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
a := [...]int{1, 2, 3}
fmt.Println(a)
}
// Output:
// [1 2 3]

11
_test/a38.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
a := [...]byte{}
fmt.Printf("%T\n", a)
}
// Output:
// [0]uint8

12
_test/a39.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
a := [...]byte{}
b := a
fmt.Printf("%T %T\n", a, b)
}
// Output:
// [0]uint8 [0]uint8

10
_test/assign10.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func main() {
var a uint
a = 1 + 2
println(a)
}
// Output:
// 3

7
_test/b1/foo/foo.go Normal file
View File

@@ -0,0 +1,7 @@
package foo
import bar "github.com/containous/yaegi/_test/b2/foo"
var Desc = "in b1/foo"
var Desc2 = Desc + bar.Desc

3
_test/b2/foo/foo.go Normal file
View File

@@ -0,0 +1,3 @@
package foo
var Desc = "in b2/foo"

13
_test/bin2.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Abs(-5))
}
// Output:
// 5

10
_test/closure8.go Normal file
View File

@@ -0,0 +1,10 @@
package main
var f = func(a int) int { return 2 + a }
func main() {
println(f(3))
}
// Output:
// 5

12
_test/complex1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
var c complex128
c = 1
fmt.Printf("%T %v\n", c, c)
}
// Output:
// complex128 (1+0i)

12
_test/complex2.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
c := complex(1, 0)
c += 1
fmt.Printf("%T %v\n", c, c)
}
// Output:
// complex128 (2+0i)

11
_test/complex3.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
var s int = 1 + complex(1, 0)
fmt.Printf("%T %v\n", s, s)
}
// Output
// int 2

12
_test/composite3.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
var err error
var ok bool
_, ok = err.(interface{ IsSet() bool })
println(ok)
}
// Output:
// false

11
_test/composite4.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func main() {
var err error
_, ok := err.(interface{ IsSet() bool })
println(ok)
}
// Output:
// false

3
_test/ct/ct1.go Normal file
View File

@@ -0,0 +1,3 @@
package ct
func init() { println("hello from ct1") }

5
_test/ct/ct2.go Normal file
View File

@@ -0,0 +1,5 @@
// +build !dummy
package ct
func init() { println("hello from ct2") }

5
_test/ct/ct3.go Normal file
View File

@@ -0,0 +1,5 @@
// +build dummy
package ct
func init() { println("hello from ct3") }

3
_test/foo-bar/foo-bar.go Normal file
View File

@@ -0,0 +1,3 @@
package bar
var Name = "foo-bar"

9
_test/for7.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
for i := 0; i; {
}
}
// Error:
// 4:14: non-bool used as for condition

18
_test/for8.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
for i := 0; i < 4; i++ {
for {
break
}
if i == 1 {
continue
}
println(i)
}
}
// Output:
// 0
// 2
// 3

19
_test/fun10.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import "fmt"
func f() func() {
return nil
}
func main() {
g := f()
fmt.Printf("%T %v\n", g, g)
if g == nil {
fmt.Println("nil func")
}
}
// Output:
// func() <nil>
// nil func

12
_test/fun11.go Normal file
View File

@@ -0,0 +1,12 @@
package main
var f F
type F func(int)
func main() {
println("ok")
}
// Output:
// ok

13
_test/if2.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "fmt"
func main() {
var i int
if i % 1000000 {
fmt.Println("oops")
}
}
// Error:
// 7:5: non-bool used as if condition

10
_test/import7.go Normal file
View File

@@ -0,0 +1,10 @@
package main
import "github.com/containous/yaegi/_test/foo-bar"
func main() {
println(bar.Name)
}
// Output:
// foo-bar

10
_test/import8.go Normal file
View File

@@ -0,0 +1,10 @@
package main
import "github.com/containous/yaegi/_test/b1/foo"
func main() {
println(foo.Desc)
}
// Output:
// in b1/foo

18
_test/interface12.go Normal file
View File

@@ -0,0 +1,18 @@
package main
type I1 interface {
Truc()
}
type T1 struct{}
func (T1) Truc() { println("in T1 truc") }
var x I1 = T1{}
func main() {
x.Truc()
}
// Output:
// in T1 truc

32
_test/interface13.go Normal file
View File

@@ -0,0 +1,32 @@
package main
import (
"fmt"
)
type X struct{}
func (X) Foo() int {
return 1
}
func (X) Bar() int {
return 2
}
type Foo interface {
Foo() int
}
type Bar interface {
Bar() int
}
func main() {
var x X
var i Foo = x
j := i.(Bar)
fmt.Println(j.Bar())
}
// Output:
// 2

17
_test/interface14.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type T struct{}
func (t *T) Error() string { return "T: error" }
var invalidT = &T{}
func main() {
var err error
if err != invalidT {
println("ok")
}
}
// Output:
// ok

28
_test/interface15.go Normal file
View File

@@ -0,0 +1,28 @@
package main
type Fooer interface {
Foo() string
}
type Barer interface {
//fmt.Stringer
Fooer
Bar()
}
type T struct{}
func (t *T) Foo() string { return "T: foo" }
func (*T) Bar() { println("in bar") }
var t = &T{}
func main() {
var f Barer
if f != t {
println("ok")
}
}
// Output:
// ok

25
_test/interface16.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import "fmt"
type Barer interface {
fmt.Stringer
Bar()
}
type T struct{}
func (*T) String() string { return "T: nothing" }
func (*T) Bar() { println("in bar") }
var t = &T{}
func main() {
var f Barer
if f != t {
println("ok")
}
}
// Output:
// ok

17
_test/interface17.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type T struct{}
func (t T) Error() string { return "T: error" }
var invalidT = T{}
func main() {
var err error
if err != invalidT {
println("ok")
}
}
// Output:
// ok

18
_test/interface18.go Normal file
View File

@@ -0,0 +1,18 @@
package main
type T struct{}
func (t *T) Error() string { return "T: error" }
func (*T) Foo() { println("foo") }
var invalidT = &T{}
func main() {
var err error
if err != invalidT {
println("ok")
}
}
// Output:
// ok

12
_test/interface19.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
var I interface{}
func main() {
fmt.Printf("%T %v\n", I, I)
}
// Output:
// <nil> <nil>

16
_test/map19.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import "fmt"
type cmap struct {
servers map[int64]*server
}
type server struct {
cm *cmap
}
func main() {
m := cmap{}
fmt.Println(m)
}

13
_test/math1.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "math"
func main() {
var f float32
if f < math.MaxFloat32 {
println("ok")
}
}
// Output:
// ok

18
_test/method28.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import "fmt"
type T struct {
Name string
}
func (T) create() *T {
return &T{"Hello"}
}
func main() {
fmt.Println(T{}.create())
}
// Output:
// &{Hello}

16
_test/method29.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"context"
"net"
)
var lookupHost = net.DefaultResolver.LookupHost
func main() {
res, err := lookupHost(context.Background(), "localhost")
println(len(res) > 0, err == nil)
}
// Output:
// true true

20
_test/method30.go Normal file
View File

@@ -0,0 +1,20 @@
package main
type T struct {
Name string
}
func (t *T) foo(a string) string {
return t.Name + a
}
var g = &T{"global"}
var f = g.foo
func main() {
println(f("-x"))
}
// Output:
// global-x

15
_test/nil0.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
func f() (host, port string, err error) {
return "", "", nil
}
func main() {
h, p, err := f()
fmt.Println(h, p, err)
}
// Output:
// <nil>

12
_test/nil1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
var a error = nil
if a == nil || a.Error() == "nil" {
println("a is nil")
}
}
// Output:
// a is nil

9
_test/op4.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
i := 100
println(i % 1e2)
}
// Output:
// 0

9
_test/or0.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
c := false
println(c || !c)
}
// Output:
// true

9
_test/or1.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
c := false
println(!c || c)
}
// Output:
// true

26
_test/primes.go Normal file
View File

@@ -0,0 +1,26 @@
package main
func Primes(n int) int {
var xs []int
for i := 2; len(xs) < n; i++ {
ok := true
for _, x := range xs {
if i%x == 0 {
ok = false
break
}
}
if !ok {
continue
}
xs = append(xs, i)
}
return xs[n-1]
}
func main() {
println(Primes(3))
}
// Output:
// 5

14
_test/range0.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
v := []int{1, 2, 3}
for i := range v {
v = append(v, i)
}
fmt.Println(v)
}
// Output:
// [1 2 3 0 1 2]

14
_test/range1.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
a := [...]int{2, 1, 0}
for _, v := range a {
a[v] = v
}
fmt.Println(a)
}
// Output:
// [0 1 2]

14
_test/range2.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
a := [...]int{2, 1, 0}
for _, v := range &a {
a[v] = v
}
fmt.Println(a)
}
// Output:
// [2 1 2]

14
_test/range3.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
m := map[int]bool{1: true, 3: true, 5: true}
for k := range m {
m[k*2] = true
}
fmt.Println("ok")
}
// Output:
// ok

15
_test/range4.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
func main() {
m := map[int]bool{1: true, 3: true, 5: true}
for _, v := range m {
fmt.Println(v)
}
}
// Output:
// true
// true
// true

15
_test/range5.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
func main() {
m := map[int]bool{1: true, 3: true, 5: true}
var n int
for range m {
n++
}
fmt.Println(n)
}
// Output:
// 3

18
_test/range6.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"fmt"
"math"
)
func main() {
m := map[float64]bool{math.NaN(): true, math.NaN(): true, math.NaN(): true}
for _, v := range m {
fmt.Println(v)
}
}
// Output:
// true
// true
// true

11
_test/ret7.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func one() uint {
return 1
}
func main() {
println(one())
}
// Output:
// 1

11
_test/str3.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "strconv"
func main() {
str := strconv.Itoa(101)
println(str[0] == '1')
}
// Output:
// true

11
_test/str4.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "unicode/utf8"
func main() {
r, _ := utf8.DecodeRuneInString("Hello")
println(r < utf8.RuneSelf)
}
// Output:
// true

19
_test/struct29.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type T1 struct {
A []T2
B []T2
}
type T2 struct {
name string
}
var t = T1{}
func main() {
println("ok")
}
// Output:
// ok

19
_test/struct30.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type T1 struct {
A []T2
M map[uint64]T2
}
type T2 struct {
name string
}
var t = T1{}
func main() {
println("ok")
}
// Output:
// ok

17
_test/switch21.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
func main() {
var err error
switch v := err.(type) {
case fmt.Formatter:
println("formatter")
default:
fmt.Println(v)
}
}
// Output:
// <nil>

21
_test/switch22.go Normal file
View File

@@ -0,0 +1,21 @@
package main
type T struct {
Name string
}
func f(t interface{}) {
switch ext := t.(type) {
case *T:
println("*T", ext.Name)
default:
println("unknown")
}
}
func main() {
f(&T{"truc"})
}
// Output:
// *T truc

15
_test/tag0.go Normal file
View File

@@ -0,0 +1,15 @@
// The following comment line has the same effect as 'go run -tags=dummy'
//yaegi:tags dummy
package main
import _ "github.com/containous/yaegi/_test/ct"
func main() {
println("bye")
}
// Output:
// hello from ct1
// hello from ct3
// bye

13
_test/time10.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "time"
var UnixTime func(int64, int64) time.Time
func main() {
UnixTime = time.Unix
println(UnixTime(1e9, 0).In(time.UTC).Minute())
}
// Output:
// 46

15
_test/time11.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"fmt"
"time"
)
const df = time.Minute * 30
func main() {
fmt.Printf("df: %v %T\n", df, df)
}
// Output:
// df: 30m0s time.Duration

13
_test/time9.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println((5 * time.Minute).Seconds())
}
// Output:
// 300

11
_test/var10.go Normal file
View File

@@ -0,0 +1,11 @@
package main
var _ = true
var _ = "hello"
func main() {
println("hello")
}
// Output:
// hello

10
_test/var11.go Normal file
View File

@@ -0,0 +1,10 @@
package main
var a, _, _, b = 1, true, "foo", 2
func main() {
println(a, b)
}
// Output:
// 1 2

11
_test/var9.go Normal file
View File

@@ -0,0 +1,11 @@
package main
var a = "sdofjsdfj"
var z = a[0:2]
func main() {
println(z)
}
// Output:
// sd

View File

@@ -1,7 +1,7 @@
//go:generate go build
/*
Goexports generates wrappers of package exported symbols
Goexports generates wrappers of package exported symbols.
Output files are written in the current directory, and prefixed with the go version.
@@ -26,6 +26,7 @@ import (
"go/constant"
"go/format"
"go/importer"
"go/token"
"go/types"
"io/ioutil"
"log"
@@ -114,7 +115,7 @@ type Wrap struct {
}
func genContent(dest, pkgName, license string) ([]byte, error) {
p, err := importer.For("source", nil).Import(pkgName)
p, err := importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgName)
if err != nil {
return nil, err
}
@@ -147,7 +148,12 @@ func genContent(dest, pkgName, license string) ([]byte, error) {
pname := path.Base(pkgName) + "." + name
switch o := o.(type) {
case *types.Const:
val[name] = Val{fixConst(pname, o.Val()), false}
if b, ok := o.Type().(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 {
// convert untyped constant to right type to avoid overflow
val[name] = Val{fixConst(pname, o.Val()), false}
} else {
val[name] = Val{pname, false}
}
case *types.Func:
val[name] = Val{pname, false}
case *types.Var:
@@ -248,7 +254,14 @@ func genContent(dest, pkgName, license string) ([]byte, error) {
// fixConst checks untyped constant value, converting it if necessary to avoid overflow
func fixConst(name string, val constant.Value) string {
if val.Kind() == constant.Int {
switch val.Kind() {
case constant.Float:
str := val.ExactString()
if _, err := strconv.ParseFloat(str, 32); err == nil {
return "float32(" + name + ")"
}
return name
case constant.Int:
str := val.ExactString()
i, err := strconv.ParseInt(str, 0, 64)
if err == nil {
@@ -265,8 +278,10 @@ func fixConst(name string, val constant.Value) string {
if err == nil {
return "uint64(" + name + ")"
}
return name
default:
return name
}
return name
}
// genLicense generates the correct LICENSE header text from the provided

View File

@@ -8,7 +8,7 @@ If invoked with no arguments, it processes the standard input
in a Read-Eval-Print-Loop. A prompt is displayed if standard input
is a terminal.
Given a file, it operates on that file. if the first line starts with
Given a file, it operates on that file. If the first line starts with
"#!/usr/bin/env yaegi", and the file has exec permission, then the file
can be invoked directly from the shell.
@@ -18,7 +18,10 @@ at global level in an implicit main package.
Options:
-i
start an interactive REPL after file execution
start an interactive REPL after file execution.
-tags tag,list
a comma-separated list of build tags to consider satisfied during
the interpretation.
Debugging support (may be removed at any time):
YAEGI_AST_DOT=1
@@ -43,7 +46,9 @@ import (
func main() {
var interactive bool
var tags string
flag.BoolVar(&interactive, "i", false, "start an interactive REPL")
flag.StringVar(&tags, "tags", "", "set a list of build tags")
flag.Usage = func() {
fmt.Println("Usage:", os.Args[0], "[options] [script] [args]")
fmt.Println("Options:")
@@ -53,7 +58,7 @@ func main() {
args := flag.Args()
log.SetFlags(log.Lshortfile)
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
i.Use(stdlib.Symbols)
i.Use(interp.Symbols)
@@ -79,9 +84,9 @@ func main() {
}
if interactive {
i.Repl(os.Stdin, os.Stdout)
i.REPL(os.Stdin, os.Stdout)
}
} else {
i.Repl(os.Stdin, os.Stdout)
i.REPL(os.Stdin, os.Stdout)
}
}

91
cmd/yaegi/yaegi_test.go Normal file
View File

@@ -0,0 +1,91 @@
package main
import (
"bytes"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"
"time"
)
func TestYaegiCmdCancel(t *testing.T) {
tmp, err := ioutil.TempDir("", "yaegi-")
if err != nil {
t.Fatalf("failed to create tmp directory: %v", err)
}
defer func() {
err = os.RemoveAll(tmp)
if err != nil {
t.Errorf("failed to clean up %v: %v", tmp, err)
}
}()
yaegi := filepath.Join(tmp, "yaegi")
build := exec.Command("go", "build", "-race", "-o", yaegi, ".")
err = build.Run()
if err != nil {
t.Fatalf("failed to build yaegi command: %v", err)
}
// Test src must be terminated by a single newline.
tests := []string{
"for {}\n",
"select {}\n",
}
for _, src := range tests {
cmd := exec.Command(yaegi)
in, err := cmd.StdinPipe()
if err != nil {
t.Errorf("failed to get stdin pipe to yaegi command: %v", err)
}
var outBuf, errBuf bytes.Buffer
cmd.Stdout = &outBuf
cmd.Stderr = &errBuf
// https://golang.org/doc/articles/race_detector.html#Options
cmd.Env = []string{`GORACE="halt_on_error=1"`}
err = cmd.Start()
if err != nil {
t.Fatalf("failed to start yaegi command: %v", err)
}
_, err = in.Write([]byte(src))
if err != nil {
t.Errorf("failed pipe test source to yaegi command: %v", err)
}
time.Sleep(100 * time.Millisecond)
err = cmd.Process.Signal(os.Interrupt)
if err != nil {
t.Errorf("failed to send os.Interrupt to yaegi command: %v", err)
}
_, err = in.Write([]byte("1+1\n"))
if err != nil {
t.Errorf("failed to probe race: %v", err)
}
err = in.Close()
if err != nil {
t.Errorf("failed to close stdin pipe: %v", err)
}
err = cmd.Wait()
if err != nil {
if cmd.ProcessState.ExitCode() == 66 { // See race_detector.html article.
t.Errorf("race detected running yaegi command canceling %q: %v", src, err)
if testing.Verbose() {
t.Log(&errBuf)
}
} else {
t.Errorf("error running yaegi command for %q: %v", src, err)
}
continue
}
if outBuf.String() != "context canceled\n2\n" {
t.Errorf("unexpected output: %q", &outBuf)
}
}
}

View File

@@ -69,7 +69,6 @@ func TestPackages(t *testing.T) {
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
goPath, err := filepath.Abs(test.goPath)
if err != nil {
t.Fatal(err)
@@ -116,7 +115,6 @@ func TestPackagesError(t *testing.T) {
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
// Init go interpreter
i := interp.New(interp.Options{GoPath: test.goPath})
i.Use(stdlib.Symbols) // Use binary standard library

2
go.mod
View File

@@ -1,3 +1,3 @@
module github.com/containous/yaegi
go 1.11
go 1.12

View File

@@ -10,7 +10,7 @@ import (
const model = `package interp
// Code generated by 'go run ../cmd/genop/genop.go'. DO NOT EDIT.
// Code generated by 'go run ../internal/genop/genop.go'. DO NOT EDIT.
import "reflect"
@@ -352,6 +352,30 @@ func {{$name}}(n *node) {
}
}
{{end}}
{{range $name, $op := .Unary}}
func {{$name}}Const(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
{{- if $op.Bool}}
n.rval.SetBool({{$op.Name}} v.Bool())
{{- else}}
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt({{$op.Name}} v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint({{$op.Name}} v.Uint())
{{- if $op.Float}}
case reflect.Float32, reflect.Float64:
n.rval.SetFloat({{$op.Name}} v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex({{$op.Name}} v.Complex())
{{- end}}
}
{{- end}}
}
{{end}}
{{range $name, $op := .Comparison}}
func {{$name}}(n *node) {
tnext := getExec(n.tnext)
@@ -427,148 +451,6 @@ func {{$name}}(n *node) {
}
}
}
{{- if $op.Complex}}
case isComplex(t0) || isComplex(t1):
switch {
case c0.rval.IsValid():
s0 := vComplex(c0.rval)
v1 := genValueComplex(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, s1 := v1(f)
if s0 {{$op.Name}} s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
_, s1 := v1(f)
dest(f).SetBool(s0 {{$op.Name}} s1)
return tnext
}
}
case c1.rval.IsValid():
s1 := vComplex(c1.rval)
v0 := genValueComplex(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
if s0 {{$op.Name}} s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
dest(f).SetBool(s0 {{$op.Name}} s1)
return tnext
}
}
default:
v0 := genValueComplex(n.child[0])
v1 := genValueComplex(n.child[1])
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
_, s1 := v1(f)
if s0 {{$op.Name}} s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
_, s1 := v1(f)
dest(f).SetBool(s0 {{$op.Name}} s1)
return tnext
}
}
}
default:
switch {
case c0.rval.IsValid():
i0 := c0.rval.Interface()
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
if i0 {{$op.Name}} i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
case c1.rval.IsValid():
i1 := c1.rval.Interface()
v0 := genValue(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
if i0 {{$op.Name}} i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
default:
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 {{$op.Name}} i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
}
{{- end}}
case isFloat(t0) || isFloat(t1):
switch {
case c0.rval.IsValid():
@@ -781,6 +663,148 @@ func {{$name}}(n *node) {
}
}
}
{{- if $op.Complex}}
case isComplex(t0) || isComplex(t1):
switch {
case c0.rval.IsValid():
s0 := vComplex(c0.rval)
v1 := genComplex(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
s1 := v1(f)
if s0 {{$op.Name}} s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
s1 := v1(f)
dest(f).SetBool(s0 {{$op.Name}} s1)
return tnext
}
}
case c1.rval.IsValid():
s1 := vComplex(c1.rval)
v0 := genComplex(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
s0 := v0(f)
if s0 {{$op.Name}} s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
s0 := v0(f)
dest(f).SetBool(s0 {{$op.Name}} s1)
return tnext
}
}
default:
v0 := genComplex(n.child[0])
v1 := genComplex(n.child[1])
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
s0 := v0(f)
s1 := v1(f)
if s0 {{$op.Name}} s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
s0 := v0(f)
s1 := v1(f)
dest(f).SetBool(s0 {{$op.Name}} s1)
return tnext
}
}
}
default:
switch {
case c0.rval.IsValid():
i0 := c0.rval.Interface()
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
if i0 {{$op.Name}} i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
case c1.rval.IsValid():
i1 := c1.rval.Interface()
v0 := genValue(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
if i0 {{$op.Name}} i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
default:
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 {{$op.Name}} i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
}
{{- end}}
}
}
{{end}}
@@ -793,6 +817,7 @@ type Op struct {
Float bool // true if operator applies to float
Complex bool // true if operator applies to complex
Shift bool // true if operator is a shift operation
Bool bool // true if operator applies to bool
}
func main() {
@@ -805,17 +830,17 @@ func main() {
b := &bytes.Buffer{}
data := map[string]interface{}{
"Arithmetic": map[string]Op{
"add": {"+", true, true, true, false},
"sub": {"-", false, true, true, false},
"mul": {"*", false, true, true, false},
"quo": {"/", false, true, true, false},
"rem": {"%", false, false, false, false},
"shl": {"<<", false, false, false, true},
"shr": {">>", false, false, false, true},
"and": {"&", false, false, false, false},
"or": {"|", false, false, false, false},
"xor": {"^", false, false, false, false},
"andNot": {"&^", false, false, false, false},
"add": {"+", true, true, true, false, false},
"sub": {"-", false, true, true, false, false},
"mul": {"*", false, true, true, false, false},
"quo": {"/", false, true, true, false, false},
"rem": {"%", false, false, false, false, false},
"shl": {"<<", false, false, false, true, false},
"shr": {">>", false, false, false, true, false},
"and": {"&", false, false, false, false, false},
"or": {"|", false, false, false, false, false},
"xor": {"^", false, false, false, false, false},
"andNot": {"&^", false, false, false, false, false},
},
"IncDec": map[string]Op{
"inc": {Name: "+"},
@@ -829,6 +854,12 @@ func main() {
"lowerEqual": {Name: "<=", Complex: false},
"notEqual": {Name: "!=", Complex: true},
},
"Unary": map[string]Op{
"not": {Name: "!", Float: false, Bool: true},
"neg": {Name: "-", Float: true, Bool: false},
"pos": {Name: "+", Float: true, Bool: false},
"bitNot": {Name: "^", Float: false, Bool: false},
},
}
if err = parse.Execute(b, data); err != nil {
log.Fatal(err)

View File

@@ -6,8 +6,10 @@ import (
"go/parser"
"go/scanner"
"go/token"
"math"
"reflect"
"strconv"
"sync/atomic"
)
// nkind defines the kind of AST, i.e. the grammar category
@@ -197,6 +199,7 @@ const (
aAndAssign
aAndNot
aAndNotAssign
aBitNot
aCall
aCase
aCompositeLit
@@ -215,11 +218,12 @@ const (
aMethod
aMul
aMulAssign
aNegate
aNeg
aNot
aNotEqual
aOr
aOrAssign
aPos
aQuo
aQuoAssign
aRange
@@ -253,6 +257,7 @@ var actions = [...]string{
aAndAssign: "&=",
aAndNot: "&^",
aAndNotAssign: "&^=",
aBitNot: "^",
aCall: "call",
aCase: "case",
aCompositeLit: "compositeLit",
@@ -269,11 +274,12 @@ var actions = [...]string{
aMethod: "Method",
aMul: "*",
aMulAssign: "*=",
aNegate: "-",
aNeg: "-",
aNot: "!",
aNotEqual: "!=",
aOr: "|",
aOrAssign: "|=",
aPos: "+",
aQuo: "/",
aQuoAssign: "/=",
aRange: "range",
@@ -320,6 +326,7 @@ func (interp *Interpreter) firstToken(src string) token.Token {
func (interp *Interpreter) ast(src, name string) (string, *node, error) {
inRepl := name == ""
var inFunc bool
var mode parser.Mode
// Allow incremental parsing of declarations or statements, by inserting
// them in a pseudo file package or function. Those statements or
@@ -334,26 +341,30 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
inFunc = true
src = "package main; func main() {" + src + "}"
}
// Parse comments in REPL mode, to allow tag setting
mode |= parser.ParseComments
}
if ok, err := interp.buildOk(interp.context, name, src); !ok || err != nil {
if ok, err := interp.buildOk(&interp.context, name, src); !ok || err != nil {
return "", nil, err // skip source not matching build constraints
}
f, err := parser.ParseFile(interp.fset, name, src, 0)
f, err := parser.ParseFile(interp.fset, name, src, mode)
if err != nil {
return "", nil, err
}
setYaegiTags(&interp.context, f.Comments)
var root *node
var anc astNode
var st nodestack
var pkgName string
addChild := func(root **node, anc astNode, pos token.Pos, kind nkind, act action) *node {
interp.nindex++
var i interface{}
n := &node{anc: anc.node, interp: interp, index: interp.nindex, pos: pos, kind: kind, action: act, val: &i, gen: builtin[act]}
nindex := atomic.AddInt64(&interp.nindex, 1)
n := &node{anc: anc.node, interp: interp, index: nindex, pos: pos, kind: kind, action: act, val: &i, gen: builtin[act]}
n.start = n
if anc.node == nil {
*root = n
@@ -364,15 +375,15 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
if len(ancAst.List)+len(ancAst.Body) == len(anc.node.child) {
// All case clause children are collected.
// Split children in condition and body nodes to desambiguify the AST.
interp.nindex++
body := &node{anc: anc.node, interp: interp, index: interp.nindex, pos: pos, kind: caseBody, action: aNop, val: &i, gen: nop}
nindex = atomic.AddInt64(&interp.nindex, 1)
body := &node{anc: anc.node, interp: interp, index: nindex, pos: pos, kind: caseBody, action: aNop, val: &i, gen: nop}
if ts := anc.node.anc.anc; ts.kind == typeSwitch && ts.child[1].action == aAssign {
// In type switch clause, if a switch guard is assigned, duplicate the switch guard symbol
// in each clause body, so a different guard type can be set in each clause
name := ts.child[1].child[0].ident
interp.nindex++
gn := &node{anc: body, interp: interp, ident: name, index: interp.nindex, pos: pos, kind: identExpr, action: aNop, val: &i, gen: nop}
nindex = atomic.AddInt64(&interp.nindex, 1)
gn := &node{anc: body, interp: interp, ident: name, index: nindex, pos: pos, kind: identExpr, action: aNop, val: &i, gen: nop}
body.child = append(body.child, gn)
}
@@ -459,7 +470,11 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
n.rval = reflect.ValueOf(v)
case token.FLOAT:
v, _ := strconv.ParseFloat(a.Value, 64)
n.rval = reflect.ValueOf(v)
if math.Trunc(v) == v {
n.rval = reflect.ValueOf(int(v))
} else {
n.rval = reflect.ValueOf(v)
}
case token.IMAG:
v, _ := strconv.ParseFloat(a.Value[:len(a.Value)-1], 64)
n.rval = reflect.ValueOf(complex(0, v))
@@ -548,6 +563,9 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
case *ast.CommClause:
st.push(addChild(&root, anc, pos, commClause, aNop), nod)
case *ast.CommentGroup:
return false
case *ast.CompositeLit:
st.push(addChild(&root, anc, pos, compositeLitExpr, aCompositeLit), nod)
@@ -757,6 +775,8 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
var kind = unaryExpr
var act action
switch a.Op {
case token.ADD:
act = aPos
case token.AND:
kind = addressExpr
act = aAddr
@@ -765,7 +785,9 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
case token.NOT:
act = aNot
case token.SUB:
act = aNegate
act = aNeg
case token.XOR:
act = aBitNot
}
st.push(addChild(&root, anc, pos, kind, act), nod)
@@ -839,9 +861,9 @@ func (s *nodestack) top() astNode {
// dup returns a duplicated node subtree
func (interp *Interpreter) dup(nod, anc *node) *node {
interp.nindex++
nindex := atomic.AddInt64(&interp.nindex, 1)
n := *nod
n.index = interp.nindex
n.index = nindex
n.anc = anc
n.start = &n
n.pos = anc.pos

View File

@@ -1,6 +1,7 @@
package interp
import (
"go/ast"
"go/build"
"go/parser"
"path"
@@ -11,7 +12,7 @@ import (
// buildOk returns true if a file or script matches build constraints
// as specified in https://golang.org/pkg/go/build/#hdr-Build_Constraints.
// An error from parser is returned as well.
func (interp *Interpreter) buildOk(ctx build.Context, name, src string) (bool, error) {
func (interp *Interpreter) buildOk(ctx *build.Context, name, src string) (bool, error) {
// Extract comments before the first clause
f, err := parser.ParseFile(interp.fset, name, src, parser.PackageClauseOnly|parser.ParseComments)
if err != nil {
@@ -25,12 +26,13 @@ func (interp *Interpreter) buildOk(ctx build.Context, name, src string) (bool, e
}
}
}
setYaegiTags(ctx, f.Comments)
return true, nil
}
// buildLineOk returns true if line is not a build constraint or
// if build constraint is satisfied
func buildLineOk(ctx build.Context, line string) (ok bool) {
func buildLineOk(ctx *build.Context, line string) (ok bool) {
if len(line) < 7 || line[:7] != "+build " {
return true
}
@@ -45,7 +47,7 @@ func buildLineOk(ctx build.Context, line string) (ok bool) {
}
// buildOptionOk return true if all comma separated tags match, false otherwise
func buildOptionOk(ctx build.Context, tag string) bool {
func buildOptionOk(ctx *build.Context, tag string) bool {
// in option, evaluate the AND of individual tags
for _, t := range strings.Split(tag, ",") {
if !buildTagOk(ctx, t) {
@@ -57,7 +59,7 @@ func buildOptionOk(ctx build.Context, tag string) bool {
// buildTagOk returns true if a build tag matches, false otherwise
// if first character is !, result is negated
func buildTagOk(ctx build.Context, s string) (r bool) {
func buildTagOk(ctx *build.Context, s string) (r bool) {
not := s[0] == '!'
if not {
s = s[1:]
@@ -82,6 +84,25 @@ func buildTagOk(ctx build.Context, s string) (r bool) {
return
}
// setYaegiTags scans a comment group for "yaegi:tags tag1 tag2 ..." lines
// and adds the corresponding tags to the interpreter build tags.
func setYaegiTags(ctx *build.Context, comments []*ast.CommentGroup) {
for _, g := range comments {
for _, line := range strings.Split(strings.TrimSpace(g.Text()), "\n") {
if len(line) < 11 || line[:11] != "yaegi:tags " {
continue
}
tags := strings.Split(strings.TrimSpace(line[10:]), " ")
for _, tag := range tags {
if !contains(ctx.BuildTags, tag) {
ctx.BuildTags = append(ctx.BuildTags, tag)
}
}
}
}
}
func contains(tags []string, tag string) bool {
for _, t := range tags {
if t == tag {
@@ -92,7 +113,7 @@ func contains(tags []string, tag string) bool {
}
// goMinorVersion returns the go minor version number
func goMinorVersion(ctx build.Context) int {
func goMinorVersion(ctx *build.Context) int {
current := ctx.ReleaseTags[len(ctx.ReleaseTags)-1]
v := strings.Split(current, ".")
@@ -108,7 +129,7 @@ func goMinorVersion(ctx build.Context) int {
}
// skipFile returns true if file should be skipped
func skipFile(ctx build.Context, p string) bool {
func skipFile(ctx *build.Context, p string) bool {
if !strings.HasSuffix(p, ".go") {
return true
}

View File

@@ -44,7 +44,7 @@ func TestBuildTag(t *testing.T) {
test := test
src := test.src + "\npackage x"
t.Run(test.src, func(t *testing.T) {
if r, _ := i.buildOk(ctx, "", src); r != test.res {
if r, _ := i.buildOk(&ctx, "", src); r != test.res {
t.Errorf("got %v, want %v", r, test.res)
}
})
@@ -74,7 +74,7 @@ func TestBuildFile(t *testing.T) {
for _, test := range tests {
test := test
t.Run(test.src, func(t *testing.T) {
if r := skipFile(ctx, test.src); r != test.res {
if r := skipFile(&ctx, test.src); r != test.res {
t.Errorf("got %v, want %v", r, test.res)
}
})
@@ -106,7 +106,7 @@ func Test_goMinorVersion(t *testing.T) {
for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
minor := goMinorVersion(test.context)
minor := goMinorVersion(&test.context)
if minor != test.expected {
t.Errorf("got %v, want %v", minor, test.expected)

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"log"
"math"
"path"
"reflect"
"regexp"
"unicode"
)
@@ -24,15 +24,26 @@ var constOp = map[action]func(*node){
aShr: shrConst,
aAndNot: andNotConst,
aXor: xorConst,
aNot: notConst,
aBitNot: bitNotConst,
aNeg: negConst,
aPos: posConst,
}
var constBltn = map[string]func(*node){
"complex": complexConst,
"imag": imagConst,
"real": realConst,
}
var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
// cfg generates a control flow graph (CFG) from AST (wiring successors in AST)
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
// variables. A list of nodes of init functions is returned.
// Following this pass, the CFG is ready to run
func (interp *Interpreter) cfg(root *node) ([]*node, error) {
sc, pkgName := interp.initScopePkg(root)
var loop, loopRestart *node
func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
sc := interp.initScopePkg(pkgID)
var initNodes []*node
var iotaValue int
var err error
@@ -77,14 +88,18 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
ktyp = &itype{cat: valueT, rtype: typ.Key()}
vtyp = &itype{cat: valueT, rtype: typ.Elem()}
case reflect.String:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int")
vtyp = sc.getType("rune")
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()}
}
case mapT:
n.anc.gen = rangeMap
ityp := &itype{cat: valueT, rtype: reflect.TypeOf((*reflect.MapIter)(nil))}
sc.add(ityp)
ktyp = o.typ.key
vtyp = o.typ.val
case ptrT:
@@ -96,9 +111,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
vtyp = vtyp.val
}
case stringT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int")
vtyp = sc.getType("rune")
case arrayT, variadicT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int")
vtyp = o.typ.val
}
@@ -158,19 +175,21 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
var typ *itype
if len(n.child) == 2 {
// 1 type in clause: define the var with this type in the case clause scope
switch sym, _, ok := sc.lookup(n.child[0].ident); {
case ok && sym.kind == typeSym:
typ = sym.typ
switch {
case n.child[0].ident == "nil":
typ = sc.getType("interface{}")
default:
case !n.child[0].isType(sc):
err = n.cfgErrorf("%s is not a type", n.child[0].ident)
return false
default:
typ, err = nodeType(interp, sc, n.child[0])
}
} else {
// define the var with the type in the switch guard expression
typ = sn.child[1].child[1].child[0].typ
}
if err != nil {
return false
}
nod := n.lastChild().child[0]
index := sc.add(typ)
sc.sym[nod.ident] = &symbol{index: index, kind: varSym, typ: typ}
@@ -216,12 +235,12 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
}
case forStmt0, forRangeStmt:
loop, loopRestart = n, n.child[0]
sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.child[0]
case forStmt1, forStmt2, forStmt3, forStmt3a, forStmt4:
loop, loopRestart = n, n.lastChild()
sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.lastChild()
case funcLit:
n.typ = nil // to force nodeType to recompute the type
@@ -258,13 +277,16 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if len(n.child[0].child) > 0 {
// define receiver symbol
var typ *itype
recvName := n.child[0].child[0].child[0].ident
recvTypeNode := n.child[0].child[0].lastChild()
fr := n.child[0].child[0]
recvTypeNode := fr.lastChild()
if typ, err = nodeType(interp, sc, recvTypeNode); err != nil {
return false
}
recvTypeNode.typ = typ
sc.sym[recvName] = &symbol{index: sc.add(typ), kind: varSym, typ: typ}
index := sc.add(typ)
if len(fr.child) > 1 {
sc.sym[fr.child[0].ident] = &symbol{index: index, kind: varSym, typ: typ}
}
}
for _, c := range n.child[2].child[0].child {
// define input parameter symbols
@@ -290,7 +312,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
c[i], c[l] = c[l], c[i]
}
sc = sc.pushBloc()
loop = n
sc.loop = n
case importSpec:
var name, ipath string
@@ -299,7 +321,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
name = n.child[0].ident
} else {
ipath = n.child[0].rval.String()
name = path.Base(ipath)
name = identifier.FindString(ipath)
}
if interp.binPkg[ipath] != nil && name != "." {
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}}
@@ -388,14 +410,18 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if src.typ, err = nodeType(interp, sc, src); err != nil {
return
}
dest.typ = src.typ
if src.typ.isBinMethod {
dest.typ = &itype{cat: valueT, rtype: src.typ.methodCallType()}
} else {
dest.typ = src.typ
}
}
if dest.typ.sizedef {
dest.typ.size = compositeArrayLen(src)
dest.typ.size = arrayTypeLen(src)
dest.typ.rtype = nil
}
if sc.global {
// Do not overload existings symbols (defined in GTA) in global scope
// Do not overload existing symbols (defined in GTA) in global scope
sym, _, _ = sc.lookup(dest.ident)
} else {
sym = &symbol{index: sc.add(dest.typ), kind: varSym, typ: dest.typ}
@@ -441,11 +467,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.gen = nop
src.level = level
src.findex = dest.findex
if src.typ.untyped && !dest.typ.untyped {
src.typ = dest.typ
}
case n.action == aAssign && src.action == aRecv:
// Assign by reading from a receiving channel
n.gen = nop
src.findex = dest.findex // Set recv address to LHS
dest.typ = src.typ.val
dest.typ = src.typ
case n.action == aAssign && src.action == aCompositeLit:
n.gen = nop
src.findex = dest.findex
@@ -454,6 +483,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
// TODO: perform constant folding and propagation here
switch {
case dest.typ.cat == interfaceT:
case isComplex(dest.typ.TypeOf()):
// value set in genValue
case !src.rval.IsValid():
// Assign to nil
@@ -498,7 +528,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[l].gen = getIndexMap2
n.gen = nop
case typeAssertExpr:
n.child[l].gen = typeAssert2
if n.child[0].ident == "_" {
n.child[l].gen = typeAssertStatus
} else {
n.child[l].gen = typeAssert2
}
n.gen = nop
case unaryExpr:
if n.child[l].action == aRecv {
@@ -522,7 +556,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
// Shift operator type is inherited from first parameter only
// All other binary operators require both parameter types to be the same
if !isShiftNode(n) && !c0.typ.untyped && !c1.typ.untyped && c0.typ.id() != c1.typ.id() {
if !isShiftNode(n) && !c0.typ.untyped && !c1.typ.untyped && !c0.typ.equals(c1.typ) {
err = n.cfgErrorf("mismatched types %s and %s", c0.typ.id(), c1.typ.id())
break
}
@@ -575,8 +609,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
constOp[n.action](n)
}
switch {
//case n.typ != nil && n.typ.cat == BoolT && isAncBranch(n):
// n.findex = -1
case n.rval.IsValid():
n.gen = nop
n.findex = -1
@@ -611,7 +643,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case stringT:
n.typ = sc.getType("byte")
case valueT:
n.typ = &itype{cat: valueT, rtype: t.rtype.Elem()}
if t.rtype.Kind() == reflect.String {
n.typ = sc.getType("byte")
} else {
n.typ = &itype{cat: valueT, rtype: t.rtype.Elem()}
}
default:
n.typ = t.val
}
@@ -665,14 +701,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if len(n.child) > 0 {
gotoLabel(n.sym)
} else {
n.tnext = loop
n.tnext = sc.loop
}
case continueStmt:
if len(n.child) > 0 {
gotoLabel(n.sym)
} else {
n.tnext = loopRestart
n.tnext = sc.loopRestart
}
case gotoStmt:
@@ -698,6 +734,10 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
} else {
n.findex = sc.add(n.typ)
}
if op, ok := constBltn[n.child[0].ident]; ok && n.anc.action != aAssign {
op(n) // pre-compute non-assigned constant builtin calls
}
case n.child[0].isType(sc):
// Type conversion expression
if isInt(n.child[0].typ.TypeOf()) && n.child[1].kind == basicLit && isFloat(n.child[1].typ.TypeOf()) {
@@ -792,36 +832,41 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
body := n.child[0]
n.start = body.start
body.tnext = n.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt1: // for cond {}
cond, body := n.child[0], n.child[1]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = cond.start
cond.tnext = body.start
cond.fnext = n
body.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt2: // for init; cond; {}
init, cond, body := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = init.start
init.tnext = cond.start
cond.tnext = body.start
cond.fnext = n
body.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt3: // for ; cond; post {}
cond, post, body := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = cond.start
cond.tnext = body.start
cond.fnext = n
body.tnext = post.start
post.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt3a: // for int; ; post {}
@@ -830,22 +875,22 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
init.tnext = body.start
body.tnext = post.start
post.tnext = body.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt4: // for init; cond; post {}
init, cond, post, body := n.child[0], n.child[1], n.child[2], n.child[3]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
}
n.start = init.start
init.tnext = cond.start
cond.tnext = body.start
cond.fnext = n
body.tnext = post.start
post.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forRangeStmt:
loop, loopRestart = nil, nil
n.start = n.child[0].start
n.child[0].fnext = n
sc = sc.pop()
@@ -856,15 +901,16 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
sc = sc.pop()
funcName := n.child[1].ident
if !isMethod(n) {
interp.scopes[pkgName].sym[funcName].index = -1 // to force value to n.val
interp.scopes[pkgName].sym[funcName].typ = n.typ
interp.scopes[pkgName].sym[funcName].kind = funcSym
interp.scopes[pkgName].sym[funcName].node = n
interp.scopes[pkgID].sym[funcName].index = -1 // to force value to n.val
interp.scopes[pkgID].sym[funcName].typ = n.typ
interp.scopes[pkgID].sym[funcName].kind = funcSym
interp.scopes[pkgID].sym[funcName].node = n
}
case funcLit:
n.types = sc.types
sc = sc.pop()
err = genRun(n)
case goStmt:
wireChild(n)
@@ -917,6 +963,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case ifStmt0: // if cond {}
cond, tbody := n.child[0], n.child[1]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = cond.start
cond.tnext = tbody.start
cond.fnext = n
@@ -925,6 +974,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case ifStmt1: // if cond {} else {}
cond, tbody, fbody := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = cond.start
cond.tnext = tbody.start
cond.fnext = fbody.start
@@ -934,6 +986,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case ifStmt2: // if init; cond {}
init, cond, tbody := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = init.start
tbody.tnext = n
init.tnext = cond.start
@@ -943,6 +998,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case ifStmt3: // if init; cond {} else {}
init, cond, tbody, fbody := n.child[0], n.child[1], n.child[2], n.child[3]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as if condition")
}
n.start = init.start
init.tnext = cond.start
cond.tnext = tbody.start
@@ -961,6 +1019,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case lorExpr:
n.start = n.child[0].start
@@ -969,6 +1030,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case parenExpr:
wireChild(n)
@@ -1008,10 +1072,15 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
// nil: Set node value to zero of return type
f := sc.def
var typ *itype
if typ, err = nodeType(interp, sc, f.child[2].child[1].child[i].lastChild()); err != nil {
if typ, err = nodeType(interp, sc, f.child[2].child[1].fieldType(i)); err != nil {
return
}
c.rval = reflect.New(typ.TypeOf()).Elem()
if typ.cat == funcT {
// Wrap the typed nil value in a node, as per other interpreter functions
c.rval = reflect.ValueOf(&node{kind: basicLit, rval: reflect.New(typ.TypeOf()).Elem()})
} else {
c.rval = reflect.New(typ.TypeOf()).Elem()
}
}
}
@@ -1032,7 +1101,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.val = method.Index
n.gen = getIndexBinMethod
n.recv = &receiver{node: n.child[0]}
n.typ = &itype{cat: valueT, rtype: method.Type}
n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true}
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}
@@ -1077,7 +1146,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index
n.gen = getPtrIndexSeq
} else {
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident)
}
@@ -1091,7 +1159,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.typ = &itype{cat: valueT, rtype: s.Type().Elem()}
} else {
n.kind = rvalueExpr
n.typ = &itype{cat: valueT, rtype: s.Type()}
n.typ = &itype{cat: valueT, rtype: s.Type(), untyped: isValueUntyped(s)}
n.rval = s
}
n.gen = nop
@@ -1248,7 +1316,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.start = sbn.start
}
sc = sc.pop()
loop = nil
case switchIfStmt: // like an if-else chain
sbn := n.lastChild() // switch block node
@@ -1285,13 +1352,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.start = sbn.start
}
sc = sc.pop()
loop = nil
case typeAssertExpr:
if len(n.child) > 1 {
wireChild(n)
if n.child[1].typ == nil {
n.child[1].typ = sc.getType(n.child[1].ident)
if n.child[1].typ, err = nodeType(interp, sc, n.child[1]); err != nil {
return
}
}
if n.anc.action != aAssignX {
n.typ = n.child[1].typ
@@ -1303,26 +1371,68 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case sliceExpr:
wireChild(n)
ctyp := n.child[0].typ
if ctyp.cat == ptrT {
ctyp = ctyp.val
}
if ctyp.size != 0 {
// Create a slice type from an array type
n.typ = &itype{}
*n.typ = *ctyp
n.typ.size = 0
n.typ.rtype = nil
} else {
n.typ = ctyp
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
n.findex = sc.add(n.typ)
case unaryExpr:
wireChild(n)
n.typ = n.child[0].typ
switch n.action {
case 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()}
} else {
n.typ = n.typ.val
}
case aBitNot:
if !isInt(n.typ.TypeOf()) {
err = n.cfgErrorf("illegal operand type for '^' operator")
return
}
case aNot:
if !isBool(n.typ) {
err = n.cfgErrorf("illegal operand type for '!' operator")
return
}
case aNeg, aPos:
if !isNumber(n.typ.TypeOf()) {
err = n.cfgErrorf("illegal operand type for '%v' operator", n.action)
return
}
}
// TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr)
n.findex = sc.add(n.typ)
if n.child[0].rval.IsValid() && constOp[n.action] != nil {
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
}
n.typ.TypeOf() // init reflect type
constOp[n.action](n)
}
switch {
case n.rval.IsValid():
n.gen = nop
n.findex = -1
case n.anc.kind == assignStmt && n.anc.action == aAssign:
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
n.findex = dest.findex
case n.anc.kind == returnStmt:
pos := childPos(n)
n.typ = sc.def.typ.ret[pos]
n.findex = pos
default:
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
}
n.findex = sc.add(n.typ)
}
case valueSpec:
n.gen = reset
@@ -1333,8 +1443,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
}
}
for _, c := range n.child[:l] {
index := sc.add(n.typ)
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
var index int
if sc.global {
// Global object allocation is already performed in GTA.
index = sc.sym[c.ident].index
} else {
index = sc.add(n.typ)
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
}
c.typ = n.typ
c.findex = index
}
@@ -1351,9 +1467,9 @@ func compDefineX(sc *scope, n *node) error {
l := len(n.child) - 1
types := []*itype{}
switch n.child[l].kind {
switch src := n.child[l]; src.kind {
case callExpr:
funtype, err := nodeType(n.interp, sc, n.child[l].child[0])
funtype, err := nodeType(n.interp, sc, src.child[0])
if err != nil {
return err
}
@@ -1368,18 +1484,22 @@ func compDefineX(sc *scope, n *node) error {
n.gen = nop
case indexExpr:
types = append(types, n.child[l].child[0].typ.val, sc.getType("bool"))
types = append(types, src.typ, sc.getType("bool"))
n.child[l].gen = getIndexMap2
n.gen = nop
case typeAssertExpr:
if n.child[0].ident == "_" {
n.child[l].gen = typeAssertStatus
} else {
n.child[l].gen = typeAssert2
}
types = append(types, n.child[l].child[1].typ, sc.getType("bool"))
n.child[l].gen = typeAssert2
n.gen = nop
case unaryExpr:
if n.child[l].action == aRecv {
types = append(types, n.child[l].child[0].typ.val, sc.getType("bool"))
types = append(types, src.typ, sc.getType("bool"))
n.child[l].gen = recv2
n.gen = nop
}
@@ -1460,7 +1580,7 @@ func isBinType(v reflect.Value) bool { return v.IsValid() && v.Kind() == reflect
// isType returns true if node refers to a type definition, false otherwise
func (n *node) isType(sc *scope) bool {
switch n.kind {
case arrayType, chanType, funcType, mapType, structType, rtypeExpr:
case arrayType, chanType, funcType, interfaceType, mapType, structType, rtypeExpr:
return true
case parenExpr, starExpr:
if len(n.child) == 1 {
@@ -1468,7 +1588,7 @@ func (n *node) isType(sc *scope) bool {
}
case selectorExpr:
pkg, name := n.child[0].ident, n.child[1].ident
if sym, _, ok := sc.lookup(pkg); ok {
if sym, _, ok := sc.lookup(pkg); ok && sym.kind == pkgSym {
path := sym.typ.path
if p, ok := n.interp.binPkg[path]; ok && isBinType(p[name]) {
return true // Imported binary type
@@ -1538,7 +1658,7 @@ func (n *node) isInteger() bool {
if isFloat(t) {
// untyped float constant with null decimal part is ok
f := n.rval.Float()
if f == math.Round(f) {
if f == math.Trunc(f) {
n.rval = reflect.ValueOf(int(f))
n.typ.rtype = n.rval.Type()
return true
@@ -1565,7 +1685,7 @@ func (n *node) isNatural() bool {
if isFloat(t) {
// positive untyped float constant with null decimal part is ok
f := n.rval.Float()
if f == math.Round(f) && f >= 0 {
if f == math.Trunc(f) && f >= 0 {
n.rval = reflect.ValueOf(uint(f))
n.typ.rtype = n.rval.Type()
return true
@@ -1575,6 +1695,29 @@ func (n *node) isNatural() bool {
return false
}
// fieldType returns the nth parameter field node (type) of a fieldList node
func (n *node) fieldType(m int) *node {
k := 0
l := len(n.child)
for i := 0; i < l; i++ {
cl := len(n.child[i].child)
if cl < 2 {
if k == m {
return n.child[i].lastChild()
}
k++
continue
}
for j := 0; j < cl-1; j++ {
if k == m {
return n.child[i].lastChild()
}
k++
}
}
return nil
}
// lastChild returns the last child of a node
func (n *node) lastChild() *node { return n.child[len(n.child)-1] }
@@ -1582,7 +1725,8 @@ func isKey(n *node) bool {
return n.anc.kind == fileStmt ||
(n.anc.kind == selectorExpr && n.anc.child[0] != n) ||
(n.anc.kind == funcDecl && isMethod(n.anc)) ||
(n.anc.kind == keyValueExpr && isStruct(n.anc.typ) && n.anc.child[0] == n)
(n.anc.kind == keyValueExpr && isStruct(n.anc.typ) && n.anc.child[0] == n) ||
(n.anc.kind == fieldExpr && len(n.anc.child) > 1 && n.anc.child[0] == n)
}
// isNewDefine returns true if node refers to a new definition
@@ -1649,13 +1793,6 @@ func getExec(n *node) bltn {
return n.exec
}
func fileNode(n *node) *node {
if n == nil || n.kind == fileStmt {
return n
}
return fileNode(n.anc)
}
// setExec recursively sets the node exec builtin function by walking the CFG
// from the entry point (first node to exec).
func setExec(n *node) {
@@ -1735,13 +1872,20 @@ func compositeGenerator(n *node) (gen bltnGenerator) {
return
}
// compositeArrayLen return the litteral array length, computed from definition
func compositeArrayLen(n *node) int {
// 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 {
if n.typ != nil && n.typ.sizedef {
return n.typ.size
}
max := -1
for i, c := range n.child[1:] {
r := i
if c.kind == keyValueExpr {
r = int(c.child[0].rval.Int())
if v := c.child[0].rval; v.IsValid() {
r = int(c.child[0].rval.Int())
}
}
if r > max {
max = r
@@ -1749,3 +1893,13 @@ func compositeArrayLen(n *node) int {
}
return max + 1
}
// isValueUntyped returns true if value is untyped
func isValueUntyped(v reflect.Value) bool {
// Consider only constant values.
if v.CanSet() {
return false
}
t := v.Type()
return t.String() == t.Kind().String()
}

View File

@@ -1,10 +1,47 @@
/*
Package interp provides a complete Go interpreter
Package interp provides a complete Go interpreter.
For the Go language itself, refer to the official Go specification
https://golang.org/ref/spec.
Importing packages
Packages can be imported in source or binary form, using the standard
Go import statement. In source form, packages are searched first in the
vendor directory, the preferred way to store source dependencies. If not
found in vendor, sources modules will be searched in GOPATH. Go modules
are not supported yet by yaegi.
Binary form packages are compiled and linked with the interpreter
executable, and exposed to scripts with the Use method. The goexports
command can be used to generate package wrappers.
Custom build tags
Custom build tags allow to control which files in imported source
packages are interpreted, in the same way as the "-tags" option of the
"go build" command. Setting a custom build tag spans globally for all
future imports of the session.
A build tag is a line comment that begins
// yaegi:tags
that lists the build constraints to be satisfied by the further
imports of source packages.
For example the following custom build tag
// yaegi:tags noasm
Will ensure that an import of a package will exclude files containing
// +build !noasm
And include files containing
// +build noasm
*/
package interp
// BUG(marc): Type checking is not implemented yet
// BUG(marc): Type checking is not implemented yet.

View File

@@ -38,7 +38,7 @@ func (n *node) astDot(out io.Writer, name string) {
func (n *node) cfgDot(out io.Writer) {
fmt.Fprintf(out, "digraph cfg {\n")
n.Walk(nil, func(n *node) {
if n.kind == basicLit || n.kind == identExpr || n.tnext == nil {
if n.kind == basicLit || n.tnext == nil {
return
}
var label string

View File

@@ -1,7 +1,6 @@
package interp
import (
"path"
"reflect"
)
@@ -9,8 +8,8 @@ import (
// variables and functions symbols at package level, prior to CFG.
// All function bodies are skipped. GTA is necessary to handle out of
// order declarations and multiple source files packages.
func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
sc, _ := interp.initScopePkg(root)
func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error) {
sc := interp.initScopePkg(pkgID)
var err error
var iotaValue int
var revisit []*node
@@ -31,6 +30,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
case defineStmt:
var atyp *itype
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 {
return false
}
@@ -52,7 +52,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
val = src.rval
}
if typ.incomplete {
// Come back when type is known
// Come back when type is known.
revisit = append(revisit, n)
return false
}
@@ -60,6 +60,9 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
err = n.cfgErrorf("use of untyped nil")
return false
}
if typ.isBinMethod {
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true}
}
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
if n.anc.kind == constDecl {
sc.sym[dest.ident].kind = constSym
@@ -77,6 +80,11 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
if n.typ, err = nodeType(interp, sc, n.child[l]); err != nil {
return false
}
if n.typ.incomplete {
// Come back when type is known.
revisit = append(revisit, n)
return false
}
}
for _, c := range n.child[:l] {
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ}
@@ -126,7 +134,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
name = n.child[0].ident
} else {
ipath = n.child[0].rval.String()
name = path.Base(ipath)
name = identifier.FindString(ipath)
}
// Try to import a binary package first, or a source package
if interp.binPkg[ipath] != nil {
@@ -143,7 +151,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
default: // import symbols in package namespace
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}}
}
} else if err = interp.importSrc(rpath, ipath, name); err == nil {
} else if err = interp.importSrc(rpath, ipath); err == nil {
sc.types = interp.universe.types
switch name {
case "_": // no import of symbols

View File

@@ -2,13 +2,18 @@ package interp
import (
"bufio"
"context"
"fmt"
"go/build"
"go/scanner"
"go/token"
"io"
"os"
"os/signal"
"reflect"
"strconv"
"sync"
"sync/atomic"
)
// Interpreter node structure for AST and CFG
@@ -20,7 +25,7 @@ type node struct {
fnext *node // false branch successor (CFG)
interp *Interpreter // interpreter context
frame *frame // frame pointer used for closures only (TODO: suppress this)
index int // node index (dot display)
index int64 // node index (dot display)
findex int // index of value in frame or frame size (func def, type def)
level int // number of frame indirections to access value
nleft int // number of children in left part (assign)
@@ -48,10 +53,45 @@ type receiver struct {
// frame contains values for the current execution level (a function context)
type frame struct {
anc *frame // ancestor frame (global space)
data []reflect.Value // values
deferred [][]reflect.Value // defer stack
recovered interface{} // to handle panic recover
// id is an atomic counter used for cancellation, only access
// via newFrame/runid/setrunid/clone.
// Located at start of struct to ensure proper aligment.
id uint64
anc *frame // ancestor frame (global space)
data []reflect.Value // values
mutex sync.RWMutex
deferred [][]reflect.Value // defer stack
recovered interface{} // to handle panic recover
done reflect.SelectCase // for cancellation of channel operations
}
func newFrame(anc *frame, len int, id uint64) *frame {
f := &frame{
anc: anc,
data: make([]reflect.Value, len),
id: id,
}
if anc != nil {
f.done = anc.done
}
return f
}
func (f *frame) runid() uint64 { return atomic.LoadUint64(&f.id) }
func (f *frame) setrunid(id uint64) { atomic.StoreUint64(&f.id, id) }
func (f *frame) clone() *frame {
f.mutex.RLock()
defer f.mutex.RUnlock()
return &frame{
anc: f.anc,
data: f.data,
deferred: f.deferred,
recovered: f.recovered,
id: f.runid(),
done: f.done,
}
}
// Exports stores the map of binary packages per package path
@@ -62,24 +102,36 @@ 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)
noRun bool // compile, but do not run
context build.Context // build context: GOPATH, build constraints
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
context build.Context // build context: GOPATH, build constraints
}
// Interpreter contains global resources and state
type Interpreter struct {
// id is an atomic counter counter used for run cancellation,
// only accessed via runid/stop
// Located at start of struct to ensure proper alignment on 32 bit
// architectures.
id uint64
Name string // program name
opt
opt // user settable options
cancelChan bool // enables cancellable chan operations
nindex int64 // next node index
fset *token.FileSet // fileset to locate node in source code
binPkg Exports // binary packages used in interpreter, indexed by path
rdir map[string]bool // for src import cycle detection
mutex sync.RWMutex
frame *frame // program data storage during execution
nindex int // next node index
fset *token.FileSet // fileset to locate node in source code
universe *scope // interpreter global level scope
scopes map[string]*scope // package level scopes, indexed by package name
binPkg Exports // binary packages used in interpreter, indexed by path
srcPkg imports // source packages used in interpreter, indexed by path
rdir map[string]bool // for src import cycle detection
done chan struct{} // for cancellation of channel operations
}
const (
@@ -146,15 +198,17 @@ func New(options Options) *Interpreter {
i.opt.context.BuildTags = options.BuildTags
}
// AstDot activates AST graph display for the interpreter
// astDot activates AST graph display for the interpreter
i.opt.astDot, _ = strconv.ParseBool(os.Getenv("YAEGI_AST_DOT"))
// CfgDot activates AST graph display for the interpreter
// cfgDot activates AST graph display for the interpreter
i.opt.cfgDot, _ = strconv.ParseBool(os.Getenv("YAEGI_CFG_DOT"))
// NoRun disable the execution (but not the compilation) in the interpreter
// noRun disables the execution (but not the compilation) in the interpreter
i.opt.noRun, _ = strconv.ParseBool(os.Getenv("YAEGI_NO_RUN"))
// fastChan disables the cancellable version of channel operations in evalWithContext
i.opt.fastChan, _ = strconv.ParseBool(os.Getenv("YAEGI_FAST_CHAN"))
return &i
}
@@ -227,7 +281,9 @@ func (interp *Interpreter) resizeFrame() {
}
func (interp *Interpreter) main() *node {
if m, ok := interp.scopes[mainID]; ok && m.sym[mainID] != nil {
interp.mutex.RLock()
defer interp.mutex.RUnlock()
if m, ok := interp.scopes[interp.Name]; ok && m.sym[mainID] != nil {
return m.sym[mainID].node
}
return nil
@@ -252,18 +308,18 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
}
// Global type analysis
revisit, err := interp.gta(root, pkgName)
revisit, err := interp.gta(root, pkgName, interp.Name)
if err != nil {
return res, err
}
for _, n := range revisit {
if _, err = interp.gta(n, pkgName); err != nil {
if _, err = interp.gta(n, pkgName, interp.Name); err != nil {
return res, err
}
}
// Annotate AST with CFG infos
initNodes, err := interp.cfg(root)
initNodes, err := interp.cfg(root, interp.Name)
if err != nil {
return res, err
}
@@ -277,11 +333,13 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
// REPL may skip package statement
setExec(root.start)
}
interp.mutex.Lock()
if interp.universe.sym[pkgName] == nil {
// Make the package visible under a path identical to its name
interp.srcPkg[pkgName] = interp.scopes[pkgName].sym
interp.srcPkg[pkgName] = interp.scopes[interp.Name].sym
interp.universe.sym[pkgName] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: pkgName}}
}
interp.mutex.Unlock()
if interp.cfgDot {
root.cfgDot(dotX())
@@ -291,11 +349,18 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
return res, err
}
// Execute CFG
// 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)
for _, n := range initNodes {
@@ -314,6 +379,42 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
return res, err
}
// EvalWithContext evaluates Go code represented as a string. It returns
// a map on current interpreted package exported symbols.
func (interp *Interpreter) EvalWithContext(ctx context.Context, src string) (reflect.Value, error) {
var v reflect.Value
var 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)
v, err = interp.Eval(src)
}()
select {
case <-ctx.Done():
interp.stop()
return reflect.Value{}, ctx.Err()
case <-done:
return v, err
}
}
// stop sends a semaphore to all running frames and closes the chan
// operation short circuit channel. stop may only be called once per
// invocation of EvalWithContext.
func (interp *Interpreter) stop() {
atomic.AddUint64(&interp.id, 1)
close(interp.done)
}
func (interp *Interpreter) runid() uint64 { return atomic.LoadUint64(&interp.id) }
// getWrapper returns the wrapper type of the corresponding interface, or nil if not found
func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
if p, ok := interp.binPkg[t.PkgPath()]; ok {
@@ -330,16 +431,21 @@ func (interp *Interpreter) Use(values Exports) {
}
}
// Repl performs a Read-Eval-Print-Loop on input file descriptor.
// Results are printed on output.
func (interp *Interpreter) Repl(in, out *os.File) {
// REPL performs a Read-Eval-Print-Loop on input reader.
// Results are printed on output writer.
func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
s := bufio.NewScanner(in)
prompt := getPrompt(in, out)
prompt()
src := ""
for s.Scan() {
src += s.Text() + "\n"
if v, err := interp.Eval(src); err != nil {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
handleSignal(ctx, cancel)
v, err := interp.EvalWithContext(ctx, src)
signal.Reset()
if err != nil {
switch err.(type) {
case scanner.ErrorList:
// Early failure in the scanner: the source is incomplete
@@ -357,10 +463,35 @@ func (interp *Interpreter) Repl(in, out *os.File) {
}
}
// getPrompt returns a function which prints a prompt only if input is a terminal
func getPrompt(in, out *os.File) func() {
if stat, err := in.Stat(); err == nil && stat.Mode()&os.ModeCharDevice != 0 {
// Repl performs a Read-Eval-Print-Loop on input file descriptor.
// Results are printed on output.
// Deprecated: use REPL instead
func (interp *Interpreter) Repl(in, out *os.File) {
interp.REPL(in, out)
}
// getPrompt returns a function which prints a prompt only if input is a terminal.
func getPrompt(in io.Reader, out io.Writer) func() {
s, ok := in.(interface{ Stat() (os.FileInfo, error) })
if !ok {
return func() {}
}
stat, err := s.Stat()
if err == nil && stat.Mode()&os.ModeCharDevice != 0 {
return func() { fmt.Fprint(out, "> ") }
}
return func() {}
}
// handleSignal wraps signal handling for eval cancellation.
func handleSignal(ctx context.Context, cancel context.CancelFunc) {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
select {
case <-c:
cancel()
case <-ctx.Done():
}
}()
}

View File

@@ -35,6 +35,8 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "bad0.go" || // expect error
file.Name() == "export1.go" || // non-main package
file.Name() == "export0.go" || // non-main package
file.Name() == "for7.go" || // expect error
file.Name() == "if2.go" || // expect error
file.Name() == "import6.go" || // expect error
file.Name() == "io0.go" || // use random number
file.Name() == "op1.go" || // expect error
@@ -104,7 +106,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
bin := filepath.Join(dir, strings.TrimSuffix(file.Name(), ".go"))
cmdBuild := exec.Command("go", "build", "-o", bin, filePath)
cmdBuild := exec.Command("go", "build", "-tags=dummy", "-o", bin, filePath)
outBuild, err := cmdBuild.CombinedOutput()
if err != nil {
t.Log(string(outBuild))
@@ -136,6 +138,16 @@ func TestInterpErrorConsistency(t *testing.T) {
expectedInterp: "1:1: expected 'package', found println",
expectedExec: "1:1: expected 'package', found println",
},
{
fileName: "if2.go",
expectedInterp: "7:5: non-bool used as if condition",
expectedExec: "7:2: non-bool i % 1000000 (type int) used as if condition",
},
{
fileName: "for7.go",
expectedInterp: "4:14: non-bool used as for condition",
expectedExec: "4:2: non-bool i (type int) used as for condition",
},
{
fileName: "op1.go",
expectedInterp: "5:2: illegal operand types for '+=' operator",

View File

@@ -1,6 +1,7 @@
package interp_test
import (
"context"
"fmt"
"log"
"net/http"
@@ -41,16 +42,22 @@ func TestEvalArithmetic(t *testing.T) {
{desc: "mul_II", src: "2 * 3", res: "6"},
{desc: "mul_FI", src: "2.2 * 3", res: "6.6000000000000005"},
{desc: "mul_IF", src: "3 * 2.2", res: "6.6000000000000005"},
{desc: "rem_FI", src: "8.0 % 4", err: "1:28: illegal operand types for '%' operator"},
{desc: "rem_FI", src: "8.2 % 4", err: "1:28: illegal operand types for '%' operator"},
{desc: "shl_II", src: "1 << 8", res: "256"},
{desc: "shl_IN", src: "1 << -1", err: "1:28: illegal operand types for '<<' operator"},
{desc: "shl_IF", src: "1 << 1.0", res: "2"},
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: illegal operand types for '<<' operator"},
{desc: "shl_IF", src: "1.0 << 1", res: "2"},
{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: illegal operand types for '>>' operator"},
{desc: "shr_IF", src: "1 >> 1.0", res: "0"},
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: illegal operand types for '>>' operator"},
{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: illegal operand type for '^' operator"},
{desc: "not_B", src: "!false", res: "true"},
{desc: "not_I", src: "!0", err: "1:28: illegal operand type for '!' operator"},
})
}
@@ -60,7 +67,7 @@ func TestEvalAssign(t *testing.T) {
{src: `a := "Hello"; a += " world"`, res: "Hello world"},
{src: `b := "Hello"; b += 1`, err: "1:42: illegal operand types for '+=' operator"},
{src: `c := "Hello"; c -= " world"`, err: "1:42: illegal operand types for '-=' operator"},
{src: "e := 64.0; e %= 64", err: "1:39: illegal operand types for '%=' operator"},
{src: "e := 64.4; e %= 64", err: "1:39: illegal operand types for '%=' operator"},
{src: "f := int64(3.2)", err: "1:33: truncated to integer"},
{src: "g := 1; g <<= 8", res: "256"},
{src: "h := 1; h >>= 8", res: "0"},
@@ -155,6 +162,14 @@ func TestEvalNil(t *testing.T) {
src: "Hello()",
res: "<nil>",
},
{
desc: "return nil func",
pre: func() {
eval(t, i, `func Bar() func() { return nil }`)
},
src: "Bar()",
res: "<nil>",
},
})
}
@@ -396,6 +411,100 @@ func TestEvalMissingSymbol(t *testing.T) {
}
}
func TestEvalWithContext(t *testing.T) {
tests := []testCase{
{
desc: "for {}",
src: `(func() {
for {}
})()`,
},
{
desc: "select {}",
src: `(func() {
select {}
})()`,
},
{
desc: "blocked chan send",
src: `(func() {
c := make(chan int)
c <- 1
})()`,
},
{
desc: "blocked chan recv",
src: `(func() {
c := make(chan int)
<-c
})()`,
},
{
desc: "blocked chan recv2",
src: `(func() {
c := make(chan int)
_, _ = <-c
})()`,
},
{
desc: "blocked range chan",
src: `(func() {
c := make(chan int)
for range c {}
})()`,
},
{
desc: "double lock",
src: `(func() {
var mu sync.Mutex
mu.Lock()
mu.Lock()
})()`,
},
}
for _, test := range tests {
done := make(chan struct{})
src := test.src
go func() {
defer close(done)
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
_, err := i.Eval(`import "sync"`)
if err != nil {
t.Errorf(`failed to import "sync": %v`, err)
return
}
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
_, err = i.EvalWithContext(ctx, src)
switch err {
case context.DeadlineExceeded:
// Successful cancellation.
// Check we can still execute an expression.
v, err := i.EvalWithContext(context.Background(), "1+1\n") //nolint:govet
if err != nil {
t.Errorf("failed to evaluate expression after cancellation: %v", err)
}
got := v.Interface()
if got != 2 {
t.Errorf("unexpected result of eval(1+1): got %v, want 2", got)
}
case nil:
t.Errorf("unexpected success evaluating expression %q", test.desc)
default:
t.Errorf("failed to evaluate expression %q: %v", test.desc, err)
}
}()
select {
case <-time.After(time.Second):
t.Errorf("timeout failed to terminate execution of %q", test.desc)
case <-done:
}
}
}
func runTests(t *testing.T, i *interp.Interpreter, tests []testCase) {
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {

View File

@@ -1,6 +1,6 @@
package interp
// Code generated by 'go run ../cmd/genop/genop.go'. DO NOT EDIT.
// Code generated by 'go run ../internal/genop/genop.go'. DO NOT EDIT.
import "reflect"
@@ -1864,6 +1864,57 @@ func inc(n *node) {
}
}
func bitNotConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(^v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(^v.Uint())
}
}
func negConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(-v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(-v.Uint())
case reflect.Float32, reflect.Float64:
n.rval.SetFloat(-v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex(-v.Complex())
}
}
func notConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
n.rval.SetBool(!v.Bool())
}
func posConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(+v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(+v.Uint())
case reflect.Float32, reflect.Float64:
n.rval.SetFloat(+v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex(+v.Complex())
}
}
func equal(n *node) {
tnext := getExec(n.tnext)
dest := genValue(n)
@@ -1938,146 +1989,6 @@ func equal(n *node) {
}
}
}
case isComplex(t0) || isComplex(t1):
switch {
case c0.rval.IsValid():
s0 := vComplex(c0.rval)
v1 := genValueComplex(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, s1 := v1(f)
if s0 == s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
_, s1 := v1(f)
dest(f).SetBool(s0 == s1)
return tnext
}
}
case c1.rval.IsValid():
s1 := vComplex(c1.rval)
v0 := genValueComplex(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
if s0 == s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
dest(f).SetBool(s0 == s1)
return tnext
}
}
default:
v0 := genValueComplex(n.child[0])
v1 := genValueComplex(n.child[1])
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
_, s1 := v1(f)
if s0 == s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
_, s1 := v1(f)
dest(f).SetBool(s0 == s1)
return tnext
}
}
}
default:
switch {
case c0.rval.IsValid():
i0 := c0.rval.Interface()
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
if i0 == i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
dest(f).SetBool(i0 == i1)
return tnext
}
}
case c1.rval.IsValid():
i1 := c1.rval.Interface()
v0 := genValue(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
if i0 == i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
dest(f).SetBool(i0 == i1)
return tnext
}
}
default:
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 == i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 == i1)
return tnext
}
}
}
case isFloat(t0) || isFloat(t1):
switch {
case c0.rval.IsValid():
@@ -2290,6 +2201,146 @@ func equal(n *node) {
}
}
}
case isComplex(t0) || isComplex(t1):
switch {
case c0.rval.IsValid():
s0 := vComplex(c0.rval)
v1 := genComplex(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
s1 := v1(f)
if s0 == s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
s1 := v1(f)
dest(f).SetBool(s0 == s1)
return tnext
}
}
case c1.rval.IsValid():
s1 := vComplex(c1.rval)
v0 := genComplex(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
s0 := v0(f)
if s0 == s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
s0 := v0(f)
dest(f).SetBool(s0 == s1)
return tnext
}
}
default:
v0 := genComplex(n.child[0])
v1 := genComplex(n.child[1])
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
s0 := v0(f)
s1 := v1(f)
if s0 == s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
s0 := v0(f)
s1 := v1(f)
dest(f).SetBool(s0 == s1)
return tnext
}
}
}
default:
switch {
case c0.rval.IsValid():
i0 := c0.rval.Interface()
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
if i0 == i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
dest(f).SetBool(i0 == i1)
return tnext
}
}
case c1.rval.IsValid():
i1 := c1.rval.Interface()
v0 := genValue(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
if i0 == i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
dest(f).SetBool(i0 == i1)
return tnext
}
}
default:
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 == i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 == i1)
return tnext
}
}
}
}
}
@@ -3523,146 +3574,6 @@ func notEqual(n *node) {
}
}
}
case isComplex(t0) || isComplex(t1):
switch {
case c0.rval.IsValid():
s0 := vComplex(c0.rval)
v1 := genValueComplex(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, s1 := v1(f)
if s0 != s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
_, s1 := v1(f)
dest(f).SetBool(s0 != s1)
return tnext
}
}
case c1.rval.IsValid():
s1 := vComplex(c1.rval)
v0 := genValueComplex(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
if s0 != s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
dest(f).SetBool(s0 != s1)
return tnext
}
}
default:
v0 := genValueComplex(n.child[0])
v1 := genValueComplex(n.child[1])
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
_, s1 := v1(f)
if s0 != s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
_, s0 := v0(f)
_, s1 := v1(f)
dest(f).SetBool(s0 != s1)
return tnext
}
}
}
default:
switch {
case c0.rval.IsValid():
i0 := c0.rval.Interface()
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
dest(f).SetBool(i0 != i1)
return tnext
}
}
case c1.rval.IsValid():
i1 := c1.rval.Interface()
v0 := genValue(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
dest(f).SetBool(i0 != i1)
return tnext
}
}
default:
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 != i1)
return tnext
}
}
}
case isFloat(t0) || isFloat(t1):
switch {
case c0.rval.IsValid():
@@ -3875,5 +3786,145 @@ func notEqual(n *node) {
}
}
}
case isComplex(t0) || isComplex(t1):
switch {
case c0.rval.IsValid():
s0 := vComplex(c0.rval)
v1 := genComplex(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
s1 := v1(f)
if s0 != s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
s1 := v1(f)
dest(f).SetBool(s0 != s1)
return tnext
}
}
case c1.rval.IsValid():
s1 := vComplex(c1.rval)
v0 := genComplex(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
s0 := v0(f)
if s0 != s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
s0 := v0(f)
dest(f).SetBool(s0 != s1)
return tnext
}
}
default:
v0 := genComplex(n.child[0])
v1 := genComplex(n.child[1])
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
s0 := v0(f)
s1 := v1(f)
if s0 != s1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
n.exec = func(f *frame) bltn {
s0 := v0(f)
s1 := v1(f)
dest(f).SetBool(s0 != s1)
return tnext
}
}
}
default:
switch {
case c0.rval.IsValid():
i0 := c0.rval.Interface()
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
dest(f).SetBool(i0 != i1)
return tnext
}
}
case c1.rval.IsValid():
i1 := c1.rval.Interface()
v0 := genValue(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
dest(f).SetBool(i0 != i1)
return tnext
}
}
default:
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 != i1)
return tnext
}
}
}
}
}

View File

@@ -24,6 +24,7 @@ var builtin = [...]bltnGenerator{
aAndAssign: andAssign,
aAndNot: andNot,
aAndNotAssign: andNotAssign,
aBitNot: bitNot,
aCall: call,
aCase: _case,
aCompositeLit: arrayLit,
@@ -40,11 +41,12 @@ var builtin = [...]bltnGenerator{
aLowerEqual: lowerEqual,
aMul: mul,
aMulAssign: mulAssign,
aNegate: negate,
aNeg: neg,
aNot: not,
aNotEqual: notEqual,
aOr: or,
aOrAssign: orAssign,
aPos: pos,
aQuo: quo,
aQuoAssign: quoAssign,
aRange: _range,
@@ -84,8 +86,11 @@ func (interp *Interpreter) run(n *node, cf *frame) {
if cf == nil {
f = interp.frame
} else {
f = &frame{anc: cf, data: make([]reflect.Value, len(n.types))}
f = newFrame(cf, len(n.types), interp.runid())
}
interp.mutex.RLock()
f.done = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(interp.done)}
interp.mutex.RUnlock()
for i, t := range n.types {
f.data[i] = reflect.New(t).Elem()
@@ -98,42 +103,76 @@ func (interp *Interpreter) run(n *node, cf *frame) {
// runCfg executes a node AST by walking its CFG and running node builtin at each step
func runCfg(n *node, f *frame) {
defer func() {
f.mutex.Lock()
f.recovered = recover()
for _, val := range f.deferred {
val[0].Call(val[1:])
}
if f.recovered != nil {
fmt.Println(n.cfgErrorf("panic"))
f.mutex.Unlock()
panic(f.recovered)
}
f.mutex.Unlock()
}()
for exec := n.exec; exec != nil; {
for exec := n.exec; exec != nil && f.runid() == n.interp.runid(); {
exec = exec(f)
}
}
func typeAssert(n *node) {
value := genValue(n.child[0])
i := n.findex
func typeAssertStatus(n *node) {
value := genValue(n.child[0]) // input value
value1 := genValue(n.anc.child[1]) // returned status
next := getExec(n.tnext)
switch {
case n.child[0].typ.cat == valueT:
n.exec = func(f *frame) bltn {
f.data[i].Set(value(f).Elem())
if !value(f).IsValid() || value(f).IsNil() {
value1(f).SetBool(false)
}
value1(f).SetBool(true)
return next
}
case n.child[1].typ.cat == interfaceT:
n.exec = func(f *frame) bltn {
_, ok := value(f).Interface().(valueInterface)
//value0(f).Set(reflect.ValueOf(valueInterface{v.node, v.value}))
value1(f).SetBool(ok)
return next
}
default:
n.exec = func(f *frame) bltn {
_, ok := value(f).Interface().(valueInterface)
//value0(f).Set(v.value)
value1(f).SetBool(ok)
return next
}
}
}
func typeAssert(n *node) {
value := genValue(n.child[0]) // input value
dest := genValue(n) // returned result
next := getExec(n.tnext)
switch {
case n.child[0].typ.cat == valueT:
n.exec = func(f *frame) bltn {
dest(f).Set(value(f).Elem())
return next
}
case n.child[1].typ.cat == interfaceT:
n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface)
f.data[i] = reflect.ValueOf(valueInterface{v.node, v.value})
dest(f).Set(reflect.ValueOf(valueInterface{v.node, v.value}))
return next
}
default:
n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface)
f.data[i].Set(v.value)
dest(f).Set(v.value)
return next
}
}
@@ -202,7 +241,7 @@ func isRecursiveStruct(t *itype, rtype reflect.Type) bool {
if t.cat == structT && rtype.Kind() == reflect.Interface {
return true
}
if t.cat == ptrT {
if t.cat == ptrT && t.rtype != nil {
return isRecursiveStruct(t.val, t.rtype.Elem())
}
return false
@@ -223,15 +262,21 @@ func assign(n *node) {
switch {
case dest.typ.cat == interfaceT:
svalue[i] = genValueInterface(src)
case dest.typ.cat == valueT && dest.typ.rtype.Kind() == reflect.Interface:
case (dest.typ.cat == valueT || dest.typ.cat == errorT) && dest.typ.rtype.Kind() == reflect.Interface:
svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
case dest.typ.cat == valueT && src.typ.cat == funcT:
svalue[i] = genFunctionWrapper(src)
case dest.typ.cat == funcT && src.typ.cat == valueT:
svalue[i] = genValueNode(src)
case src.kind == basicLit && src.val == nil:
t := dest.typ.TypeOf()
svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() }
case isRecursiveStruct(dest.typ, dest.typ.rtype):
svalue[i] = genValueInterfacePtr(src)
case src.typ.untyped && isComplex(dest.typ.TypeOf()):
svalue[i] = genValueComplex(src)
case src.typ.untyped && !dest.typ.untyped:
svalue[i] = genValueAs(src, dest.typ.TypeOf())
default:
svalue[i] = genValue(src)
}
@@ -248,12 +293,17 @@ func assign(n *node) {
}
if n.nleft == 1 {
if s, d, i := svalue[0], dvalue[0], ivalue[0]; i != nil {
switch s, d, i := svalue[0], dvalue[0], ivalue[0]; {
case n.child[0].ident == "_":
n.exec = func(f *frame) bltn {
return next
}
case i != nil:
n.exec = func(f *frame) bltn {
d(f).SetMapIndex(i(f), s(f))
return next
}
} else {
default:
n.exec = func(f *frame) bltn {
d(f).Set(s(f))
return next
@@ -271,7 +321,6 @@ func assign(n *node) {
default:
t = typ.TypeOf()
}
//types[i] = n.child[sbase+i].typ.TypeOf()
types[i] = t
}
@@ -281,10 +330,16 @@ func assign(n *node) {
n.exec = func(f *frame) bltn {
t := make([]reflect.Value, len(svalue))
for i, s := range svalue {
if n.child[i].ident == "_" {
continue
}
t[i] = reflect.New(types[i]).Elem()
t[i].Set(s(f))
}
for i, d := range dvalue {
if n.child[i].ident == "_" {
continue
}
if j := ivalue[i]; j != nil {
d(f).SetMapIndex(j(f), t[i]) // Assign a map entry
} else {
@@ -297,21 +352,24 @@ func assign(n *node) {
}
func not(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
tnext := getExec(n.tnext)
i := n.findex
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if !value(f).Bool() {
f.data[i].SetBool(true)
return tnext
}
f.data[i].SetBool(false)
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).Bool())
dest(f).SetBool(!value(f).Bool())
return tnext
}
}
@@ -416,6 +474,10 @@ func _panic(n *node) {
func genFunctionWrapper(n *node) func(*frame) reflect.Value {
var def *node
var ok bool
if n.kind == basicLit {
return func(f *frame) reflect.Value { return n.rval }
}
if def, ok = n.val.(*node); !ok {
return genValueAsFunctionWrapper(n)
}
@@ -438,7 +500,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
}
return reflect.MakeFunc(n.typ.TypeOf(), func(in []reflect.Value) []reflect.Value {
// Allocate and init local frame. All values to be settable and addressable.
fr := frame{anc: f, data: make([]reflect.Value, len(def.types))}
fr := newFrame(f, len(def.types), f.runid())
d := fr.data
for i, t := range def.types {
d[i] = reflect.New(t).Elem()
@@ -467,7 +529,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
}
// Interpreter code execution
runCfg(start, &fr)
runCfg(start, fr)
result := fr.data[:numRet]
for i, r := range result {
@@ -611,7 +673,7 @@ func call(n *node) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
}
default:
if c.kind == basicLit {
if c.kind == basicLit || c.rval.IsValid() {
var argType reflect.Type
if variadic >= 0 && i >= variadic {
argType = n.child[0].typ.arg[variadic].val.TypeOf()
@@ -647,12 +709,35 @@ func call(n *node) {
n.exec = func(f *frame) bltn {
def := value(f).Interface().(*node)
// Call bin func if defined
if def.rval.IsValid() {
in := make([]reflect.Value, len(values))
for i, v := range values {
in[i] = v(f)
}
if goroutine {
go def.rval.Call(in)
return tnext
}
out := def.rval.Call(in)
for i, v := range rvalues {
if v != nil {
v(f).Set(out[i])
}
}
if fnext != nil && !out[0].Bool() {
return fnext
}
return tnext
}
anc := f
// Get closure frame context (if any)
if def.frame != nil {
anc = def.frame
}
nf := frame{anc: anc, data: make([]reflect.Value, len(def.types))}
nf := newFrame(anc, len(def.types), anc.runid())
var vararg reflect.Value
// Init return values
@@ -717,10 +802,10 @@ func call(n *node) {
// Execute function body
if goroutine {
go runCfg(def.child[3].start, &nf)
go runCfg(def.child[3].start, nf)
return tnext
}
runCfg(def.child[3].start, &nf)
runCfg(def.child[3].start, nf)
// Handle branching according to boolean result
if fnext != nil && !nf.data[0].Bool() {
@@ -753,7 +838,9 @@ func callBin(n *node) {
// method signature obtained from reflect.Type include receiver as 1st arg, except for interface types
rcvrOffset := 0
if recv := n.child[0].recv; recv != nil && recv.node.typ.TypeOf().Kind() != reflect.Interface {
rcvrOffset = 1
if funcType.NumIn() > len(child) {
rcvrOffset = 1
}
}
for i, c := range child {
@@ -773,7 +860,7 @@ func callBin(n *node) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
}
default:
if c.kind == basicLit {
if c.kind == basicLit || c.rval.IsValid() {
// Convert literal value (untyped) to function argument type (if not an interface{})
var argType reflect.Type
if variadic >= 0 && i >= variadic {
@@ -792,7 +879,6 @@ func callBin(n *node) {
case interfaceT:
values = append(values, genValueInterfaceValue(c))
default:
//values = append(values, genValue(c))
values = append(values, genInterfaceWrapper(c, defType))
}
}
@@ -1022,10 +1108,10 @@ func getFunc(n *node) {
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
fr := *f
fr := f.clone()
nod := *n
nod.val = &nod
nod.frame = &fr
nod.frame = fr
dest(f).Set(reflect.ValueOf(&nod))
return next
}
@@ -1036,11 +1122,11 @@ func getMethod(n *node) {
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
fr := *f
fr := f.clone()
nod := *(n.val.(*node))
nod.val = &nod
nod.recv = n.recv
nod.frame = &fr
nod.frame = fr
f.data[i] = reflect.ValueOf(&nod)
return next
}
@@ -1055,11 +1141,11 @@ func getMethodByName(n *node) {
n.exec = func(f *frame) bltn {
val := value0(f).Interface().(valueInterface)
m, li := val.node.typ.lookupMethod(name)
fr := *f
fr := f.clone()
nod := *m
nod.val = &nod
nod.recv = &receiver{nil, val.value, li}
nod.frame = &fr
nod.frame = fr
f.data[i] = reflect.ValueOf(&nod)
return next
}
@@ -1176,7 +1262,7 @@ func getIndexSeqMethod(n *node) {
}
}
func negate(n *node) {
func neg(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
@@ -1200,6 +1286,37 @@ func negate(n *node) {
}
}
func pos(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
dest(f).Set(value(f))
return next
}
}
func bitNot(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
typ := n.typ.TypeOf()
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.exec = func(f *frame) bltn {
dest(f).SetInt(^value(f).Int())
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.exec = func(f *frame) bltn {
dest(f).SetUint(^value(f).Uint())
return next
}
}
}
func land(n *node) {
value0 := genValue(n.child[0])
value1 := genValue(n.child[1])
@@ -1284,10 +1401,16 @@ func _return(n *node) {
} else {
values[i] = genValue(c)
}
case funcT:
values[i] = genValue(c)
case interfaceT:
values[i] = genValueInterface(c)
default:
values[i] = genValue(c)
if c.typ.untyped {
values[i] = genValueAs(c, def.typ.ret[i].TypeOf())
} else {
values[i] = genValue(c)
}
}
}
@@ -1351,7 +1474,7 @@ func arrayLit(n *node) {
}
var a reflect.Value
if n.typ.size > 0 {
if n.typ.sizedef {
a, _ = n.typ.zero()
} else {
a = reflect.MakeSlice(n.typ.TypeOf(), max, max)
@@ -1460,6 +1583,15 @@ func compositeBinStruct(n *node) {
}
}
func destType(n *node) *itype {
switch n.anc.kind {
case assignStmt, defineStmt:
return n.anc.child[0].typ
default:
return n.typ
}
}
// compositeLit creates and populates a struct object
func compositeLit(n *node) {
value := valueGenerator(n, n.findex)
@@ -1468,6 +1600,7 @@ func compositeLit(n *node) {
if !n.typ.untyped {
child = n.child[1:]
}
destInterface := destType(n).cat == interfaceT
values := make([]func(*frame) reflect.Value, len(child))
for i, c := range child {
@@ -1484,9 +1617,12 @@ func compositeLit(n *node) {
for i, v := range values {
a.Field(i).Set(v(f))
}
if d := value(f); d.Type().Kind() == reflect.Ptr {
switch d := value(f); {
case d.Type().Kind() == reflect.Ptr:
d.Set(a.Addr())
} else {
case destInterface:
d.Set(reflect.ValueOf(valueInterface{n, a}))
default:
d.Set(a)
}
return next
@@ -1534,6 +1670,7 @@ var rat = reflect.ValueOf((*[]rune)(nil)).Type().Elem() // runes array type
func _range(n *node) {
index0 := n.child[0].findex // array index location in frame
index2 := index0 - 1 // shallow array for range, always just behind index0
fnext := getExec(n.fnext)
tnext := getExec(n.tnext)
@@ -1544,10 +1681,10 @@ func _range(n *node) {
if isString(an.typ.TypeOf()) {
value = genValueAs(an, rat) // range on string iterates over runes
} else {
value = genValueArray(an)
value = genValueRangeArray(an)
}
n.exec = func(f *frame) bltn {
a := value(f)
a := f.data[index2]
v0 := f.data[index0]
v0.SetInt(v0.Int() + 1)
i := int(v0.Int())
@@ -1562,13 +1699,12 @@ func _range(n *node) {
if isString(an.typ.TypeOf()) {
value = genValueAs(an, rat) // range on string iterates over runes
} else {
value = genValueArray(an)
value = genValueRangeArray(an)
}
n.exec = func(f *frame) bltn {
a := value(f)
v0 := f.data[index0]
v0.SetInt(v0.Int() + 1)
if int(v0.Int()) >= a.Len() {
if int(v0.Int()) >= f.data[index2].Len() {
return fnext
}
return tnext
@@ -1578,7 +1714,8 @@ func _range(n *node) {
// Init sequence
next := n.exec
n.child[0].exec = func(f *frame) bltn {
f.data[index0].SetInt(-1)
f.data[index2] = value(f) // set array shallow copy for range
f.data[index0].SetInt(-1) // assing index value
return next
}
}
@@ -1590,7 +1727,10 @@ func rangeChan(n *node) {
tnext := getExec(n.tnext)
n.exec = func(f *frame) bltn {
v, ok := value(f).Recv()
chosen, v, ok := reflect.Select([]reflect.SelectCase{f.done, {Dir: reflect.SelectRecv, Chan: value(f)}})
if chosen == 0 {
return nil
}
if !ok {
return fnext
}
@@ -1600,31 +1740,40 @@ func rangeChan(n *node) {
}
func rangeMap(n *node) {
index0 := n.child[0].findex // array index location in frame
index1 := n.child[1].findex // array value location in frame
value := genValue(n.child[2]) // array
index0 := n.child[0].findex // map index location in frame
index2 := index0 - 1 // iterator for range, always just behind index0
fnext := getExec(n.fnext)
tnext := getExec(n.tnext)
// TODO: move i and keys to frame
var i int
var keys []reflect.Value
n.exec = func(f *frame) bltn {
a := value(f)
i++
if i >= a.Len() {
return fnext
var value func(*frame) reflect.Value
if len(n.child) == 4 {
index1 := n.child[1].findex // map value location in frame
value = genValue(n.child[2]) // map
n.exec = func(f *frame) bltn {
iter := f.data[index2].Interface().(*reflect.MapIter)
if !iter.Next() {
return fnext
}
f.data[index0].Set(iter.Key())
f.data[index1].Set(iter.Value())
return tnext
}
} else {
value = genValue(n.child[1]) // map
n.exec = func(f *frame) bltn {
iter := f.data[index2].Interface().(*reflect.MapIter)
if !iter.Next() {
return fnext
}
f.data[index0].Set(iter.Key())
return tnext
}
f.data[index0].Set(keys[i])
f.data[index1].Set(a.MapIndex(keys[i]))
return tnext
}
// Init sequence
next := n.exec
n.child[0].exec = func(f *frame) bltn {
keys = value(f).MapKeys()
i = -1
f.data[index2].Set(reflect.ValueOf(value(f).MapRange()))
return next
}
}
@@ -1757,7 +1906,6 @@ func appendSlice(n *node) {
return next
}
}
}
func _append(n *node) {
@@ -1845,39 +1993,48 @@ func _close(n *node) {
}
func _complex(n *node) {
i := n.findex
convertLiteralValue(n.child[1], floatType)
convertLiteralValue(n.child[2], floatType)
value0 := genValue(n.child[1])
value1 := genValue(n.child[2])
dest := genValue(n)
c1, c2 := n.child[1], n.child[2]
convertLiteralValue(c1, floatType)
convertLiteralValue(c2, floatType)
value0 := genValue(c1)
value1 := genValue(c2)
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetComplex(complex(value0(f).Float(), value1(f).Float()))
return next
if typ := n.typ.TypeOf(); isComplex(typ) {
n.exec = func(f *frame) bltn {
dest(f).SetComplex(complex(value0(f).Float(), value1(f).Float()))
return next
}
} else {
// Not a complex type: ignore imaginary part
n.exec = func(f *frame) bltn {
dest(f).Set(value0(f).Convert(typ))
return next
}
}
}
func _imag(n *node) {
i := n.findex
dest := genValue(n)
convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetFloat(imag(value(f).Complex()))
dest(f).SetFloat(imag(value(f).Complex()))
return next
}
}
func _real(n *node) {
i := n.findex
dest := genValue(n)
convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetFloat(real(value(f).Complex()))
dest(f).SetFloat(real(value(f).Complex()))
return next
}
}
@@ -1895,12 +2052,12 @@ func _delete(n *node) {
}
func _len(n *node) {
i := n.findex
dest := genValue(n)
value := genValue(n.child[1])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetInt(int64(value(f).Len()))
dest(f).SetInt(int64(value(f).Len()))
return next
}
}
@@ -2014,19 +2171,63 @@ func recv(n *node) {
value := genValue(n.child[0])
tnext := getExec(n.tnext)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if v, _ := value(f).Recv(); v.Bool() {
if n.interp.cancelChan {
// Cancellable channel read
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
ch := value(f)
// Fast: channel read doesn't block
if x, ok := ch.TryRecv(); ok {
if x.Bool() {
return tnext
}
return fnext
}
// Slow: channel read blocks, allow cancel
chosen, v, _ := reflect.Select([]reflect.SelectCase{f.done, {Dir: reflect.SelectRecv, Chan: ch}})
if chosen == 0 {
return nil
}
if v.Bool() {
return tnext
}
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
// Fast: channel read doesn't block
var ok bool
ch := value(f)
if f.data[i], ok = ch.TryRecv(); ok {
return tnext
}
// Slow: channel is blocked, allow cancel
var chosen int
chosen, f.data[i], _ = reflect.Select([]reflect.SelectCase{f.done, {Dir: reflect.SelectRecv, Chan: ch}})
if chosen == 0 {
return nil
}
return tnext
}
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i], _ = value(f).Recv()
return tnext
// Blocking channel read (less overhead)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if v, _ := value(f).Recv(); v.Bool() {
return tnext
}
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i], _ = value(f).Recv()
return tnext
}
}
}
}
@@ -2037,22 +2238,47 @@ func recv2(n *node) {
vok := genValue(n.anc.child[1]) // status
tnext := getExec(n.tnext)
n.exec = func(f *frame) bltn {
v, ok := vchan(f).Recv()
vres(f).Set(v)
vok(f).SetBool(ok)
return tnext
if n.interp.cancelChan {
// Cancellable channel read
n.exec = func(f *frame) bltn {
ch, result, status := vchan(f), vres(f), vok(f)
// Fast: channel read doesn't block
if v, ok := ch.TryRecv(); ok {
result.Set(v)
status.SetBool(true)
return tnext
}
// Slow: channel is blocked, allow cancel
chosen, v, ok := reflect.Select([]reflect.SelectCase{f.done, {Dir: reflect.SelectRecv, Chan: ch}})
if chosen == 0 {
return nil
}
result.Set(v)
status.SetBool(ok)
return tnext
}
} else {
// Blocking channel read (less overhead)
n.exec = func(f *frame) bltn {
v, ok := vchan(f).Recv()
vres(f).Set(v)
vok(f).SetBool(ok)
return tnext
}
}
}
func convertLiteralValue(n *node, t reflect.Type) {
if n.kind != basicLit || t == nil || t.Kind() == reflect.Interface {
// Skip non-constant values, undefined target type or interface target type.
if !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface {
return
}
if n.rval.IsValid() {
// Convert constant value to target type.
n.rval = n.rval.Convert(t)
} else {
n.rval = reflect.New(t).Elem() // convert to type nil value
// Create a zero value of target type.
n.rval = reflect.New(t).Elem()
}
}
@@ -2063,9 +2289,27 @@ func send(n *node) {
convertLiteralValue(n.child[1], n.child[0].typ.val.TypeOf())
value1 := genValue(n.child[1]) // value to send
n.exec = func(f *frame) bltn {
value0(f).Send(value1(f))
return next
if n.interp.cancelChan {
// Cancellable send
n.exec = func(f *frame) bltn {
ch, data := value0(f), value1(f)
// Fast: send on channel doesn't block
if ok := ch.TrySend(data); ok {
return next
}
// Slow: send on channel blocks, allow cancel
chosen, _, _ := reflect.Select([]reflect.SelectCase{f.done, {Dir: reflect.SelectSend, Chan: ch, Send: data}})
if chosen == 0 {
return nil
}
return next
}
} else {
// Blocking send (less overhead)
n.exec = func(f *frame) bltn {
value0(f).Send(value1(f))
return next
}
}
}
@@ -2107,7 +2351,7 @@ func _select(n *node) {
chanValues := make([]func(*frame) reflect.Value, nbClause)
assignedValues := make([]func(*frame) reflect.Value, nbClause)
okValues := make([]func(*frame) reflect.Value, nbClause)
cases := make([]reflect.SelectCase, nbClause)
cases := make([]reflect.SelectCase, nbClause+1)
for i := 0; i < nbClause; i++ {
if len(n.child[i].child) > 1 {
@@ -2127,7 +2371,8 @@ func _select(n *node) {
}
n.exec = func(f *frame) bltn {
for i := range cases {
cases[nbClause] = f.done
for i := range cases[:nbClause] {
switch cases[i].Dir {
case reflect.SelectRecv:
cases[i].Chan = chanValues[i](f)
@@ -2139,6 +2384,9 @@ func _select(n *node) {
}
}
j, v, s := reflect.Select(cases)
if j == nbClause {
return nil
}
if cases[j].Dir == reflect.SelectRecv && assignedValues[j] != nil {
assignedValues[j](f).Set(v)
if ok[j] != nil {
@@ -2215,43 +2463,78 @@ func slice0(n *node) {
}
func isNil(n *node) {
value := genValue(n.child[0])
var value func(*frame) reflect.Value
if n.child[0].typ.cat == funcT {
value = genValueAsFunctionWrapper(n.child[0])
} else {
value = genValue(n.child[0])
}
tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i].SetBool(value(f).IsNil())
dest(f).SetBool(value(f).IsNil())
return tnext
}
}
}
func isNotNil(n *node) {
value := genValue(n.child[0])
var value func(*frame) reflect.Value
if n.child[0].typ.cat == funcT {
value = genValueAsFunctionWrapper(n.child[0])
} else {
value = genValue(n.child[0])
}
tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(false)
return fnext
}
dest(f).SetBool(true)
return tnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).IsNil())
dest(f).SetBool(!value(f).IsNil())
return tnext
}
}
}
func complexConst(n *node) {
if v0, v1 := n.child[1].rval, n.child[2].rval; v0.IsValid() && v1.IsValid() {
n.rval = reflect.ValueOf(complex(vFloat(v0), vFloat(v1)))
n.gen = nop
}
}
func imagConst(n *node) {
if v := n.child[1].rval; v.IsValid() {
n.rval = reflect.ValueOf(imag(v.Complex()))
n.gen = nop
}
}
func realConst(n *node) {
if v := n.child[1].rval; v.IsValid() {
n.rval = reflect.ValueOf(real(v.Complex()))
n.gen = nop
}
}

View File

@@ -74,12 +74,15 @@ type symbol struct {
// execution to the index in frame, created exactly from the types layout.
//
type scope struct {
anc *scope // Ancestor upper scope
def *node // function definition node this scope belongs to, or nil
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
global bool // true if scope refers to global space (single frame for universe and package level scopes)
anc *scope // Ancestor upper scope
def *node // function definition node this scope belongs to, or nil
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
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
global bool // true if scope refers to global space (single frame for universe and package level scopes)
}
// push creates a new scope and chain it to the current one
@@ -95,6 +98,8 @@ func (s *scope) push(indirect bool) *scope {
sc.global = s.global
sc.level = s.level
}
// inherit loop state and pkgID from ancestor
sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
return &sc
}
@@ -156,18 +161,15 @@ func (s *scope) add(typ *itype) (index int) {
return
}
func (interp *Interpreter) initScopePkg(n *node) (*scope, string) {
func (interp *Interpreter) initScopePkg(pkgID string) *scope {
sc := interp.universe
pkgName := mainID
if p := fileNode(n); p != nil {
pkgName = p.child[0].ident
interp.mutex.Lock()
if _, ok := interp.scopes[pkgID]; !ok {
interp.scopes[pkgID] = sc.pushBloc()
}
if _, ok := interp.scopes[pkgName]; !ok {
interp.scopes[pkgName] = sc.pushBloc()
}
sc = interp.scopes[pkgName]
return sc, pkgName
sc = interp.scopes[pkgID]
sc.pkgID = pkgID
interp.mutex.Unlock()
return sc
}

View File

@@ -8,7 +8,7 @@ import (
"strings"
)
func (interp *Interpreter) importSrc(rPath, path, alias string) error {
func (interp *Interpreter) importSrc(rPath, path string) error {
var dir string
var err error
@@ -29,6 +29,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
} else if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, path); err != nil {
return err
}
if interp.rdir[path] {
return fmt.Errorf("import cycle not allowed\n\timports %s", path)
}
@@ -49,7 +50,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// Parse source files
for _, file := range files {
name := file.Name()
if skipFile(interp.context, name) {
if skipFile(&interp.context, name) {
continue
}
@@ -66,6 +67,9 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
if root == nil {
continue
}
if interp.astDot {
root.astDot(dotX(), name)
}
if pkgName == "" {
pkgName = pname
} else if pkgName != pname {
@@ -75,7 +79,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
subRPath := effectivePkg(rPath, path)
var list []*node
list, err = interp.gta(root, subRPath)
list, err = interp.gta(root, subRPath, path)
if err != nil {
return err
}
@@ -85,7 +89,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// revisit incomplete nodes where GTA could not complete
for pkg, nodes := range revisit {
for _, n := range nodes {
if _, err = interp.gta(n, pkg); err != nil {
if _, err = interp.gta(n, pkg, path); err != nil {
return err
}
}
@@ -94,7 +98,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// Generate control flow graphs
for _, root := range rootNodes {
var nodes []*node
if nodes, err = interp.cfg(root); err != nil {
if nodes, err = interp.cfg(root, path); err != nil {
return err
}
initNodes = append(initNodes, nodes...)
@@ -102,15 +106,13 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// Register source package in the interpreter. The package contains only
// the global symbols in the package scope.
interp.srcPkg[path] = interp.scopes[pkgName].sym
// Rename imported pkgName to alias if they are different
if pkgName != alias {
interp.scopes[alias] = interp.scopes[pkgName]
delete(interp.scopes, pkgName)
}
interp.mutex.Lock()
interp.srcPkg[path] = interp.scopes[path].sym
interp.frame.mutex.Lock()
interp.resizeFrame()
interp.frame.mutex.Unlock()
interp.mutex.Unlock()
// Once all package sources have been parsed, execute entry points then init functions
for _, n := range rootNodes {
@@ -185,7 +187,7 @@ func effectivePkg(root, path string) string {
for i := 0; i < len(splitPath); i++ {
part := splitPath[len(splitPath)-1-i]
if part == splitRoot[len(splitRoot)-1-rootIndex] {
if part == splitRoot[len(splitRoot)-1-rootIndex] && i != 0 {
prevRootIndex = rootIndex
rootIndex++
} else if prevRootIndex == rootIndex {

View File

@@ -103,30 +103,32 @@ type structField struct {
// itype defines the internal representation of types in the interpreter
type itype struct {
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
method []*node // Associated methods or nil
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
size int // Size of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number)
sizedef bool // true if array size is computed from type definition
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
method []*node // Associated methods or nil
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
size int // Size of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number)
sizedef bool // true if array size is computed from type definition
isBinMethod bool // true if the type refers to a bin method function
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
}
// nodeType returns a type definition for the corresponding AST subtree
func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
var err cfgError
if n.typ != nil && !n.typ.incomplete {
return n.typ, err
if n.kind == sliceExpr {
n.typ.sizedef = false
}
return n.typ, nil
}
var t = &itype{node: n, scope: sc}
@@ -141,6 +143,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
}
var err cfgError
switch n.kind {
case addressExpr, starExpr:
t.cat = ptrT
@@ -158,7 +161,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t.size = int(n.child[0].rval.Int())
case n.child[0].kind == ellipsisExpr:
// [...]T expression
t.sizedef = true
t.size = arrayTypeLen(n.anc)
default:
if sym, _, ok := sc.lookup(n.child[0].ident); ok {
// Resolve symbol to get size value
@@ -173,7 +176,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
} else {
// Evaluate constant array size expression
if _, err = interp.cfg(n.child[0]); err != nil {
if _, err = interp.cfg(n.child[0], sc.pkgID); err != nil {
return nil, err
}
t.incomplete = true
@@ -182,6 +185,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if t.val, err = nodeType(interp, sc, n.child[1]); err != nil {
return nil, err
}
t.sizedef = true
t.incomplete = t.incomplete || t.val.incomplete
} else {
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil {
@@ -285,6 +289,9 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
default:
err = n.cfgErrorf("invalid types %s and %s", t0.Kind(), t1.Kind())
}
if nt0.untyped && nt1.untyped {
t.untyped = true
}
}
case "real", "imag":
if t, err = nodeType(interp, sc, n.child[1]); err != nil {
@@ -297,7 +304,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
case k == reflect.Complex128:
t = sc.getType("float64")
case t.untyped && isNumber(t.TypeOf()):
t = &itype{cat: valueT, rtype: floatType}
t = &itype{cat: valueT, rtype: floatType, untyped: true}
default:
err = n.cfgErrorf("invalid complex type %s", k)
}
@@ -395,6 +402,9 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t.method = m
sym.typ = t
}
if t.node == nil {
t.node = n
}
} else {
t.incomplete = true
sc.sym[n.ident] = &symbol{kind: typeSym, typ: t}
@@ -473,7 +483,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if m, _ := lt.lookupMethod(name); m != nil {
t, err = nodeType(interp, sc, m.child[2])
} else if bm, _, _, ok := lt.lookupBinMethod(name); ok {
t = &itype{cat: valueT, rtype: bm.Type}
t = &itype{cat: valueT, rtype: bm.Type, isBinMethod: true}
} else if ti := lt.lookupField(name); len(ti) > 0 {
t = lt.fieldSeq(ti)
} else if bs, _, ok := lt.lookupBinField(name); ok {
@@ -483,6 +493,18 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
}
case sliceExpr:
t, err = nodeType(interp, sc, n.child[0])
if t.cat == ptrT {
t = t.val
}
if err == nil && t.size != 0 {
t1 := *t
t1.size = 0
t1.rtype = nil
t = &t1
}
case structType:
t.cat = structT
var incomplete bool
@@ -618,6 +640,74 @@ func (t *itype) finalize() (*itype, error) {
return t, err
}
// Equals returns true if the given type is identical to the receiver one.
func (t *itype) equals(o *itype) bool {
switch ti, oi := isInterface(t), isInterface(o); {
case ti && oi:
return t.methods().equals(o.methods())
case ti && !oi:
return o.methods().contains(t.methods())
case oi && !ti:
return t.methods().contains(o.methods())
default:
return t.id() == o.id()
}
}
// MethodSet defines the set of methods signatures as strings, indexed per method name.
type methodSet map[string]string
// Contains returns true if the method set m contains the method set n.
func (m methodSet) contains(n methodSet) bool {
for k, v := range n {
if m[k] != v {
return false
}
}
return true
}
// Equal returns true if the method set m is equal to the method set n.
func (m methodSet) equals(n methodSet) bool {
return m.contains(n) && n.contains(m)
}
// Methods returns a map of method type strings, indexed by method names.
func (t *itype) methods() methodSet {
res := make(methodSet)
switch t.cat {
case interfaceT:
// Get methods from recursive analysis of interface fields
for _, f := range t.field {
if f.typ.cat == funcT {
res[f.name] = f.typ.TypeOf().String()
} else {
for k, v := range f.typ.methods() {
res[k] = v
}
}
}
case valueT, errorT:
// Get method from corresponding reflect.Type
for i := t.rtype.NumMethod() - 1; i >= 0; i-- {
m := t.rtype.Method(i)
res[m.Name] = m.Type.String()
}
case ptrT:
// Consider only methods where receiver is a pointer to type t
for _, m := range t.val.method {
if m.child[0].child[0].lastChild().typ.cat == ptrT {
res[m.ident] = m.typ.TypeOf().String()
}
}
default:
for _, m := range t.method {
res[m.ident] = m.typ.TypeOf().String()
}
}
return res
}
// id returns a unique type identificator string
func (t *itype) id() string {
// TODO: if res is nil, build identity from String()
@@ -720,6 +810,22 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int,
return s, index, ok
}
// methodCallType returns a method function type without the receiver defined.
// The input type must be a method function type with the receiver as the first input argument.
func (t *itype) methodCallType() reflect.Type {
it := []reflect.Type{}
ni := t.rtype.NumIn()
for i := 1; i < ni; i++ {
it = append(it, t.rtype.In(i))
}
ot := []reflect.Type{}
no := t.rtype.NumOut()
for i := 0; i < no; i++ {
ot = append(ot, t.rtype.Out(i))
}
return reflect.FuncOf(it, ot, t.rtype.IsVariadic())
}
// getMethod returns a pointer to the method definition
func (t *itype) getMethod(name string) *node {
for _, m := range t.method {
@@ -796,14 +902,16 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
panic(err)
}
}
if t.val != nil && defined[t.val.name] {
if t.val != nil && defined[t.val.name] && !t.val.incomplete && t.val.rtype == nil {
// Replace reference to self (direct or indirect) by an interface{} to handle
// recursive types with reflect.
t.val.rtype = interf
}
switch t.cat {
case aliasT:
t.rtype = t.val.refType(defined)
case arrayT, variadicT:
if t.size > 0 {
if t.sizedef {
t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined))
} else {
t.rtype = reflect.SliceOf(t.val.refType(defined))
@@ -816,22 +924,16 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
in := make([]reflect.Type, len(t.arg))
out := make([]reflect.Type, len(t.ret))
for i, v := range t.arg {
if defined[v.name] {
v.rtype = interf
}
in[i] = v.refType(defined)
}
for i, v := range t.ret {
if defined[v.name] {
v.rtype = interf
}
out[i] = v.refType(defined)
}
t.rtype = reflect.FuncOf(in, out, false)
case interfaceT:
t.rtype = interf
case mapT:
t.rtype = reflect.MapOf(t.key.TypeOf(), t.val.TypeOf())
t.rtype = reflect.MapOf(t.key.refType(defined), t.val.refType(defined))
case ptrT:
t.rtype = reflect.PtrTo(t.val.refType(defined))
case structT:
@@ -866,7 +968,7 @@ func (t *itype) frameType() (r reflect.Type) {
case aliasT:
r = t.val.frameType()
case arrayT, variadicT:
if t.size > 0 {
if t.sizedef {
r = reflect.ArrayOf(t.size, t.val.frameType())
} else {
r = reflect.SliceOf(t.val.frameType())
@@ -915,7 +1017,13 @@ func isInterface(t *itype) bool {
return isInterfaceSrc(t) || t.TypeOf().Kind() == reflect.Interface
}
func isStruct(t *itype) bool { return t.TypeOf().Kind() == reflect.Struct }
func isStruct(t *itype) bool {
// Test first for a struct category, because a recursive interpreter struct may be
// represented by an interface{} at reflect level.
return t.cat == structT || t.TypeOf().Kind() == reflect.Struct
}
func isBool(t *itype) bool { return t.TypeOf().Kind() == reflect.Bool }
func isInt(t reflect.Type) bool {
switch t.Kind() {

View File

@@ -7,21 +7,31 @@ import (
func valueGenerator(n *node, i int) func(*frame) reflect.Value {
switch n.level {
case 0:
return func(f *frame) reflect.Value { return f.data[i] }
return func(f *frame) reflect.Value { return valueOf(f.data, i) }
case 1:
return func(f *frame) reflect.Value { return f.anc.data[i] }
return func(f *frame) reflect.Value { return valueOf(f.anc.data, i) }
case 2:
return func(f *frame) reflect.Value { return f.anc.anc.data[i] }
return func(f *frame) reflect.Value { return valueOf(f.anc.anc.data, i) }
default:
return func(f *frame) reflect.Value {
for level := n.level; level > 0; level-- {
f = f.anc
}
return f.data[i]
return valueOf(f.data, i)
}
}
}
// valueOf safely recovers the ith element of data. This is necessary
// because a cancellation prior to any evaluation result may leave
// the frame's data empty.
func valueOf(data []reflect.Value, i int) reflect.Value {
if i < len(data) {
return data[i]
}
return reflect.Value{}
}
func genValueRecvIndirect(n *node) func(*frame) reflect.Value {
v := genValueRecv(n)
return func(f *frame) reflect.Value { return v(f).Elem() }
@@ -45,9 +55,15 @@ func genValueRecv(n *node) func(*frame) reflect.Value {
}
func genValueAsFunctionWrapper(n *node) func(*frame) reflect.Value {
v := genValue(n)
value := genValue(n)
typ := n.typ.TypeOf()
return func(f *frame) reflect.Value {
return genFunctionWrapper(v(f).Interface().(*node))(f)
v := value(f)
if v.IsNil() {
return reflect.New(typ).Elem()
}
return genFunctionWrapper(v.Interface().(*node))(f)
}
}
@@ -118,6 +134,22 @@ func genValueArray(n *node) func(*frame) reflect.Value {
return value
}
func genValueRangeArray(n *node) func(*frame) reflect.Value {
value := genValue(n)
// dereference array pointer, to support array operations on array pointer
if n.typ.TypeOf().Kind() == reflect.Ptr {
return func(f *frame) reflect.Value {
return value(f).Elem()
}
}
return func(f *frame) reflect.Value {
// This is necessary to prevent changes in the returned
// reflect.Value being reflected back to the value used
// for the range expression.
return reflect.ValueOf(value(f).Interface())
}
}
func genValueInterfacePtr(n *node) func(*frame) reflect.Value {
value := genValue(n)
it := reflect.TypeOf((*interface{})(nil)).Elem()
@@ -133,15 +165,46 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
value := genValue(n)
return func(f *frame) reflect.Value {
return reflect.ValueOf(valueInterface{n, value(f)})
v := value(f)
nod := n
for {
// traverse interface indirections to find out concrete type
vi, ok := v.Interface().(valueInterface)
if !ok {
break
}
v = vi.value
nod = vi.node
}
return reflect.ValueOf(valueInterface{nod, v})
}
}
func zeroInterfaceValue() reflect.Value {
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true}}
v := reflect.New(reflect.TypeOf((*interface{})(nil)).Elem()).Elem()
return reflect.ValueOf(valueInterface{n, v})
}
func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
value := genValue(n)
return func(f *frame) reflect.Value {
return value(f).Interface().(valueInterface).value
v := value(f)
if v.Interface().(valueInterface).node == nil {
// Uninitialized interface value, set it to a correct zero value.
v.Set(zeroInterfaceValue())
v = value(f)
}
return v.Interface().(valueInterface).value
}
}
func genValueNode(n *node) func(*frame) reflect.Value {
value := genValue(n)
return func(f *frame) reflect.Value {
return reflect.ValueOf(&node{rval: value(f)})
}
}
@@ -153,6 +216,8 @@ func vInt(v reflect.Value) (i int64) {
i = int64(v.Uint())
case reflect.Float32, reflect.Float64:
i = int64(v.Float())
case reflect.Complex64, reflect.Complex128:
i = int64(real(v.Complex()))
}
return
}
@@ -165,6 +230,8 @@ func vUint(v reflect.Value) (i uint64) {
i = v.Uint()
case reflect.Float32, reflect.Float64:
i = uint64(v.Float())
case reflect.Complex64, reflect.Complex128:
i = uint64(real(v.Complex()))
}
return
}
@@ -191,6 +258,8 @@ func vFloat(v reflect.Value) (i float64) {
i = float64(v.Uint())
case reflect.Float32, reflect.Float64:
i = v.Float()
case reflect.Complex64, reflect.Complex128:
i = real(v.Complex())
}
return
}
@@ -205,6 +274,10 @@ func genValueInt(n *node) func(*frame) (reflect.Value, int64) {
return func(f *frame) (reflect.Value, int64) { v := value(f); return v, int64(v.Uint()) }
case reflect.Float32, reflect.Float64:
return func(f *frame) (reflect.Value, int64) { v := value(f); return v, int64(v.Float()) }
case reflect.Complex64, reflect.Complex128:
if n.typ.untyped && n.rval.IsValid() && imag(n.rval.Complex()) == 0 {
return func(f *frame) (reflect.Value, int64) { v := value(f); return v, int64(real(v.Complex())) }
}
}
return nil
}
@@ -219,6 +292,10 @@ func genValueUint(n *node) func(*frame) (reflect.Value, uint64) {
return func(f *frame) (reflect.Value, uint64) { v := value(f); return v, v.Uint() }
case reflect.Float32, reflect.Float64:
return func(f *frame) (reflect.Value, uint64) { v := value(f); return v, uint64(v.Float()) }
case reflect.Complex64, reflect.Complex128:
if n.typ.untyped && n.rval.IsValid() && imag(n.rval.Complex()) == 0 {
return func(f *frame) (reflect.Value, uint64) { v := value(f); return v, uint64(real(v.Complex())) }
}
}
return nil
}
@@ -233,22 +310,31 @@ func genValueFloat(n *node) func(*frame) (reflect.Value, float64) {
return func(f *frame) (reflect.Value, float64) { v := value(f); return v, float64(v.Uint()) }
case reflect.Float32, reflect.Float64:
return func(f *frame) (reflect.Value, float64) { v := value(f); return v, v.Float() }
case reflect.Complex64, reflect.Complex128:
if n.typ.untyped && n.rval.IsValid() && imag(n.rval.Complex()) == 0 {
return func(f *frame) (reflect.Value, float64) { v := value(f); return v, real(v.Complex()) }
}
}
return nil
}
func genValueComplex(n *node) func(*frame) (reflect.Value, complex128) {
func genValueComplex(n *node) func(*frame) reflect.Value {
vc := genComplex(n)
return func(f *frame) reflect.Value { return reflect.ValueOf(vc(f)) }
}
func genComplex(n *node) func(*frame) complex128 {
value := genValue(n)
switch n.typ.TypeOf().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return func(f *frame) (reflect.Value, complex128) { v := value(f); return v, complex(float64(v.Int()), 0) }
return func(f *frame) complex128 { return complex(float64(value(f).Int()), 0) }
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return func(f *frame) (reflect.Value, complex128) { v := value(f); return v, complex(float64(v.Uint()), 0) }
return func(f *frame) complex128 { return complex(float64(value(f).Uint()), 0) }
case reflect.Float32, reflect.Float64:
return func(f *frame) (reflect.Value, complex128) { v := value(f); return v, complex(v.Float(), 0) }
return func(f *frame) complex128 { return complex(value(f).Float(), 0) }
case reflect.Complex64, reflect.Complex128:
return func(f *frame) (reflect.Value, complex128) { v := value(f); return v, v.Complex() }
return func(f *frame) complex128 { return value(f).Complex() }
}
return nil
}

View File

@@ -1,47 +0,0 @@
// Code generated by 'goexports archive/tar'. DO NOT EDIT.
// +build go1.11,!go1.12
package stdlib
import (
"archive/tar"
"reflect"
)
func init() {
Symbols["archive/tar"] = map[string]reflect.Value{
// function, constant and variable definitions
"ErrFieldTooLong": reflect.ValueOf(&tar.ErrFieldTooLong).Elem(),
"ErrHeader": reflect.ValueOf(&tar.ErrHeader).Elem(),
"ErrWriteAfterClose": reflect.ValueOf(&tar.ErrWriteAfterClose).Elem(),
"ErrWriteTooLong": reflect.ValueOf(&tar.ErrWriteTooLong).Elem(),
"FileInfoHeader": reflect.ValueOf(tar.FileInfoHeader),
"FormatGNU": reflect.ValueOf(tar.FormatGNU),
"FormatPAX": reflect.ValueOf(tar.FormatPAX),
"FormatUSTAR": reflect.ValueOf(tar.FormatUSTAR),
"FormatUnknown": reflect.ValueOf(tar.FormatUnknown),
"NewReader": reflect.ValueOf(tar.NewReader),
"NewWriter": reflect.ValueOf(tar.NewWriter),
"TypeBlock": reflect.ValueOf(tar.TypeBlock),
"TypeChar": reflect.ValueOf(tar.TypeChar),
"TypeCont": reflect.ValueOf(tar.TypeCont),
"TypeDir": reflect.ValueOf(tar.TypeDir),
"TypeFifo": reflect.ValueOf(tar.TypeFifo),
"TypeGNULongLink": reflect.ValueOf(tar.TypeGNULongLink),
"TypeGNULongName": reflect.ValueOf(tar.TypeGNULongName),
"TypeGNUSparse": reflect.ValueOf(tar.TypeGNUSparse),
"TypeLink": reflect.ValueOf(tar.TypeLink),
"TypeReg": reflect.ValueOf(tar.TypeReg),
"TypeRegA": reflect.ValueOf(tar.TypeRegA),
"TypeSymlink": reflect.ValueOf(tar.TypeSymlink),
"TypeXGlobalHeader": reflect.ValueOf(tar.TypeXGlobalHeader),
"TypeXHeader": reflect.ValueOf(tar.TypeXHeader),
// type definitions
"Format": reflect.ValueOf((*tar.Format)(nil)),
"Header": reflect.ValueOf((*tar.Header)(nil)),
"Reader": reflect.ValueOf((*tar.Reader)(nil)),
"Writer": reflect.ValueOf((*tar.Writer)(nil)),
}
}

View File

@@ -1,36 +0,0 @@
// Code generated by 'goexports archive/zip'. DO NOT EDIT.
// +build go1.11,!go1.12
package stdlib
import (
"archive/zip"
"reflect"
)
func init() {
Symbols["archive/zip"] = map[string]reflect.Value{
// function, constant and variable definitions
"Deflate": reflect.ValueOf(zip.Deflate),
"ErrAlgorithm": reflect.ValueOf(&zip.ErrAlgorithm).Elem(),
"ErrChecksum": reflect.ValueOf(&zip.ErrChecksum).Elem(),
"ErrFormat": reflect.ValueOf(&zip.ErrFormat).Elem(),
"FileInfoHeader": reflect.ValueOf(zip.FileInfoHeader),
"NewReader": reflect.ValueOf(zip.NewReader),
"NewWriter": reflect.ValueOf(zip.NewWriter),
"OpenReader": reflect.ValueOf(zip.OpenReader),
"RegisterCompressor": reflect.ValueOf(zip.RegisterCompressor),
"RegisterDecompressor": reflect.ValueOf(zip.RegisterDecompressor),
"Store": reflect.ValueOf(zip.Store),
// type definitions
"Compressor": reflect.ValueOf((*zip.Compressor)(nil)),
"Decompressor": reflect.ValueOf((*zip.Decompressor)(nil)),
"File": reflect.ValueOf((*zip.File)(nil)),
"FileHeader": reflect.ValueOf((*zip.FileHeader)(nil)),
"ReadCloser": reflect.ValueOf((*zip.ReadCloser)(nil)),
"Reader": reflect.ValueOf((*zip.Reader)(nil)),
"Writer": reflect.ValueOf((*zip.Writer)(nil)),
}
}

View File

@@ -1,42 +0,0 @@
// Code generated by 'goexports bufio'. DO NOT EDIT.
// +build go1.11,!go1.12
package stdlib
import (
"bufio"
"reflect"
)
func init() {
Symbols["bufio"] = map[string]reflect.Value{
// function, constant and variable definitions
"ErrAdvanceTooFar": reflect.ValueOf(&bufio.ErrAdvanceTooFar).Elem(),
"ErrBufferFull": reflect.ValueOf(&bufio.ErrBufferFull).Elem(),
"ErrFinalToken": reflect.ValueOf(&bufio.ErrFinalToken).Elem(),
"ErrInvalidUnreadByte": reflect.ValueOf(&bufio.ErrInvalidUnreadByte).Elem(),
"ErrInvalidUnreadRune": reflect.ValueOf(&bufio.ErrInvalidUnreadRune).Elem(),
"ErrNegativeAdvance": reflect.ValueOf(&bufio.ErrNegativeAdvance).Elem(),
"ErrNegativeCount": reflect.ValueOf(&bufio.ErrNegativeCount).Elem(),
"ErrTooLong": reflect.ValueOf(&bufio.ErrTooLong).Elem(),
"MaxScanTokenSize": reflect.ValueOf(bufio.MaxScanTokenSize),
"NewReadWriter": reflect.ValueOf(bufio.NewReadWriter),
"NewReader": reflect.ValueOf(bufio.NewReader),
"NewReaderSize": reflect.ValueOf(bufio.NewReaderSize),
"NewScanner": reflect.ValueOf(bufio.NewScanner),
"NewWriter": reflect.ValueOf(bufio.NewWriter),
"NewWriterSize": reflect.ValueOf(bufio.NewWriterSize),
"ScanBytes": reflect.ValueOf(bufio.ScanBytes),
"ScanLines": reflect.ValueOf(bufio.ScanLines),
"ScanRunes": reflect.ValueOf(bufio.ScanRunes),
"ScanWords": reflect.ValueOf(bufio.ScanWords),
// type definitions
"ReadWriter": reflect.ValueOf((*bufio.ReadWriter)(nil)),
"Reader": reflect.ValueOf((*bufio.Reader)(nil)),
"Scanner": reflect.ValueOf((*bufio.Scanner)(nil)),
"SplitFunc": reflect.ValueOf((*bufio.SplitFunc)(nil)),
"Writer": reflect.ValueOf((*bufio.Writer)(nil)),
}
}

View File

@@ -1,70 +0,0 @@
// Code generated by 'goexports bytes'. DO NOT EDIT.
// +build go1.11,!go1.12
package stdlib
import (
"bytes"
"reflect"
)
func init() {
Symbols["bytes"] = map[string]reflect.Value{
// function, constant and variable definitions
"Compare": reflect.ValueOf(bytes.Compare),
"Contains": reflect.ValueOf(bytes.Contains),
"ContainsAny": reflect.ValueOf(bytes.ContainsAny),
"ContainsRune": reflect.ValueOf(bytes.ContainsRune),
"Count": reflect.ValueOf(bytes.Count),
"Equal": reflect.ValueOf(bytes.Equal),
"EqualFold": reflect.ValueOf(bytes.EqualFold),
"ErrTooLarge": reflect.ValueOf(&bytes.ErrTooLarge).Elem(),
"Fields": reflect.ValueOf(bytes.Fields),
"FieldsFunc": reflect.ValueOf(bytes.FieldsFunc),
"HasPrefix": reflect.ValueOf(bytes.HasPrefix),
"HasSuffix": reflect.ValueOf(bytes.HasSuffix),
"Index": reflect.ValueOf(bytes.Index),
"IndexAny": reflect.ValueOf(bytes.IndexAny),
"IndexByte": reflect.ValueOf(bytes.IndexByte),
"IndexFunc": reflect.ValueOf(bytes.IndexFunc),
"IndexRune": reflect.ValueOf(bytes.IndexRune),
"Join": reflect.ValueOf(bytes.Join),
"LastIndex": reflect.ValueOf(bytes.LastIndex),
"LastIndexAny": reflect.ValueOf(bytes.LastIndexAny),
"LastIndexByte": reflect.ValueOf(bytes.LastIndexByte),
"LastIndexFunc": reflect.ValueOf(bytes.LastIndexFunc),
"Map": reflect.ValueOf(bytes.Map),
"MinRead": reflect.ValueOf(bytes.MinRead),
"NewBuffer": reflect.ValueOf(bytes.NewBuffer),
"NewBufferString": reflect.ValueOf(bytes.NewBufferString),
"NewReader": reflect.ValueOf(bytes.NewReader),
"Repeat": reflect.ValueOf(bytes.Repeat),
"Replace": reflect.ValueOf(bytes.Replace),
"Runes": reflect.ValueOf(bytes.Runes),
"Split": reflect.ValueOf(bytes.Split),
"SplitAfter": reflect.ValueOf(bytes.SplitAfter),
"SplitAfterN": reflect.ValueOf(bytes.SplitAfterN),
"SplitN": reflect.ValueOf(bytes.SplitN),
"Title": reflect.ValueOf(bytes.Title),
"ToLower": reflect.ValueOf(bytes.ToLower),
"ToLowerSpecial": reflect.ValueOf(bytes.ToLowerSpecial),
"ToTitle": reflect.ValueOf(bytes.ToTitle),
"ToTitleSpecial": reflect.ValueOf(bytes.ToTitleSpecial),
"ToUpper": reflect.ValueOf(bytes.ToUpper),
"ToUpperSpecial": reflect.ValueOf(bytes.ToUpperSpecial),
"Trim": reflect.ValueOf(bytes.Trim),
"TrimFunc": reflect.ValueOf(bytes.TrimFunc),
"TrimLeft": reflect.ValueOf(bytes.TrimLeft),
"TrimLeftFunc": reflect.ValueOf(bytes.TrimLeftFunc),
"TrimPrefix": reflect.ValueOf(bytes.TrimPrefix),
"TrimRight": reflect.ValueOf(bytes.TrimRight),
"TrimRightFunc": reflect.ValueOf(bytes.TrimRightFunc),
"TrimSpace": reflect.ValueOf(bytes.TrimSpace),
"TrimSuffix": reflect.ValueOf(bytes.TrimSuffix),
// type definitions
"Buffer": reflect.ValueOf((*bytes.Buffer)(nil)),
"Reader": reflect.ValueOf((*bytes.Reader)(nil)),
}
}

Some files were not shown because too many files have changed in this diff Show More