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: Expected result:
```console ```console
$ go run ./sample.go $ go run ./sample.go
// ouput // output
``` ```
Got: Got:
```console ```console
$ yaegi ./sample.go $ yaegi ./sample.go
// ouput // output
``` ```

View File

@@ -5,7 +5,7 @@
[linters-settings] [linters-settings]
[linters-settings.govet] [linters-settings.govet]
check-shadowing = true check-shadowing = false
[linters-settings.gocyclo] [linters-settings.gocyclo]
min-complexity = 12.0 min-complexity = 12.0
@@ -32,7 +32,11 @@
"gocyclo", "gocyclo",
"gochecknoinits", "gochecknoinits",
"gochecknoglobals", "gochecknoglobals",
"typecheck", # v1.17.1 and Go1.13 => bug "wsl",
"godox",
"funlen",
"gocognit",
"stylecheck",
] ]
[issues] [issues]
@@ -41,10 +45,13 @@
max-same-issues = 0 max-same-issues = 0
exclude = [] 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]] [[issues.exclude-rules]]
path = "interp/.+_test\\.go" path = "interp/.+_test\\.go"
linters = ["goconst"] 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: matrix:
fast_finish: true fast_finish: true
include: include:
- go: 1.11.x
- go: 1.12.x - go: 1.12.x
- go: 1.13.x - go: 1.13.x
env: STABLE=true env: STABLE=true

View File

@@ -13,7 +13,12 @@ discussions.
Once the proposal is approved, a Pull Request can be opened. If you want 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]. 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 [Issues]: https://github.com/containous/yaegi/issues
[Pull Requests]: 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 [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/ [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: tests:
GO111MODULE=off go test -v ./... GO111MODULE=off go test -v ./...
GO111MODULE=off go test -race ./interp
.PHONY: check gen_all_syscall gen_tests .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
Documentation about Yaegi commands and libraries can be found at usual [godoc.org][docs]. 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 //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. Output files are written in the current directory, and prefixed with the go version.
@@ -26,6 +26,7 @@ import (
"go/constant" "go/constant"
"go/format" "go/format"
"go/importer" "go/importer"
"go/token"
"go/types" "go/types"
"io/ioutil" "io/ioutil"
"log" "log"
@@ -114,7 +115,7 @@ type Wrap struct {
} }
func genContent(dest, pkgName, license string) ([]byte, error) { 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 { if err != nil {
return nil, err return nil, err
} }
@@ -147,7 +148,12 @@ func genContent(dest, pkgName, license string) ([]byte, error) {
pname := path.Base(pkgName) + "." + name pname := path.Base(pkgName) + "." + name
switch o := o.(type) { switch o := o.(type) {
case *types.Const: 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: case *types.Func:
val[name] = Val{pname, false} val[name] = Val{pname, false}
case *types.Var: 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 // fixConst checks untyped constant value, converting it if necessary to avoid overflow
func fixConst(name string, val constant.Value) string { 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() str := val.ExactString()
i, err := strconv.ParseInt(str, 0, 64) i, err := strconv.ParseInt(str, 0, 64)
if err == nil { if err == nil {
@@ -265,8 +278,10 @@ func fixConst(name string, val constant.Value) string {
if err == nil { if err == nil {
return "uint64(" + name + ")" return "uint64(" + name + ")"
} }
return name
default:
return name
} }
return name
} }
// genLicense generates the correct LICENSE header text from the provided // 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 in a Read-Eval-Print-Loop. A prompt is displayed if standard input
is a terminal. 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 "#!/usr/bin/env yaegi", and the file has exec permission, then the file
can be invoked directly from the shell. can be invoked directly from the shell.
@@ -18,7 +18,10 @@ at global level in an implicit main package.
Options: Options:
-i -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): Debugging support (may be removed at any time):
YAEGI_AST_DOT=1 YAEGI_AST_DOT=1
@@ -43,7 +46,9 @@ import (
func main() { func main() {
var interactive bool var interactive bool
var tags string
flag.BoolVar(&interactive, "i", false, "start an interactive REPL") flag.BoolVar(&interactive, "i", false, "start an interactive REPL")
flag.StringVar(&tags, "tags", "", "set a list of build tags")
flag.Usage = func() { flag.Usage = func() {
fmt.Println("Usage:", os.Args[0], "[options] [script] [args]") fmt.Println("Usage:", os.Args[0], "[options] [script] [args]")
fmt.Println("Options:") fmt.Println("Options:")
@@ -53,7 +58,7 @@ func main() {
args := flag.Args() args := flag.Args()
log.SetFlags(log.Lshortfile) 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(stdlib.Symbols)
i.Use(interp.Symbols) i.Use(interp.Symbols)
@@ -79,9 +84,9 @@ func main() {
} }
if interactive { if interactive {
i.Repl(os.Stdin, os.Stdout) i.REPL(os.Stdin, os.Stdout)
} }
} else { } 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 { for _, test := range testCases {
test := test test := test
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
goPath, err := filepath.Abs(test.goPath) goPath, err := filepath.Abs(test.goPath)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -116,7 +115,6 @@ func TestPackagesError(t *testing.T) {
for _, test := range testCases { for _, test := range testCases {
test := test test := test
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
// Init go interpreter // Init go interpreter
i := interp.New(interp.Options{GoPath: test.goPath}) i := interp.New(interp.Options{GoPath: test.goPath})
i.Use(stdlib.Symbols) // Use binary standard library i.Use(stdlib.Symbols) // Use binary standard library

2
go.mod
View File

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

View File

@@ -10,7 +10,7 @@ import (
const model = `package interp 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" import "reflect"
@@ -352,6 +352,30 @@ func {{$name}}(n *node) {
} }
} }
{{end}} {{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}} {{range $name, $op := .Comparison}}
func {{$name}}(n *node) { func {{$name}}(n *node) {
tnext := getExec(n.tnext) 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): case isFloat(t0) || isFloat(t1):
switch { switch {
case c0.rval.IsValid(): 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}} {{end}}
@@ -793,6 +817,7 @@ type Op struct {
Float bool // true if operator applies to float Float bool // true if operator applies to float
Complex bool // true if operator applies to complex Complex bool // true if operator applies to complex
Shift bool // true if operator is a shift operation Shift bool // true if operator is a shift operation
Bool bool // true if operator applies to bool
} }
func main() { func main() {
@@ -805,17 +830,17 @@ func main() {
b := &bytes.Buffer{} b := &bytes.Buffer{}
data := map[string]interface{}{ data := map[string]interface{}{
"Arithmetic": map[string]Op{ "Arithmetic": map[string]Op{
"add": {"+", true, true, true, false}, "add": {"+", true, true, true, false, false},
"sub": {"-", false, true, true, false}, "sub": {"-", false, true, true, false, false},
"mul": {"*", false, true, true, false}, "mul": {"*", false, true, true, false, false},
"quo": {"/", false, true, true, false}, "quo": {"/", false, true, true, false, false},
"rem": {"%", false, false, false, false}, "rem": {"%", false, false, false, false, false},
"shl": {"<<", false, false, false, true}, "shl": {"<<", false, false, false, true, false},
"shr": {">>", false, false, false, true}, "shr": {">>", false, false, false, true, false},
"and": {"&", false, false, false, false}, "and": {"&", false, false, false, false, false},
"or": {"|", false, false, false, false}, "or": {"|", false, false, false, false, false},
"xor": {"^", false, false, false, false}, "xor": {"^", false, false, false, false, false},
"andNot": {"&^", false, false, false, false}, "andNot": {"&^", false, false, false, false, false},
}, },
"IncDec": map[string]Op{ "IncDec": map[string]Op{
"inc": {Name: "+"}, "inc": {Name: "+"},
@@ -829,6 +854,12 @@ func main() {
"lowerEqual": {Name: "<=", Complex: false}, "lowerEqual": {Name: "<=", Complex: false},
"notEqual": {Name: "!=", Complex: true}, "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 { if err = parse.Execute(b, data); err != nil {
log.Fatal(err) log.Fatal(err)

View File

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

View File

@@ -1,6 +1,7 @@
package interp package interp
import ( import (
"go/ast"
"go/build" "go/build"
"go/parser" "go/parser"
"path" "path"
@@ -11,7 +12,7 @@ import (
// buildOk returns true if a file or script matches build constraints // buildOk returns true if a file or script matches build constraints
// as specified in https://golang.org/pkg/go/build/#hdr-Build_Constraints. // as specified in https://golang.org/pkg/go/build/#hdr-Build_Constraints.
// An error from parser is returned as well. // 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 // Extract comments before the first clause
f, err := parser.ParseFile(interp.fset, name, src, parser.PackageClauseOnly|parser.ParseComments) f, err := parser.ParseFile(interp.fset, name, src, parser.PackageClauseOnly|parser.ParseComments)
if err != nil { 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 return true, nil
} }
// buildLineOk returns true if line is not a build constraint or // buildLineOk returns true if line is not a build constraint or
// if build constraint is satisfied // 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 " { if len(line) < 7 || line[:7] != "+build " {
return true 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 // 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 // in option, evaluate the AND of individual tags
for _, t := range strings.Split(tag, ",") { for _, t := range strings.Split(tag, ",") {
if !buildTagOk(ctx, t) { 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 // buildTagOk returns true if a build tag matches, false otherwise
// if first character is !, result is negated // 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] == '!' not := s[0] == '!'
if not { if not {
s = s[1:] s = s[1:]
@@ -82,6 +84,25 @@ func buildTagOk(ctx build.Context, s string) (r bool) {
return 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 { func contains(tags []string, tag string) bool {
for _, t := range tags { for _, t := range tags {
if t == tag { if t == tag {
@@ -92,7 +113,7 @@ func contains(tags []string, tag string) bool {
} }
// goMinorVersion returns the go minor version number // 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] current := ctx.ReleaseTags[len(ctx.ReleaseTags)-1]
v := strings.Split(current, ".") v := strings.Split(current, ".")
@@ -108,7 +129,7 @@ func goMinorVersion(ctx build.Context) int {
} }
// skipFile returns true if file should be skipped // 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") { if !strings.HasSuffix(p, ".go") {
return true return true
} }

View File

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

View File

@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"log" "log"
"math" "math"
"path"
"reflect" "reflect"
"regexp"
"unicode" "unicode"
) )
@@ -24,15 +24,26 @@ var constOp = map[action]func(*node){
aShr: shrConst, aShr: shrConst,
aAndNot: andNotConst, aAndNot: andNotConst,
aXor: xorConst, 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) // 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 // and pre-compute frame sizes and indexes for all un-named (temporary) and named
// variables. A list of nodes of init functions is returned. // variables. A list of nodes of init functions is returned.
// Following this pass, the CFG is ready to run // Following this pass, the CFG is ready to run
func (interp *Interpreter) cfg(root *node) ([]*node, error) { func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
sc, pkgName := interp.initScopePkg(root) sc := interp.initScopePkg(pkgID)
var loop, loopRestart *node
var initNodes []*node var initNodes []*node
var iotaValue int var iotaValue int
var err error var err error
@@ -77,14 +88,18 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
ktyp = &itype{cat: valueT, rtype: typ.Key()} ktyp = &itype{cat: valueT, rtype: typ.Key()}
vtyp = &itype{cat: valueT, rtype: typ.Elem()} vtyp = &itype{cat: valueT, rtype: typ.Elem()}
case reflect.String: case reflect.String:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int") ktyp = sc.getType("int")
vtyp = sc.getType("rune") vtyp = sc.getType("rune")
case reflect.Array, reflect.Slice: 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") ktyp = sc.getType("int")
vtyp = &itype{cat: valueT, rtype: typ.Elem()} vtyp = &itype{cat: valueT, rtype: typ.Elem()}
} }
case mapT: case mapT:
n.anc.gen = rangeMap n.anc.gen = rangeMap
ityp := &itype{cat: valueT, rtype: reflect.TypeOf((*reflect.MapIter)(nil))}
sc.add(ityp)
ktyp = o.typ.key ktyp = o.typ.key
vtyp = o.typ.val vtyp = o.typ.val
case ptrT: case ptrT:
@@ -96,9 +111,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
vtyp = vtyp.val vtyp = vtyp.val
} }
case stringT: case stringT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int") ktyp = sc.getType("int")
vtyp = sc.getType("rune") vtyp = sc.getType("rune")
case arrayT, variadicT: case arrayT, variadicT:
sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range
ktyp = sc.getType("int") ktyp = sc.getType("int")
vtyp = o.typ.val vtyp = o.typ.val
} }
@@ -158,19 +175,21 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
var typ *itype var typ *itype
if len(n.child) == 2 { if len(n.child) == 2 {
// 1 type in clause: define the var with this type in the case clause scope // 1 type in clause: define the var with this type in the case clause scope
switch sym, _, ok := sc.lookup(n.child[0].ident); { switch {
case ok && sym.kind == typeSym:
typ = sym.typ
case n.child[0].ident == "nil": case n.child[0].ident == "nil":
typ = sc.getType("interface{}") typ = sc.getType("interface{}")
default: case !n.child[0].isType(sc):
err = n.cfgErrorf("%s is not a type", n.child[0].ident) err = n.cfgErrorf("%s is not a type", n.child[0].ident)
return false default:
typ, err = nodeType(interp, sc, n.child[0])
} }
} else { } else {
// define the var with the type in the switch guard expression // define the var with the type in the switch guard expression
typ = sn.child[1].child[1].child[0].typ typ = sn.child[1].child[1].child[0].typ
} }
if err != nil {
return false
}
nod := n.lastChild().child[0] nod := n.lastChild().child[0]
index := sc.add(typ) index := sc.add(typ)
sc.sym[nod.ident] = &symbol{index: index, kind: varSym, typ: 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: case forStmt0, forRangeStmt:
loop, loopRestart = n, n.child[0]
sc = sc.pushBloc() sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.child[0]
case forStmt1, forStmt2, forStmt3, forStmt3a, forStmt4: case forStmt1, forStmt2, forStmt3, forStmt3a, forStmt4:
loop, loopRestart = n, n.lastChild()
sc = sc.pushBloc() sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.lastChild()
case funcLit: case funcLit:
n.typ = nil // to force nodeType to recompute the type 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 { if len(n.child[0].child) > 0 {
// define receiver symbol // define receiver symbol
var typ *itype var typ *itype
recvName := n.child[0].child[0].child[0].ident fr := n.child[0].child[0]
recvTypeNode := n.child[0].child[0].lastChild() recvTypeNode := fr.lastChild()
if typ, err = nodeType(interp, sc, recvTypeNode); err != nil { if typ, err = nodeType(interp, sc, recvTypeNode); err != nil {
return false return false
} }
recvTypeNode.typ = typ 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 { for _, c := range n.child[2].child[0].child {
// define input parameter symbols // define input parameter symbols
@@ -290,7 +312,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
c[i], c[l] = c[l], c[i] c[i], c[l] = c[l], c[i]
} }
sc = sc.pushBloc() sc = sc.pushBloc()
loop = n sc.loop = n
case importSpec: case importSpec:
var name, ipath string var name, ipath string
@@ -299,7 +321,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
name = n.child[0].ident name = n.child[0].ident
} else { } else {
ipath = n.child[0].rval.String() ipath = n.child[0].rval.String()
name = path.Base(ipath) name = identifier.FindString(ipath)
} }
if interp.binPkg[ipath] != nil && name != "." { if interp.binPkg[ipath] != nil && name != "." {
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}} 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 { if src.typ, err = nodeType(interp, sc, src); err != nil {
return 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 { if dest.typ.sizedef {
dest.typ.size = compositeArrayLen(src) dest.typ.size = arrayTypeLen(src)
dest.typ.rtype = nil dest.typ.rtype = nil
} }
if sc.global { 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) sym, _, _ = sc.lookup(dest.ident)
} else { } else {
sym = &symbol{index: sc.add(dest.typ), kind: varSym, typ: dest.typ} 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 n.gen = nop
src.level = level src.level = level
src.findex = dest.findex src.findex = dest.findex
if src.typ.untyped && !dest.typ.untyped {
src.typ = dest.typ
}
case n.action == aAssign && src.action == aRecv: case n.action == aAssign && src.action == aRecv:
// Assign by reading from a receiving channel // Assign by reading from a receiving channel
n.gen = nop n.gen = nop
src.findex = dest.findex // Set recv address to LHS 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: case n.action == aAssign && src.action == aCompositeLit:
n.gen = nop n.gen = nop
src.findex = dest.findex src.findex = dest.findex
@@ -454,6 +483,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
// TODO: perform constant folding and propagation here // TODO: perform constant folding and propagation here
switch { switch {
case dest.typ.cat == interfaceT: case dest.typ.cat == interfaceT:
case isComplex(dest.typ.TypeOf()):
// value set in genValue // value set in genValue
case !src.rval.IsValid(): case !src.rval.IsValid():
// Assign to nil // Assign to nil
@@ -498,7 +528,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[l].gen = getIndexMap2 n.child[l].gen = getIndexMap2
n.gen = nop n.gen = nop
case typeAssertExpr: 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 n.gen = nop
case unaryExpr: case unaryExpr:
if n.child[l].action == aRecv { 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() t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
// Shift operator type is inherited from first parameter only // Shift operator type is inherited from first parameter only
// All other binary operators require both parameter types to be the same // 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()) err = n.cfgErrorf("mismatched types %s and %s", c0.typ.id(), c1.typ.id())
break break
} }
@@ -575,8 +609,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
constOp[n.action](n) constOp[n.action](n)
} }
switch { switch {
//case n.typ != nil && n.typ.cat == BoolT && isAncBranch(n):
// n.findex = -1
case n.rval.IsValid(): case n.rval.IsValid():
n.gen = nop n.gen = nop
n.findex = -1 n.findex = -1
@@ -611,7 +643,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case stringT: case stringT:
n.typ = sc.getType("byte") n.typ = sc.getType("byte")
case valueT: 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: default:
n.typ = t.val n.typ = t.val
} }
@@ -665,14 +701,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if len(n.child) > 0 { if len(n.child) > 0 {
gotoLabel(n.sym) gotoLabel(n.sym)
} else { } else {
n.tnext = loop n.tnext = sc.loop
} }
case continueStmt: case continueStmt:
if len(n.child) > 0 { if len(n.child) > 0 {
gotoLabel(n.sym) gotoLabel(n.sym)
} else { } else {
n.tnext = loopRestart n.tnext = sc.loopRestart
} }
case gotoStmt: case gotoStmt:
@@ -698,6 +734,10 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
} else { } else {
n.findex = sc.add(n.typ) 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): case n.child[0].isType(sc):
// Type conversion expression // Type conversion expression
if isInt(n.child[0].typ.TypeOf()) && n.child[1].kind == basicLit && isFloat(n.child[1].typ.TypeOf()) { 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] body := n.child[0]
n.start = body.start n.start = body.start
body.tnext = n.start body.tnext = n.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt1: // for cond {} case forStmt1: // for cond {}
cond, body := n.child[0], n.child[1] 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 n.start = cond.start
cond.tnext = body.start cond.tnext = body.start
cond.fnext = n cond.fnext = n
body.tnext = cond.start body.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt2: // for init; cond; {} case forStmt2: // for init; cond; {}
init, cond, body := n.child[0], n.child[1], n.child[2] 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 n.start = init.start
init.tnext = cond.start init.tnext = cond.start
cond.tnext = body.start cond.tnext = body.start
cond.fnext = n cond.fnext = n
body.tnext = cond.start body.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt3: // for ; cond; post {} case forStmt3: // for ; cond; post {}
cond, post, body := n.child[0], n.child[1], n.child[2] 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 n.start = cond.start
cond.tnext = body.start cond.tnext = body.start
cond.fnext = n cond.fnext = n
body.tnext = post.start body.tnext = post.start
post.tnext = cond.start post.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt3a: // for int; ; post {} case forStmt3a: // for int; ; post {}
@@ -830,22 +875,22 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
init.tnext = body.start init.tnext = body.start
body.tnext = post.start body.tnext = post.start
post.tnext = body.start post.tnext = body.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forStmt4: // for init; cond; post {} case forStmt4: // for init; cond; post {}
init, cond, post, body := n.child[0], n.child[1], n.child[2], n.child[3] 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 n.start = init.start
init.tnext = cond.start init.tnext = cond.start
cond.tnext = body.start cond.tnext = body.start
cond.fnext = n cond.fnext = n
body.tnext = post.start body.tnext = post.start
post.tnext = cond.start post.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop() sc = sc.pop()
case forRangeStmt: case forRangeStmt:
loop, loopRestart = nil, nil
n.start = n.child[0].start n.start = n.child[0].start
n.child[0].fnext = n n.child[0].fnext = n
sc = sc.pop() sc = sc.pop()
@@ -856,15 +901,16 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
sc = sc.pop() sc = sc.pop()
funcName := n.child[1].ident funcName := n.child[1].ident
if !isMethod(n) { if !isMethod(n) {
interp.scopes[pkgName].sym[funcName].index = -1 // to force value to n.val interp.scopes[pkgID].sym[funcName].index = -1 // to force value to n.val
interp.scopes[pkgName].sym[funcName].typ = n.typ interp.scopes[pkgID].sym[funcName].typ = n.typ
interp.scopes[pkgName].sym[funcName].kind = funcSym interp.scopes[pkgID].sym[funcName].kind = funcSym
interp.scopes[pkgName].sym[funcName].node = n interp.scopes[pkgID].sym[funcName].node = n
} }
case funcLit: case funcLit:
n.types = sc.types n.types = sc.types
sc = sc.pop() sc = sc.pop()
err = genRun(n)
case goStmt: case goStmt:
wireChild(n) wireChild(n)
@@ -917,6 +963,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case ifStmt0: // if cond {} case ifStmt0: // if cond {}
cond, tbody := n.child[0], n.child[1] 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 n.start = cond.start
cond.tnext = tbody.start cond.tnext = tbody.start
cond.fnext = n cond.fnext = n
@@ -925,6 +974,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case ifStmt1: // if cond {} else {} case ifStmt1: // if cond {} else {}
cond, tbody, fbody := n.child[0], n.child[1], n.child[2] 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 n.start = cond.start
cond.tnext = tbody.start cond.tnext = tbody.start
cond.fnext = fbody.start cond.fnext = fbody.start
@@ -934,6 +986,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case ifStmt2: // if init; cond {} case ifStmt2: // if init; cond {}
init, cond, tbody := n.child[0], n.child[1], n.child[2] 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 n.start = init.start
tbody.tnext = n tbody.tnext = n
init.tnext = cond.start init.tnext = cond.start
@@ -943,6 +998,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case ifStmt3: // if init; cond {} else {} case ifStmt3: // if init; cond {} else {}
init, cond, tbody, fbody := n.child[0], n.child[1], n.child[2], n.child[3] 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 n.start = init.start
init.tnext = cond.start init.tnext = cond.start
cond.tnext = tbody.start cond.tnext = tbody.start
@@ -961,6 +1019,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n n.child[1].tnext = n
n.typ = n.child[0].typ n.typ = n.child[0].typ
n.findex = sc.add(n.typ) n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case lorExpr: case lorExpr:
n.start = n.child[0].start n.start = n.child[0].start
@@ -969,6 +1030,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n n.child[1].tnext = n
n.typ = n.child[0].typ n.typ = n.child[0].typ
n.findex = sc.add(n.typ) n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case parenExpr: case parenExpr:
wireChild(n) wireChild(n)
@@ -1008,10 +1072,15 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
// nil: Set node value to zero of return type // nil: Set node value to zero of return type
f := sc.def f := sc.def
var typ *itype 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 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.val = method.Index
n.gen = getIndexBinMethod n.gen = getIndexBinMethod
n.recv = &receiver{node: n.child[0]} 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: case n.typ.rtype.Kind() == reflect.Ptr:
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok { if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type} 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.typ = &itype{cat: valueT, rtype: field.Type}
n.val = field.Index n.val = field.Index
n.gen = getPtrIndexSeq n.gen = getPtrIndexSeq
} else { } else {
err = n.cfgErrorf("undefined selector: %s", n.child[1].ident) 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()} n.typ = &itype{cat: valueT, rtype: s.Type().Elem()}
} else { } else {
n.kind = rvalueExpr 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.rval = s
} }
n.gen = nop n.gen = nop
@@ -1248,7 +1316,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.start = sbn.start n.start = sbn.start
} }
sc = sc.pop() sc = sc.pop()
loop = nil
case switchIfStmt: // like an if-else chain case switchIfStmt: // like an if-else chain
sbn := n.lastChild() // switch block node sbn := n.lastChild() // switch block node
@@ -1285,13 +1352,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.start = sbn.start n.start = sbn.start
} }
sc = sc.pop() sc = sc.pop()
loop = nil
case typeAssertExpr: case typeAssertExpr:
if len(n.child) > 1 { if len(n.child) > 1 {
wireChild(n) wireChild(n)
if n.child[1].typ == nil { 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 { if n.anc.action != aAssignX {
n.typ = n.child[1].typ n.typ = n.child[1].typ
@@ -1303,26 +1371,68 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case sliceExpr: case sliceExpr:
wireChild(n) wireChild(n)
ctyp := n.child[0].typ if n.typ, err = nodeType(interp, sc, n); err != nil {
if ctyp.cat == ptrT { return
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
} }
n.findex = sc.add(n.typ) n.findex = sc.add(n.typ)
case unaryExpr: case unaryExpr:
wireChild(n) wireChild(n)
n.typ = n.child[0].typ 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) // 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: case valueSpec:
n.gen = reset n.gen = reset
@@ -1333,8 +1443,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
} }
} }
for _, c := range n.child[:l] { for _, c := range n.child[:l] {
index := sc.add(n.typ) var index int
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ} 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.typ = n.typ
c.findex = index c.findex = index
} }
@@ -1351,9 +1467,9 @@ func compDefineX(sc *scope, n *node) error {
l := len(n.child) - 1 l := len(n.child) - 1
types := []*itype{} types := []*itype{}
switch n.child[l].kind { switch src := n.child[l]; src.kind {
case callExpr: 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 { if err != nil {
return err return err
} }
@@ -1368,18 +1484,22 @@ func compDefineX(sc *scope, n *node) error {
n.gen = nop n.gen = nop
case indexExpr: 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.child[l].gen = getIndexMap2
n.gen = nop n.gen = nop
case typeAssertExpr: 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")) types = append(types, n.child[l].child[1].typ, sc.getType("bool"))
n.child[l].gen = typeAssert2
n.gen = nop n.gen = nop
case unaryExpr: case unaryExpr:
if n.child[l].action == aRecv { 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.child[l].gen = recv2
n.gen = nop 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 // isType returns true if node refers to a type definition, false otherwise
func (n *node) isType(sc *scope) bool { func (n *node) isType(sc *scope) bool {
switch n.kind { switch n.kind {
case arrayType, chanType, funcType, mapType, structType, rtypeExpr: case arrayType, chanType, funcType, interfaceType, mapType, structType, rtypeExpr:
return true return true
case parenExpr, starExpr: case parenExpr, starExpr:
if len(n.child) == 1 { if len(n.child) == 1 {
@@ -1468,7 +1588,7 @@ func (n *node) isType(sc *scope) bool {
} }
case selectorExpr: case selectorExpr:
pkg, name := n.child[0].ident, n.child[1].ident 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 path := sym.typ.path
if p, ok := n.interp.binPkg[path]; ok && isBinType(p[name]) { if p, ok := n.interp.binPkg[path]; ok && isBinType(p[name]) {
return true // Imported binary type return true // Imported binary type
@@ -1538,7 +1658,7 @@ func (n *node) isInteger() bool {
if isFloat(t) { if isFloat(t) {
// untyped float constant with null decimal part is ok // untyped float constant with null decimal part is ok
f := n.rval.Float() f := n.rval.Float()
if f == math.Round(f) { if f == math.Trunc(f) {
n.rval = reflect.ValueOf(int(f)) n.rval = reflect.ValueOf(int(f))
n.typ.rtype = n.rval.Type() n.typ.rtype = n.rval.Type()
return true return true
@@ -1565,7 +1685,7 @@ func (n *node) isNatural() bool {
if isFloat(t) { if isFloat(t) {
// positive untyped float constant with null decimal part is ok // positive untyped float constant with null decimal part is ok
f := n.rval.Float() 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.rval = reflect.ValueOf(uint(f))
n.typ.rtype = n.rval.Type() n.typ.rtype = n.rval.Type()
return true return true
@@ -1575,6 +1695,29 @@ func (n *node) isNatural() bool {
return false 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 // lastChild returns the last child of a node
func (n *node) lastChild() *node { return n.child[len(n.child)-1] } 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 || return n.anc.kind == fileStmt ||
(n.anc.kind == selectorExpr && n.anc.child[0] != n) || (n.anc.kind == selectorExpr && n.anc.child[0] != n) ||
(n.anc.kind == funcDecl && isMethod(n.anc)) || (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 // isNewDefine returns true if node refers to a new definition
@@ -1649,13 +1793,6 @@ func getExec(n *node) bltn {
return n.exec 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 // setExec recursively sets the node exec builtin function by walking the CFG
// from the entry point (first node to exec). // from the entry point (first node to exec).
func setExec(n *node) { func setExec(n *node) {
@@ -1735,13 +1872,20 @@ func compositeGenerator(n *node) (gen bltnGenerator) {
return return
} }
// compositeArrayLen return the litteral array length, computed from definition // arrayTypeLen returns the node's array length. If the expression is an
func compositeArrayLen(n *node) int { // 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 max := -1
for i, c := range n.child[1:] { for i, c := range n.child[1:] {
r := i r := i
if c.kind == keyValueExpr { 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 { if r > max {
max = r max = r
@@ -1749,3 +1893,13 @@ func compositeArrayLen(n *node) int {
} }
return max + 1 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 For the Go language itself, refer to the official Go specification
https://golang.org/ref/spec. 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 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) { func (n *node) cfgDot(out io.Writer) {
fmt.Fprintf(out, "digraph cfg {\n") fmt.Fprintf(out, "digraph cfg {\n")
n.Walk(nil, func(n *node) { n.Walk(nil, func(n *node) {
if n.kind == basicLit || n.kind == identExpr || n.tnext == nil { if n.kind == basicLit || n.tnext == nil {
return return
} }
var label string var label string

View File

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

View File

@@ -2,13 +2,18 @@ package interp
import ( import (
"bufio" "bufio"
"context"
"fmt" "fmt"
"go/build" "go/build"
"go/scanner" "go/scanner"
"go/token" "go/token"
"io"
"os" "os"
"os/signal"
"reflect" "reflect"
"strconv" "strconv"
"sync"
"sync/atomic"
) )
// Interpreter node structure for AST and CFG // Interpreter node structure for AST and CFG
@@ -20,7 +25,7 @@ type node struct {
fnext *node // false branch successor (CFG) fnext *node // false branch successor (CFG)
interp *Interpreter // interpreter context interp *Interpreter // interpreter context
frame *frame // frame pointer used for closures only (TODO: suppress this) 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) findex int // index of value in frame or frame size (func def, type def)
level int // number of frame indirections to access value level int // number of frame indirections to access value
nleft int // number of children in left part (assign) 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) // frame contains values for the current execution level (a function context)
type frame struct { type frame struct {
anc *frame // ancestor frame (global space) // id is an atomic counter used for cancellation, only access
data []reflect.Value // values // via newFrame/runid/setrunid/clone.
deferred [][]reflect.Value // defer stack // Located at start of struct to ensure proper aligment.
recovered interface{} // to handle panic recover 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 // 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 // opt stores interpreter options
type opt struct { type opt struct {
astDot bool // display AST graph (debug) astDot bool // display AST graph (debug)
cfgDot bool // display CFG graph (debug) cfgDot bool // display CFG graph (debug)
noRun bool // compile, but do not run noRun bool // compile, but do not run
context build.Context // build context: GOPATH, build constraints fastChan bool // disable cancellable chan operations
context build.Context // build context: GOPATH, build constraints
} }
// Interpreter contains global resources and state // Interpreter contains global resources and state
type Interpreter struct { 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 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 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 universe *scope // interpreter global level scope
scopes map[string]*scope // package level scopes, indexed by package name 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 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 ( const (
@@ -146,15 +198,17 @@ func New(options Options) *Interpreter {
i.opt.context.BuildTags = options.BuildTags 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")) 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")) 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")) 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 return &i
} }
@@ -227,7 +281,9 @@ func (interp *Interpreter) resizeFrame() {
} }
func (interp *Interpreter) main() *node { 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 m.sym[mainID].node
} }
return nil return nil
@@ -252,18 +308,18 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
} }
// Global type analysis // Global type analysis
revisit, err := interp.gta(root, pkgName) revisit, err := interp.gta(root, pkgName, interp.Name)
if err != nil { if err != nil {
return res, err return res, err
} }
for _, n := range revisit { 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 return res, err
} }
} }
// Annotate AST with CFG infos // Annotate AST with CFG infos
initNodes, err := interp.cfg(root) initNodes, err := interp.cfg(root, interp.Name)
if err != nil { if err != nil {
return res, err return res, err
} }
@@ -277,11 +333,13 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
// REPL may skip package statement // REPL may skip package statement
setExec(root.start) setExec(root.start)
} }
interp.mutex.Lock()
if interp.universe.sym[pkgName] == nil { if interp.universe.sym[pkgName] == nil {
// Make the package visible under a path identical to its name // 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.universe.sym[pkgName] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: pkgName}}
} }
interp.mutex.Unlock()
if interp.cfgDot { if interp.cfgDot {
root.cfgDot(dotX()) root.cfgDot(dotX())
@@ -291,11 +349,18 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
return res, err return res, err
} }
// Execute CFG // Generate node exec closures
if err = genRun(root); err != nil { if err = genRun(root); err != nil {
return res, err return res, err
} }
// Init interpreter execution memory frame
interp.frame.setrunid(interp.runid())
interp.frame.mutex.Lock()
interp.resizeFrame() interp.resizeFrame()
interp.frame.mutex.Unlock()
// Execute node closures
interp.run(root, nil) interp.run(root, nil)
for _, n := range initNodes { for _, n := range initNodes {
@@ -314,6 +379,42 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
return res, err 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 // getWrapper returns the wrapper type of the corresponding interface, or nil if not found
func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type { func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
if p, ok := interp.binPkg[t.PkgPath()]; ok { 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. // REPL performs a Read-Eval-Print-Loop on input reader.
// Results are printed on output. // Results are printed on output writer.
func (interp *Interpreter) Repl(in, out *os.File) { func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
s := bufio.NewScanner(in) s := bufio.NewScanner(in)
prompt := getPrompt(in, out) prompt := getPrompt(in, out)
prompt() prompt()
src := "" src := ""
for s.Scan() { for s.Scan() {
src += s.Text() + "\n" 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) { switch err.(type) {
case scanner.ErrorList: case scanner.ErrorList:
// Early failure in the scanner: the source is incomplete // 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 // Repl performs a Read-Eval-Print-Loop on input file descriptor.
func getPrompt(in, out *os.File) func() { // Results are printed on output.
if stat, err := in.Stat(); err == nil && stat.Mode()&os.ModeCharDevice != 0 { // 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() { fmt.Fprint(out, "> ") }
} }
return func() {} 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() == "bad0.go" || // expect error
file.Name() == "export1.go" || // non-main package file.Name() == "export1.go" || // non-main package
file.Name() == "export0.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() == "import6.go" || // expect error
file.Name() == "io0.go" || // use random number file.Name() == "io0.go" || // use random number
file.Name() == "op1.go" || // expect error file.Name() == "op1.go" || // expect error
@@ -104,7 +106,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
bin := filepath.Join(dir, strings.TrimSuffix(file.Name(), ".go")) 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() outBuild, err := cmdBuild.CombinedOutput()
if err != nil { if err != nil {
t.Log(string(outBuild)) t.Log(string(outBuild))
@@ -136,6 +138,16 @@ func TestInterpErrorConsistency(t *testing.T) {
expectedInterp: "1:1: expected 'package', found println", expectedInterp: "1:1: expected 'package', found println",
expectedExec: "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", fileName: "op1.go",
expectedInterp: "5:2: illegal operand types for '+=' operator", expectedInterp: "5:2: illegal operand types for '+=' operator",

View File

@@ -1,6 +1,7 @@
package interp_test package interp_test
import ( import (
"context"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
@@ -41,16 +42,22 @@ func TestEvalArithmetic(t *testing.T) {
{desc: "mul_II", src: "2 * 3", res: "6"}, {desc: "mul_II", src: "2 * 3", res: "6"},
{desc: "mul_FI", src: "2.2 * 3", res: "6.6000000000000005"}, {desc: "mul_FI", src: "2.2 * 3", res: "6.6000000000000005"},
{desc: "mul_IF", src: "3 * 2.2", 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_II", src: "1 << 8", res: "256"},
{desc: "shl_IN", src: "1 << -1", err: "1:28: illegal operand types for '<<' operator"}, {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_IF", src: "1 << 1.0", res: "2"},
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: illegal operand types for '<<' operator"}, {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_II", src: "1 >> 8", res: "0"},
{desc: "shr_IN", src: "1 >> -1", err: "1:28: illegal operand types for '>>' operator"}, {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_IF", src: "1 >> 1.0", res: "0"},
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: illegal operand types for '>>' operator"}, {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: `a := "Hello"; a += " world"`, res: "Hello world"},
{src: `b := "Hello"; b += 1`, err: "1:42: illegal operand types for '+=' operator"}, {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: `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: "f := int64(3.2)", err: "1:33: truncated to integer"},
{src: "g := 1; g <<= 8", res: "256"}, {src: "g := 1; g <<= 8", res: "256"},
{src: "h := 1; h >>= 8", res: "0"}, {src: "h := 1; h >>= 8", res: "0"},
@@ -155,6 +162,14 @@ func TestEvalNil(t *testing.T) {
src: "Hello()", src: "Hello()",
res: "<nil>", 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) { func runTests(t *testing.T, i *interp.Interpreter, tests []testCase) {
for _, test := range tests { for _, test := range tests {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {

View File

@@ -1,6 +1,6 @@
package interp 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" 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) { func equal(n *node) {
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
dest := genValue(n) 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): case isFloat(t0) || isFloat(t1):
switch { switch {
case c0.rval.IsValid(): 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): case isFloat(t0) || isFloat(t1):
switch { switch {
case c0.rval.IsValid(): 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, aAndAssign: andAssign,
aAndNot: andNot, aAndNot: andNot,
aAndNotAssign: andNotAssign, aAndNotAssign: andNotAssign,
aBitNot: bitNot,
aCall: call, aCall: call,
aCase: _case, aCase: _case,
aCompositeLit: arrayLit, aCompositeLit: arrayLit,
@@ -40,11 +41,12 @@ var builtin = [...]bltnGenerator{
aLowerEqual: lowerEqual, aLowerEqual: lowerEqual,
aMul: mul, aMul: mul,
aMulAssign: mulAssign, aMulAssign: mulAssign,
aNegate: negate, aNeg: neg,
aNot: not, aNot: not,
aNotEqual: notEqual, aNotEqual: notEqual,
aOr: or, aOr: or,
aOrAssign: orAssign, aOrAssign: orAssign,
aPos: pos,
aQuo: quo, aQuo: quo,
aQuoAssign: quoAssign, aQuoAssign: quoAssign,
aRange: _range, aRange: _range,
@@ -84,8 +86,11 @@ func (interp *Interpreter) run(n *node, cf *frame) {
if cf == nil { if cf == nil {
f = interp.frame f = interp.frame
} else { } 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 { for i, t := range n.types {
f.data[i] = reflect.New(t).Elem() 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 // runCfg executes a node AST by walking its CFG and running node builtin at each step
func runCfg(n *node, f *frame) { func runCfg(n *node, f *frame) {
defer func() { defer func() {
f.mutex.Lock()
f.recovered = recover() f.recovered = recover()
for _, val := range f.deferred { for _, val := range f.deferred {
val[0].Call(val[1:]) val[0].Call(val[1:])
} }
if f.recovered != nil { if f.recovered != nil {
fmt.Println(n.cfgErrorf("panic")) fmt.Println(n.cfgErrorf("panic"))
f.mutex.Unlock()
panic(f.recovered) 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) exec = exec(f)
} }
} }
func typeAssert(n *node) { func typeAssertStatus(n *node) {
value := genValue(n.child[0]) value := genValue(n.child[0]) // input value
i := n.findex value1 := genValue(n.anc.child[1]) // returned status
next := getExec(n.tnext) next := getExec(n.tnext)
switch { switch {
case n.child[0].typ.cat == valueT: case n.child[0].typ.cat == valueT:
n.exec = func(f *frame) bltn { 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 return next
} }
case n.child[1].typ.cat == interfaceT: case n.child[1].typ.cat == interfaceT:
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface) 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 return next
} }
default: default:
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface) v := value(f).Interface().(valueInterface)
f.data[i].Set(v.value) dest(f).Set(v.value)
return next return next
} }
} }
@@ -202,7 +241,7 @@ func isRecursiveStruct(t *itype, rtype reflect.Type) bool {
if t.cat == structT && rtype.Kind() == reflect.Interface { if t.cat == structT && rtype.Kind() == reflect.Interface {
return true return true
} }
if t.cat == ptrT { if t.cat == ptrT && t.rtype != nil {
return isRecursiveStruct(t.val, t.rtype.Elem()) return isRecursiveStruct(t.val, t.rtype.Elem())
} }
return false return false
@@ -223,15 +262,21 @@ func assign(n *node) {
switch { switch {
case dest.typ.cat == interfaceT: case dest.typ.cat == interfaceT:
svalue[i] = genValueInterface(src) 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) svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
case dest.typ.cat == valueT && src.typ.cat == funcT: case dest.typ.cat == valueT && src.typ.cat == funcT:
svalue[i] = genFunctionWrapper(src) svalue[i] = genFunctionWrapper(src)
case dest.typ.cat == funcT && src.typ.cat == valueT:
svalue[i] = genValueNode(src)
case src.kind == basicLit && src.val == nil: case src.kind == basicLit && src.val == nil:
t := dest.typ.TypeOf() t := dest.typ.TypeOf()
svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() } svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() }
case isRecursiveStruct(dest.typ, dest.typ.rtype): case isRecursiveStruct(dest.typ, dest.typ.rtype):
svalue[i] = genValueInterfacePtr(src) 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: default:
svalue[i] = genValue(src) svalue[i] = genValue(src)
} }
@@ -248,12 +293,17 @@ func assign(n *node) {
} }
if n.nleft == 1 { 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 { n.exec = func(f *frame) bltn {
d(f).SetMapIndex(i(f), s(f)) d(f).SetMapIndex(i(f), s(f))
return next return next
} }
} else { default:
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
d(f).Set(s(f)) d(f).Set(s(f))
return next return next
@@ -271,7 +321,6 @@ func assign(n *node) {
default: default:
t = typ.TypeOf() t = typ.TypeOf()
} }
//types[i] = n.child[sbase+i].typ.TypeOf()
types[i] = t types[i] = t
} }
@@ -281,10 +330,16 @@ func assign(n *node) {
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
t := make([]reflect.Value, len(svalue)) t := make([]reflect.Value, len(svalue))
for i, s := range svalue { for i, s := range svalue {
if n.child[i].ident == "_" {
continue
}
t[i] = reflect.New(types[i]).Elem() t[i] = reflect.New(types[i]).Elem()
t[i].Set(s(f)) t[i].Set(s(f))
} }
for i, d := range dvalue { for i, d := range dvalue {
if n.child[i].ident == "_" {
continue
}
if j := ivalue[i]; j != nil { if j := ivalue[i]; j != nil {
d(f).SetMapIndex(j(f), t[i]) // Assign a map entry d(f).SetMapIndex(j(f), t[i]) // Assign a map entry
} else { } else {
@@ -297,21 +352,24 @@ func assign(n *node) {
} }
func not(n *node) { func not(n *node) {
dest := genValue(n)
value := genValue(n.child[0]) value := genValue(n.child[0])
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
i := n.findex
if n.fnext != nil { if n.fnext != nil {
fnext := getExec(n.fnext) fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
if !value(f).Bool() { if !value(f).Bool() {
f.data[i].SetBool(true)
return tnext return tnext
} }
f.data[i].SetBool(false)
return fnext return fnext
} }
} else { } else {
i := n.findex
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).Bool()) dest(f).SetBool(!value(f).Bool())
return tnext return tnext
} }
} }
@@ -416,6 +474,10 @@ func _panic(n *node) {
func genFunctionWrapper(n *node) func(*frame) reflect.Value { func genFunctionWrapper(n *node) func(*frame) reflect.Value {
var def *node var def *node
var ok bool var ok bool
if n.kind == basicLit {
return func(f *frame) reflect.Value { return n.rval }
}
if def, ok = n.val.(*node); !ok { if def, ok = n.val.(*node); !ok {
return genValueAsFunctionWrapper(n) 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 { return reflect.MakeFunc(n.typ.TypeOf(), func(in []reflect.Value) []reflect.Value {
// Allocate and init local frame. All values to be settable and addressable. // 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 d := fr.data
for i, t := range def.types { for i, t := range def.types {
d[i] = reflect.New(t).Elem() d[i] = reflect.New(t).Elem()
@@ -467,7 +529,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
} }
// Interpreter code execution // Interpreter code execution
runCfg(start, &fr) runCfg(start, fr)
result := fr.data[:numRet] result := fr.data[:numRet]
for i, r := range result { 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] }) values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
} }
default: default:
if c.kind == basicLit { if c.kind == basicLit || c.rval.IsValid() {
var argType reflect.Type var argType reflect.Type
if variadic >= 0 && i >= variadic { if variadic >= 0 && i >= variadic {
argType = n.child[0].typ.arg[variadic].val.TypeOf() argType = n.child[0].typ.arg[variadic].val.TypeOf()
@@ -647,12 +709,35 @@ func call(n *node) {
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
def := value(f).Interface().(*node) 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 anc := f
// Get closure frame context (if any) // Get closure frame context (if any)
if def.frame != nil { if def.frame != nil {
anc = def.frame 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 var vararg reflect.Value
// Init return values // Init return values
@@ -717,10 +802,10 @@ func call(n *node) {
// Execute function body // Execute function body
if goroutine { if goroutine {
go runCfg(def.child[3].start, &nf) go runCfg(def.child[3].start, nf)
return tnext return tnext
} }
runCfg(def.child[3].start, &nf) runCfg(def.child[3].start, nf)
// Handle branching according to boolean result // Handle branching according to boolean result
if fnext != nil && !nf.data[0].Bool() { 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 // method signature obtained from reflect.Type include receiver as 1st arg, except for interface types
rcvrOffset := 0 rcvrOffset := 0
if recv := n.child[0].recv; recv != nil && recv.node.typ.TypeOf().Kind() != reflect.Interface { 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 { 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] }) values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
} }
default: default:
if c.kind == basicLit { if c.kind == basicLit || c.rval.IsValid() {
// Convert literal value (untyped) to function argument type (if not an interface{}) // Convert literal value (untyped) to function argument type (if not an interface{})
var argType reflect.Type var argType reflect.Type
if variadic >= 0 && i >= variadic { if variadic >= 0 && i >= variadic {
@@ -792,7 +879,6 @@ func callBin(n *node) {
case interfaceT: case interfaceT:
values = append(values, genValueInterfaceValue(c)) values = append(values, genValueInterfaceValue(c))
default: default:
//values = append(values, genValue(c))
values = append(values, genInterfaceWrapper(c, defType)) values = append(values, genInterfaceWrapper(c, defType))
} }
} }
@@ -1022,10 +1108,10 @@ func getFunc(n *node) {
next := getExec(n.tnext) next := getExec(n.tnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
fr := *f fr := f.clone()
nod := *n nod := *n
nod.val = &nod nod.val = &nod
nod.frame = &fr nod.frame = fr
dest(f).Set(reflect.ValueOf(&nod)) dest(f).Set(reflect.ValueOf(&nod))
return next return next
} }
@@ -1036,11 +1122,11 @@ func getMethod(n *node) {
next := getExec(n.tnext) next := getExec(n.tnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
fr := *f fr := f.clone()
nod := *(n.val.(*node)) nod := *(n.val.(*node))
nod.val = &nod nod.val = &nod
nod.recv = n.recv nod.recv = n.recv
nod.frame = &fr nod.frame = fr
f.data[i] = reflect.ValueOf(&nod) f.data[i] = reflect.ValueOf(&nod)
return next return next
} }
@@ -1055,11 +1141,11 @@ func getMethodByName(n *node) {
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
val := value0(f).Interface().(valueInterface) val := value0(f).Interface().(valueInterface)
m, li := val.node.typ.lookupMethod(name) m, li := val.node.typ.lookupMethod(name)
fr := *f fr := f.clone()
nod := *m nod := *m
nod.val = &nod nod.val = &nod
nod.recv = &receiver{nil, val.value, li} nod.recv = &receiver{nil, val.value, li}
nod.frame = &fr nod.frame = fr
f.data[i] = reflect.ValueOf(&nod) f.data[i] = reflect.ValueOf(&nod)
return next return next
} }
@@ -1176,7 +1262,7 @@ func getIndexSeqMethod(n *node) {
} }
} }
func negate(n *node) { func neg(n *node) {
dest := genValue(n) dest := genValue(n)
value := genValue(n.child[0]) value := genValue(n.child[0])
next := getExec(n.tnext) 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) { func land(n *node) {
value0 := genValue(n.child[0]) value0 := genValue(n.child[0])
value1 := genValue(n.child[1]) value1 := genValue(n.child[1])
@@ -1284,10 +1401,16 @@ func _return(n *node) {
} else { } else {
values[i] = genValue(c) values[i] = genValue(c)
} }
case funcT:
values[i] = genValue(c)
case interfaceT: case interfaceT:
values[i] = genValueInterface(c) values[i] = genValueInterface(c)
default: 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 var a reflect.Value
if n.typ.size > 0 { if n.typ.sizedef {
a, _ = n.typ.zero() a, _ = n.typ.zero()
} else { } else {
a = reflect.MakeSlice(n.typ.TypeOf(), max, max) 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 // compositeLit creates and populates a struct object
func compositeLit(n *node) { func compositeLit(n *node) {
value := valueGenerator(n, n.findex) value := valueGenerator(n, n.findex)
@@ -1468,6 +1600,7 @@ func compositeLit(n *node) {
if !n.typ.untyped { if !n.typ.untyped {
child = n.child[1:] child = n.child[1:]
} }
destInterface := destType(n).cat == interfaceT
values := make([]func(*frame) reflect.Value, len(child)) values := make([]func(*frame) reflect.Value, len(child))
for i, c := range child { for i, c := range child {
@@ -1484,9 +1617,12 @@ func compositeLit(n *node) {
for i, v := range values { for i, v := range values {
a.Field(i).Set(v(f)) 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()) d.Set(a.Addr())
} else { case destInterface:
d.Set(reflect.ValueOf(valueInterface{n, a}))
default:
d.Set(a) d.Set(a)
} }
return next return next
@@ -1534,6 +1670,7 @@ var rat = reflect.ValueOf((*[]rune)(nil)).Type().Elem() // runes array type
func _range(n *node) { func _range(n *node) {
index0 := n.child[0].findex // array index location in frame 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) fnext := getExec(n.fnext)
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
@@ -1544,10 +1681,10 @@ func _range(n *node) {
if isString(an.typ.TypeOf()) { if isString(an.typ.TypeOf()) {
value = genValueAs(an, rat) // range on string iterates over runes value = genValueAs(an, rat) // range on string iterates over runes
} else { } else {
value = genValueArray(an) value = genValueRangeArray(an)
} }
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
a := value(f) a := f.data[index2]
v0 := f.data[index0] v0 := f.data[index0]
v0.SetInt(v0.Int() + 1) v0.SetInt(v0.Int() + 1)
i := int(v0.Int()) i := int(v0.Int())
@@ -1562,13 +1699,12 @@ func _range(n *node) {
if isString(an.typ.TypeOf()) { if isString(an.typ.TypeOf()) {
value = genValueAs(an, rat) // range on string iterates over runes value = genValueAs(an, rat) // range on string iterates over runes
} else { } else {
value = genValueArray(an) value = genValueRangeArray(an)
} }
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
a := value(f)
v0 := f.data[index0] v0 := f.data[index0]
v0.SetInt(v0.Int() + 1) v0.SetInt(v0.Int() + 1)
if int(v0.Int()) >= a.Len() { if int(v0.Int()) >= f.data[index2].Len() {
return fnext return fnext
} }
return tnext return tnext
@@ -1578,7 +1714,8 @@ func _range(n *node) {
// Init sequence // Init sequence
next := n.exec next := n.exec
n.child[0].exec = func(f *frame) bltn { 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 return next
} }
} }
@@ -1590,7 +1727,10 @@ func rangeChan(n *node) {
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
n.exec = func(f *frame) bltn { 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 { if !ok {
return fnext return fnext
} }
@@ -1600,31 +1740,40 @@ func rangeChan(n *node) {
} }
func rangeMap(n *node) { func rangeMap(n *node) {
index0 := n.child[0].findex // array index location in frame index0 := n.child[0].findex // map index location in frame
index1 := n.child[1].findex // array value location in frame index2 := index0 - 1 // iterator for range, always just behind index0
value := genValue(n.child[2]) // array
fnext := getExec(n.fnext) fnext := getExec(n.fnext)
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
// TODO: move i and keys to frame
var i int
var keys []reflect.Value
n.exec = func(f *frame) bltn { var value func(*frame) reflect.Value
a := value(f) if len(n.child) == 4 {
i++ index1 := n.child[1].findex // map value location in frame
if i >= a.Len() { value = genValue(n.child[2]) // map
return fnext 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 // Init sequence
next := n.exec next := n.exec
n.child[0].exec = func(f *frame) bltn { n.child[0].exec = func(f *frame) bltn {
keys = value(f).MapKeys() f.data[index2].Set(reflect.ValueOf(value(f).MapRange()))
i = -1
return next return next
} }
} }
@@ -1757,7 +1906,6 @@ func appendSlice(n *node) {
return next return next
} }
} }
} }
func _append(n *node) { func _append(n *node) {
@@ -1845,39 +1993,48 @@ func _close(n *node) {
} }
func _complex(n *node) { func _complex(n *node) {
i := n.findex dest := genValue(n)
convertLiteralValue(n.child[1], floatType) c1, c2 := n.child[1], n.child[2]
convertLiteralValue(n.child[2], floatType) convertLiteralValue(c1, floatType)
value0 := genValue(n.child[1]) convertLiteralValue(c2, floatType)
value1 := genValue(n.child[2]) value0 := genValue(c1)
value1 := genValue(c2)
next := getExec(n.tnext) next := getExec(n.tnext)
n.exec = func(f *frame) bltn { if typ := n.typ.TypeOf(); isComplex(typ) {
f.data[i].SetComplex(complex(value0(f).Float(), value1(f).Float())) n.exec = func(f *frame) bltn {
return next 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) { func _imag(n *node) {
i := n.findex dest := genValue(n)
convertLiteralValue(n.child[1], complexType) convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1]) value := genValue(n.child[1])
next := getExec(n.tnext) next := getExec(n.tnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetFloat(imag(value(f).Complex())) dest(f).SetFloat(imag(value(f).Complex()))
return next return next
} }
} }
func _real(n *node) { func _real(n *node) {
i := n.findex dest := genValue(n)
convertLiteralValue(n.child[1], complexType) convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1]) value := genValue(n.child[1])
next := getExec(n.tnext) next := getExec(n.tnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetFloat(real(value(f).Complex())) dest(f).SetFloat(real(value(f).Complex()))
return next return next
} }
} }
@@ -1895,12 +2052,12 @@ func _delete(n *node) {
} }
func _len(n *node) { func _len(n *node) {
i := n.findex dest := genValue(n)
value := genValue(n.child[1]) value := genValue(n.child[1])
next := getExec(n.tnext) next := getExec(n.tnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetInt(int64(value(f).Len())) dest(f).SetInt(int64(value(f).Len()))
return next return next
} }
} }
@@ -2014,19 +2171,63 @@ func recv(n *node) {
value := genValue(n.child[0]) value := genValue(n.child[0])
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
if n.fnext != nil { if n.interp.cancelChan {
fnext := getExec(n.fnext) // Cancellable channel read
n.exec = func(f *frame) bltn { if n.fnext != nil {
if v, _ := value(f).Recv(); v.Bool() { 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 tnext
} }
return fnext
} }
} else { } else {
i := n.findex // Blocking channel read (less overhead)
n.exec = func(f *frame) bltn { if n.fnext != nil {
f.data[i], _ = value(f).Recv() fnext := getExec(n.fnext)
return tnext 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 vok := genValue(n.anc.child[1]) // status
tnext := getExec(n.tnext) tnext := getExec(n.tnext)
n.exec = func(f *frame) bltn { if n.interp.cancelChan {
v, ok := vchan(f).Recv() // Cancellable channel read
vres(f).Set(v) n.exec = func(f *frame) bltn {
vok(f).SetBool(ok) ch, result, status := vchan(f), vres(f), vok(f)
return tnext // 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) { 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 return
} }
if n.rval.IsValid() { if n.rval.IsValid() {
// Convert constant value to target type.
n.rval = n.rval.Convert(t) n.rval = n.rval.Convert(t)
} else { } 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()) convertLiteralValue(n.child[1], n.child[0].typ.val.TypeOf())
value1 := genValue(n.child[1]) // value to send value1 := genValue(n.child[1]) // value to send
n.exec = func(f *frame) bltn { if n.interp.cancelChan {
value0(f).Send(value1(f)) // Cancellable send
return next 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) chanValues := make([]func(*frame) reflect.Value, nbClause)
assignedValues := make([]func(*frame) reflect.Value, nbClause) assignedValues := make([]func(*frame) reflect.Value, nbClause)
okValues := 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++ { for i := 0; i < nbClause; i++ {
if len(n.child[i].child) > 1 { if len(n.child[i].child) > 1 {
@@ -2127,7 +2371,8 @@ func _select(n *node) {
} }
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
for i := range cases { cases[nbClause] = f.done
for i := range cases[:nbClause] {
switch cases[i].Dir { switch cases[i].Dir {
case reflect.SelectRecv: case reflect.SelectRecv:
cases[i].Chan = chanValues[i](f) cases[i].Chan = chanValues[i](f)
@@ -2139,6 +2384,9 @@ func _select(n *node) {
} }
} }
j, v, s := reflect.Select(cases) j, v, s := reflect.Select(cases)
if j == nbClause {
return nil
}
if cases[j].Dir == reflect.SelectRecv && assignedValues[j] != nil { if cases[j].Dir == reflect.SelectRecv && assignedValues[j] != nil {
assignedValues[j](f).Set(v) assignedValues[j](f).Set(v)
if ok[j] != nil { if ok[j] != nil {
@@ -2215,43 +2463,78 @@ func slice0(n *node) {
} }
func isNil(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) tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil { if n.fnext != nil {
fnext := getExec(n.fnext) fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
if value(f).IsNil() { if value(f).IsNil() {
dest(f).SetBool(true)
return tnext return tnext
} }
dest(f).SetBool(false)
return fnext return fnext
} }
} else { } else {
i := n.findex
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetBool(value(f).IsNil()) dest(f).SetBool(value(f).IsNil())
return tnext return tnext
} }
} }
} }
func isNotNil(n *node) { 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) tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil { if n.fnext != nil {
fnext := getExec(n.fnext) fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
if value(f).IsNil() { if value(f).IsNil() {
dest(f).SetBool(false)
return fnext return fnext
} }
dest(f).SetBool(true)
return tnext return tnext
} }
} else { } else {
i := n.findex
n.exec = func(f *frame) bltn { n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).IsNil()) dest(f).SetBool(!value(f).IsNil())
return tnext 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. // execution to the index in frame, created exactly from the types layout.
// //
type scope struct { type scope struct {
anc *scope // Ancestor upper scope anc *scope // Ancestor upper scope
def *node // function definition node this scope belongs to, or nil def *node // function definition node this scope belongs to, or nil
types []reflect.Type // Frame layout, may be shared by same level scopes loop *node // loop exit node for break statement
level int // Frame level: number of frame indirections to access var during execution loopRestart *node // loop restart node for continue statement
sym map[string]*symbol // Map of symbols defined in this current scope pkgID string // unique id of package in which scope is defined
global bool // true if scope refers to global space (single frame for universe and package level scopes) 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 // 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.global = s.global
sc.level = s.level 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 return &sc
} }
@@ -156,18 +161,15 @@ func (s *scope) add(typ *itype) (index int) {
return return
} }
func (interp *Interpreter) initScopePkg(n *node) (*scope, string) { func (interp *Interpreter) initScopePkg(pkgID string) *scope {
sc := interp.universe sc := interp.universe
pkgName := mainID
if p := fileNode(n); p != nil { interp.mutex.Lock()
pkgName = p.child[0].ident if _, ok := interp.scopes[pkgID]; !ok {
interp.scopes[pkgID] = sc.pushBloc()
} }
sc = interp.scopes[pkgID]
if _, ok := interp.scopes[pkgName]; !ok { sc.pkgID = pkgID
interp.scopes[pkgName] = sc.pushBloc() interp.mutex.Unlock()
} return sc
sc = interp.scopes[pkgName]
return sc, pkgName
} }

View File

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

View File

@@ -103,30 +103,32 @@ type structField struct {
// itype defines the internal representation of types in the interpreter // itype defines the internal representation of types in the interpreter
type itype struct { type itype struct {
cat tcat // Type category cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
arg []*itype // Argument types if funcT or nil arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil ret []*itype // Return types if funcT or nil
method []*node // Associated methods or nil method []*node // Associated methods or nil
name string // name of type within its package for a defined type name string // name of type within its package for a defined type
path string // for a defined type, the package import path path string // for a defined type, the package import path
size int // Size of array if ArrayT size int // Size of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil rtype reflect.Type // Reflection type if ValueT, or nil
incomplete bool // true if type must be parsed again (out of order declarations) incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number) untyped bool // true for a literal value (string or number)
sizedef bool // true if array size is computed from type definition sizedef bool // true if array size is computed from type definition
node *node // root AST node of type definition isBinMethod bool // true if the type refers to a bin method function
scope *scope // type declaration scope (in case of re-parse incomplete type) 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 // nodeType returns a type definition for the corresponding AST subtree
func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
var err cfgError
if n.typ != nil && !n.typ.incomplete { 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} 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 { switch n.kind {
case addressExpr, starExpr: case addressExpr, starExpr:
t.cat = ptrT 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()) t.size = int(n.child[0].rval.Int())
case n.child[0].kind == ellipsisExpr: case n.child[0].kind == ellipsisExpr:
// [...]T expression // [...]T expression
t.sizedef = true t.size = arrayTypeLen(n.anc)
default: default:
if sym, _, ok := sc.lookup(n.child[0].ident); ok { if sym, _, ok := sc.lookup(n.child[0].ident); ok {
// Resolve symbol to get size value // Resolve symbol to get size value
@@ -173,7 +176,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
} }
} else { } else {
// Evaluate constant array size expression // 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 return nil, err
} }
t.incomplete = true 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 { if t.val, err = nodeType(interp, sc, n.child[1]); err != nil {
return nil, err return nil, err
} }
t.sizedef = true
t.incomplete = t.incomplete || t.val.incomplete t.incomplete = t.incomplete || t.val.incomplete
} else { } else {
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil { 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: default:
err = n.cfgErrorf("invalid types %s and %s", t0.Kind(), t1.Kind()) err = n.cfgErrorf("invalid types %s and %s", t0.Kind(), t1.Kind())
} }
if nt0.untyped && nt1.untyped {
t.untyped = true
}
} }
case "real", "imag": case "real", "imag":
if t, err = nodeType(interp, sc, n.child[1]); err != nil { 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: case k == reflect.Complex128:
t = sc.getType("float64") t = sc.getType("float64")
case t.untyped && isNumber(t.TypeOf()): case t.untyped && isNumber(t.TypeOf()):
t = &itype{cat: valueT, rtype: floatType} t = &itype{cat: valueT, rtype: floatType, untyped: true}
default: default:
err = n.cfgErrorf("invalid complex type %s", k) 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 t.method = m
sym.typ = t sym.typ = t
} }
if t.node == nil {
t.node = n
}
} else { } else {
t.incomplete = true t.incomplete = true
sc.sym[n.ident] = &symbol{kind: typeSym, typ: t} 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 { if m, _ := lt.lookupMethod(name); m != nil {
t, err = nodeType(interp, sc, m.child[2]) t, err = nodeType(interp, sc, m.child[2])
} else if bm, _, _, ok := lt.lookupBinMethod(name); ok { } 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 { } else if ti := lt.lookupField(name); len(ti) > 0 {
t = lt.fieldSeq(ti) t = lt.fieldSeq(ti)
} else if bs, _, ok := lt.lookupBinField(name); ok { } 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: case structType:
t.cat = structT t.cat = structT
var incomplete bool var incomplete bool
@@ -618,6 +640,74 @@ func (t *itype) finalize() (*itype, error) {
return t, err 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 // id returns a unique type identificator string
func (t *itype) id() string { func (t *itype) id() string {
// TODO: if res is nil, build identity from 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 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 // getMethod returns a pointer to the method definition
func (t *itype) getMethod(name string) *node { func (t *itype) getMethod(name string) *node {
for _, m := range t.method { for _, m := range t.method {
@@ -796,14 +902,16 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
panic(err) 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 t.val.rtype = interf
} }
switch t.cat { switch t.cat {
case aliasT: case aliasT:
t.rtype = t.val.refType(defined) t.rtype = t.val.refType(defined)
case arrayT, variadicT: case arrayT, variadicT:
if t.size > 0 { if t.sizedef {
t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined)) t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined))
} else { } else {
t.rtype = reflect.SliceOf(t.val.refType(defined)) 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)) in := make([]reflect.Type, len(t.arg))
out := make([]reflect.Type, len(t.ret)) out := make([]reflect.Type, len(t.ret))
for i, v := range t.arg { for i, v := range t.arg {
if defined[v.name] {
v.rtype = interf
}
in[i] = v.refType(defined) in[i] = v.refType(defined)
} }
for i, v := range t.ret { for i, v := range t.ret {
if defined[v.name] {
v.rtype = interf
}
out[i] = v.refType(defined) out[i] = v.refType(defined)
} }
t.rtype = reflect.FuncOf(in, out, false) t.rtype = reflect.FuncOf(in, out, false)
case interfaceT: case interfaceT:
t.rtype = interf t.rtype = interf
case mapT: 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: case ptrT:
t.rtype = reflect.PtrTo(t.val.refType(defined)) t.rtype = reflect.PtrTo(t.val.refType(defined))
case structT: case structT:
@@ -866,7 +968,7 @@ func (t *itype) frameType() (r reflect.Type) {
case aliasT: case aliasT:
r = t.val.frameType() r = t.val.frameType()
case arrayT, variadicT: case arrayT, variadicT:
if t.size > 0 { if t.sizedef {
r = reflect.ArrayOf(t.size, t.val.frameType()) r = reflect.ArrayOf(t.size, t.val.frameType())
} else { } else {
r = reflect.SliceOf(t.val.frameType()) r = reflect.SliceOf(t.val.frameType())
@@ -915,7 +1017,13 @@ func isInterface(t *itype) bool {
return isInterfaceSrc(t) || t.TypeOf().Kind() == reflect.Interface 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 { func isInt(t reflect.Type) bool {
switch t.Kind() { switch t.Kind() {

View File

@@ -7,21 +7,31 @@ import (
func valueGenerator(n *node, i int) func(*frame) reflect.Value { func valueGenerator(n *node, i int) func(*frame) reflect.Value {
switch n.level { switch n.level {
case 0: 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: 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: 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: default:
return func(f *frame) reflect.Value { return func(f *frame) reflect.Value {
for level := n.level; level > 0; level-- { for level := n.level; level > 0; level-- {
f = f.anc 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 { func genValueRecvIndirect(n *node) func(*frame) reflect.Value {
v := genValueRecv(n) v := genValueRecv(n)
return func(f *frame) reflect.Value { return v(f).Elem() } 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 { 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 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 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 { func genValueInterfacePtr(n *node) func(*frame) reflect.Value {
value := genValue(n) value := genValue(n)
it := reflect.TypeOf((*interface{})(nil)).Elem() it := reflect.TypeOf((*interface{})(nil)).Elem()
@@ -133,15 +165,46 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
value := genValue(n) value := genValue(n)
return func(f *frame) reflect.Value { 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 { func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
value := genValue(n) value := genValue(n)
return func(f *frame) reflect.Value { 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()) i = int64(v.Uint())
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
i = int64(v.Float()) i = int64(v.Float())
case reflect.Complex64, reflect.Complex128:
i = int64(real(v.Complex()))
} }
return return
} }
@@ -165,6 +230,8 @@ func vUint(v reflect.Value) (i uint64) {
i = v.Uint() i = v.Uint()
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
i = uint64(v.Float()) i = uint64(v.Float())
case reflect.Complex64, reflect.Complex128:
i = uint64(real(v.Complex()))
} }
return return
} }
@@ -191,6 +258,8 @@ func vFloat(v reflect.Value) (i float64) {
i = float64(v.Uint()) i = float64(v.Uint())
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
i = v.Float() i = v.Float()
case reflect.Complex64, reflect.Complex128:
i = real(v.Complex())
} }
return 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()) } return func(f *frame) (reflect.Value, int64) { v := value(f); return v, int64(v.Uint()) }
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
return func(f *frame) (reflect.Value, int64) { v := value(f); return v, int64(v.Float()) } 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 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() } return func(f *frame) (reflect.Value, uint64) { v := value(f); return v, v.Uint() }
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
return func(f *frame) (reflect.Value, uint64) { v := value(f); return v, uint64(v.Float()) } 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 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()) } return func(f *frame) (reflect.Value, float64) { v := value(f); return v, float64(v.Uint()) }
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
return func(f *frame) (reflect.Value, float64) { v := value(f); return v, v.Float() } 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 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) value := genValue(n)
switch n.typ.TypeOf().Kind() { switch n.typ.TypeOf().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 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: 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: 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: 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 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